random musings about function names in standard lib

122 views
Skip to first unread message

Janis Voigtländer

unread,
Dec 17, 2014, 1:41:28 AM12/17/14
to elm-d...@googlegroups.com
... from preparing lectures on Elm.

1. Why is it List.filter instead of List.keepIf? With 0.14, Signal.lift was renamed into Signal.map, for the sake of correspondence to List.map. For consistency, it would also make sense to either rename Signal.keepIf to Signal.filter, or List.filter to List.keepIf. The latter of these two is the better suggestion, except for historical baggage about filtering functions in other FP languages.

2. Somewhere in the Elm library guidelines, there is advice against encoding into function names information that could be appropriately expressed through qualified use of the module name. Arguably, this means that foldp should be just Signal.fold. The "p" in the name is not needed to disambiguate that folding function from any other folding function (in the Signal module, or elsewhere - which would be the job of the "Signal." qualification anyway).

3. When using filtering/keepIf, an oft occurring case (seen in a lot of code) is to process a signal of Bools with either keepIf identity or keepIf not. It would be good if Basics contained two more mnemonic functions for that:

isTrue : Bool -> Bool
isTrue = identity

isFalse : Bool -> Bool
isFalse = not


Janis Voigtländer

unread,
Dec 17, 2014, 2:13:19 AM12/17/14
to elm-d...@googlegroups.com
BTW, I'm also not happy with isJust and isNothing having disappeared from Maybe in 0.14. Certainly, we are going to see people reimplementing these functions again and again now in their own code (often for filtering/keepIf/dropIf).

Janis Voigtländer

unread,
Dec 17, 2014, 2:21:06 AM12/17/14
to elm-d...@googlegroups.com
Checking the release notes for why isJust and isNothing were removed ... they say they were removed in favor of pattern matching.

1. Typical uses of these two functions (with keepIf, say) are not really served well by pattern matching.

2. For consistency, if isJust and isNothing are removed in favor of pattern matching, then List.isEmpty "should" be removed as well, in favor of pattern matching.

Evan Czaplicki

unread,
Dec 17, 2014, 3:07:47 AM12/17/14
to elm-d...@googlegroups.com
1. Why is it List.filter instead of List.keepIf? With 0.14, Signal.lift was renamed into Signal.map, for the sake of correspondence to List.map. For consistency, it would also make sense to either rename Signal.keepIf to Signal.filter, or List.filter to List.keepIf. The latter of these two is the better suggestion, except for historical baggage about filtering functions in other FP languages.

I still forget which way filter is going to work sometimes, so I am very open to List.keepIf and List.dropIf instead. It seems like the meaning is clearer and nothing essential is lost.
 
2. Somewhere in the Elm library guidelines, there is advice against encoding into function names information that could be appropriately expressed through qualified use of the module name. Arguably, this means that foldp should be just Signal.fold. The "p" in the name is not needed to disambiguate that folding function from any other folding function (in the Signal module, or elsewhere - which would be the job of the "Signal." qualification anyway).

I'm not sure on this one. I think it's important to keep the fold idea across libraries, so reduce or accumulate (hard to spell / weird / too long) do not seem to be good contenders to me. It took me a while before I really remembered "foldr = fold from the right" and could visualize that in my head. Perhaps it is valuable to have "fold from the past" to have an example that could not possibly be "fold towards the past"?

Also, I think foldp is kind of delightful. I think that's most of why I'd hesitate here.
 
3. When using filtering/keepIf, an oft occurring case (seen in a lot of code) is to process a signal of Bools with either keepIf identity or keepIf not. It would be good if Basics contained two more mnemonic functions for that:

isTrue : Bool -> Bool
isTrue = identity

isFalse : Bool -> Bool
isFalse = not
 
I'm not so sure about these. I might do ((==) True) or something and not feel weird about it.

Evan Czaplicki

unread,
Dec 17, 2014, 3:15:41 AM12/17/14
to elm-d...@googlegroups.com
Checking the release notes for why isJust and isNothing were removed ... they say they were removed in favor of pattern matching.

It's more that whenever I see that in my code, I'm usually writing something silly. Maybe that's not the experience of others though. I'd want to see some examples. 
 
1. Typical uses of these two functions (with keepIf, say) are not really served well by pattern matching.

In Haskell, there's no List.filterMap that's super easy to get to, so I think this argument works better there. I can never remember if it's Maybe.mapMaybes or Maybe.mapMaybe or Maybe.catMaybes or Maybe.catMaybe and I usually do not guess correctly.

So in Elm, I'm not sure I buy this argument.

Side note, what would be the name of filterMap in keepIf world?

2. For consistency, if isJust and isNothing are removed in favor of pattern matching, then List.isEmpty "should" be removed as well, in favor of pattern matching.

I think I find myself using isEmpty in a way that is non-awful sometimes. Like, in a multiway if? I don't know really, but I don't feel that this is as superfluous. Maybe List.concatMap could play the same role as List.filterMap though?

Janis Voigtländer

unread,
Dec 17, 2014, 3:33:18 AM12/17/14
to elm-d...@googlegroups.com
Will have more answers later (am about to head to the lecture), but: With keepIf here, I meant Signal.keepIf. I frequently see/have Signal.keepIf isNothing or Signal.keepIf isJust. Replacing isNothing/isJust by pattern matching in such situations is a pain. And List.filterMap doesn't help at all with that.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jeff Smits

unread,
Dec 17, 2014, 6:29:00 AM12/17/14
to elm-discuss
  1. Rename filter to keepIf, possibly add dropIf: YES! I've come to really dislike filter because it can filter two ways and the name and type don't give any hint. In Stratego I usually use retain-all and remove-all instead of filter for the same reason, filter is just confusing.
  2. Rename foldp to fold because it should be used qualified anyway. I'm not completely sure because it's a bit of an iconic name in Elm land. But lift was also iconic and has been renamed. If we had generalised state in Elm then foldp wouldn't be the primitive for introducing state any more and I would have less of a problem renaming it to fold. Right now I think it's helpful to have it remain foldp.
  3. I always use ((==) True) or ((==) False) in filtering functions. It's a little annoying to type but reads a lot better. I prefer (/=) over xor for booleans too. I would be ok with isTrue/isFalse, people new to programming will write redundant code like if bool == True then ... anyway.
  4. I can see that isJust and isNothing might be used in places where pattern matching is more appropriate. I guess filterMap can be used for filtering Just/Nothing. I'd prefer them being added over List.isEmpty being deleted though. 
  5. "Maybe List.concatMap could play the same role as List.filterMap though?" It can but filterMap is meant to be used for filtering so it's better style to use that I think. 
  6. This is a complaint of my own to add to the list: Why is filterMap defined for Lists but not for Signals? I think it would be super useful!

Janis Voigtländer

unread,
Dec 17, 2014, 7:16:10 AM12/17/14
to elm-d...@googlegroups.com
Thanks, Evan and Jeff, for your comments. In light of those, here my updated thoughts on my original points:

I do suggest to rename List.filter to List.keepIf in 0.15, and to add List.dropIf. I have no immediate idea how List.filterMap would be named then, but surely we can come up with a useful name.

Signal.foldp vs. Signal.fold is but a minor issue.

Concerning isTrue and isFalse, I hadn't thought of writing ((==) True) and ((==) False). Probably because Elm has no operator sectioning, so I would have wanted to write those as (== True) and (== False), but can't. But yes, ((==) True) and ((==) False) are okay alternatives for isTrue/isFalse.

Concerning isNothing/isJust, here is a typical example:

dblclicks : Time -> Signal ()
dblclicks d =
  let step t' s = case s of
                    Nothing -> Just t'
                    Just t  -> if t'-t < d then Nothing else Just t'
  in map (always ())
         (keepIf isNothing Nothing
                 (foldp step Nothing
                        (map fst (timestamp Mouse.clicks))))

Evan, is there something you dislike about the use of isNothing in there? How would you write that with pattern matching instead?

More generally, I'm wondering about your comment that code using isNothing and isJust smells bad to you. I can easily imagine that if you give beginners the functions isNothing, isJust, and fromJust, they will do things that would be better done with pattern matching. But I'm deliberately not proposing to introduce fromJust. With just isNothing and isJust, I don't see any particular danger of people misusing them. Do you have a specific example of code that you have seen or written that used isNothing and/or isJust and where you think that use was a bad idea.

(Aside: Having a Signal analogue of the current List.filterMap would address uses of Signal.keepIf isJust, but not of Signal.keepIf isNothing like in the above example.)


Janis Voigtländer

unread,
Dec 17, 2014, 7:33:33 AM12/17/14
to elm-d...@googlegroups.com
Now, after thinking a bit about how List.filterMap should be named (and analogously for Signal.filterMap):

What about

mapKeepIf : (a -> Maybe b) -> [a] -> [b]

or

mapKeepIfJust : (a -> Maybe b) -> [a] -> [b]


?

Better than both filterMap and (Haskell's) mapMaybe, I think.


2014-12-17 9:15 GMT+01:00 Evan Czaplicki <eva...@gmail.com>:

--

Janis Voigtländer

unread,
Dec 17, 2014, 7:35:26 AM12/17/14
to elm-d...@googlegroups.com
Or mapKeepJusts. :-)

Jeff Smits

unread,
Dec 17, 2014, 7:50:39 AM12/17/14
to elm-discuss

Totally forgot to comment on renaming filterMap. I think it doesn't need to be renamed. It's a filter and a map in one. The Maybe type is enough of a hint as to how the filtering works.

Janis Voigtländer

unread,
Dec 17, 2014, 8:05:59 AM12/17/14
to elm-d...@googlegroups.com
Fair point (about "filter" being a good enough name in that case, no keepIf/dropIf distinction being required), because the function's type does not open up any ambiguity concerning what is kept and what is not.

Max Goldstein

unread,
Dec 17, 2014, 9:02:56 AM12/17/14
to elm-d...@googlegroups.com
There's a function I've asked for for a while, name open to discussion:

justs : Signal (Maybe a) -> a -> Signal a

It's a little tricky to implement because Nothings should drop out entirely, rather than cause repeats of the old value or a repetition of the default. Sometimes I find myself filtering on isJust and then writing (\(Just x) -> x) which are both bad practice. I wonder how much of your problems would subside with this function?

Janis Voigtländer

unread,
Dec 17, 2014, 10:22:02 AM12/17/14
to elm-d...@googlegroups.com
That is simply a variation/invocation of what Jeff calls Signal.filterMap, isn't it? As such, the answer to your question is the same as in my previous mail, concerning Signal.filterMap: That would address cases of Signal.keepIf isJust, but not of Signal.keepIf isNothing.

Also, what you describe, with (\(Just x) -> x), is exactly the "having fromJust leads to bad practice", which I immediately agree to. But I don't see the problem (bad practice arising from) isNothing and isJust.



--

Alexey Zlobin

unread,
Dec 20, 2014, 5:27:23 AM12/20/14
to elm-d...@googlegroups.com
'filter' is by far the most common operation and it's name across languages and libraries. It widely applied to collections, streams, persisted data, it also makes sense in real world signal processing.

I would also say that keepIf/dropIf presumes mutability and in-place change rather than new object.

Janis Voigtländer

unread,
Dec 20, 2014, 5:46:04 AM12/20/14
to elm-d...@googlegroups.com
And yet, Elm uses the names Signal.keepIf and Signal.dropIf. Isn't consistency between Elm's List and Signal libraries of higher priority than consistency between Elm's List library and other languages' collection libraries? Even if it's not (of higher priority), consistency between Elm's List and Signal libraries is certainly worth something.

So, arguing for keeping the name List.filter should come with a proposal to rename Signal.keepIf to Signal.filter, and Signal.dropIf to what?

(Or, an argument that List.filter and Signal.keepIf are not indeed similar enough to aspire for consistent naming?)

Alexey Zlobin

unread,
Dec 20, 2014, 6:43:40 AM12/20/14
to elm-d...@googlegroups.com
List/Signal.dropIf look fine. And Haskell's List lives without dropIf (not sure, just quickly checked the docs).

I'm not arguing for anything, just sharing some design concerns.

Max Goldstein

unread,
Dec 20, 2014, 10:24:40 AM12/20/14
to elm-d...@googlegroups.com
Ruby uses select and reject for its filtering, something to consider if you're worried about implying mutability.

But I agree that (1) I have to think about which way filter goes (2) consistency between Signal and List is important. I'm fine with keep/dropIf.

Alexey Zlobin

unread,
Dec 22, 2014, 11:20:19 AM12/22/14
to elm-d...@googlegroups.com
Gave it a second thought... Signal.keepIf actually has different semantics than List.filter. A Signal isn't allowed to be empty, and these methods carry an additional argument for defaults.

May be 'consistency' is a too strong word here? It feels more like 'similarity'.

On Sat, Dec 20, 2014 at 7:24 PM, Max Goldstein <maxgol...@gmail.com> wrote:
Ruby uses select and reject for its filtering, something to consider if you're worried about implying mutability.

But I agree that (1) I have to think about which way filter goes (2) consistency between Signal and List is important. I'm fine with keep/dropIf.

--

Jeff Smits

unread,
Dec 22, 2014, 11:26:17 AM12/22/14
to elm-discuss
Nice observation. Signals are closer to NonEmptyLists from that perspective :)
Reply all
Reply to author
Forward
0 new messages