for = flip map

134 views
Skip to first unread message

Evan Czaplicki

unread,
Mar 24, 2014, 1:55:53 PM3/24/14
to elm-d...@googlegroups.com
I was talking with Shriram about Pyret at mloc.js and he mentioned that their syntax lets you write things in a functional way, using a syntax that looks like a typical for loop. I don't want to go so far as to introduce syntactic constructs, but you can get pretty far with just variables:

for : [a] -> (a -> b) -> [b]
for xs f = map f xs

So for is the same as flip map (which always reminds me of Flipmode Squad). It is nice for "imperative" feeling things and I find forM is handy in Haskell sometimes. I wonder if that would be helpful for people in general? Or if enough people know about map for this to be redundant?

Which version of grid is clearer, this or this?

Joey Eremondi

unread,
Mar 24, 2014, 2:03:44 PM3/24/14
to elm-d...@googlegroups.com
My worry with for is that it has a very imperative feel, and that newcomers to Elm would mistakenly interpret it as "do something for each element in the list" instead of "compute a value for each element in the list".
With forM, the former interpretation is correct, because with monads, you are actually executing some computation n times, and there may be no list left over (or trivially, a list of () values).

That said, the advantage is that for is familiar. So I think it boils down to, having for is better for attracting people to Elm, but not having it is better for getting people to quickly use Elm properly. Which is more important, I'm not sure.

Maybe there should be a blog post explaining how for is just flip map, so people searching it will find it, but it won't be in the Basics library for people to mistakenly misuse? If they do misuse it, the type-checker should catch it most of the time.

Jeff Smits

unread,
Mar 24, 2014, 3:07:32 PM3/24/14
to elm-discuss
I think I agree with Joey that `for` is confusing because it doesn't correspond to the normal for-loop of an imperative language. I wouldn't add it.
As for the examples, the one with `for` immediately clear to me. The one with `map` I would write like so: http://share-elm.com/sprout/5330820ae4b0f7cc0dd4ef9e

--
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.

Evan Czaplicki

unread,
Mar 24, 2014, 3:37:37 PM3/24/14
to elm-d...@googlegroups.com
Okay, I agree that adding it to the List library would be a bad choice :) All the objections you raise are really good, and I think adding more functions is not solving the problem of having some unfamiliar ones.

Jeff, I would write it that way too :) Though novice-me would probably try it in one line first, see that it is tough to understand, then break it apart. I wonder if there is a way to make an "imperative feeling" library though. You can rewrite map and fold to look like this:

-- general purpose looping function
for : (f -> [a] -> b) -> [a] -> f -> b

-- do something to each element
each : (a -> b) -> [a] -> [b]

-- accumulate some result
accumulate : b -> (a -> b -> b) -> [a] -> b

for each [ 1.. 9 ] <| \i ->
    i^2

for (accumulate 0) [ 1 .. 9 ] \i total ->
    total + i

I don't really know if there is some pedagogical value to doing something like this. I can definitely see how it could be extra confusing. But this is more about the fact that this is possible :)

Max Goldstein

unread,
Mar 24, 2014, 4:16:04 PM3/24/14
to elm-d...@googlegroups.com
Maybe it's because I was taught FP on a stripped-down version of Scheme but I'm much more comfortable with map, filter, and fold than for, each, and accumulate. The version with for is superficially easier to read, because we've all seen enough nested loops in imperative languages with indentation that the pattern sticks out, but it's masking the fact that something different is going on. But technically the Cartesian product wouldn't have all the intermediate lists; it should be flattened (in the lisp sense, not the monad sense from the other thread). If you're going to map some sort of render function ((Int,Int) -> Form) over this list, having it nested isn't what you want, but that's not the only thing you can do with the list.

Dandandan

unread,
Mar 24, 2014, 6:25:41 PM3/24/14
to elm-d...@googlegroups.com
Another option: list comprehensions, pretty common in Haskell / Python and some other languages:

Haskell:

grid : Int -> Int -> [[(Int,Int)]]
grid width height = 
    [[(i, j) | i <- [0..width - 1]] | j <- [0..height - 1]]

Python:
 
def grid (width, height):
    return [[(i, j) for i in range(0, width)] for j in range (0, height)]



Op maandag 24 maart 2014 21:16:04 UTC+1 schreef Max Goldstein:

John Nilsson

unread,
Mar 24, 2014, 6:57:25 PM3/24/14
to elm-d...@googlegroups.com
Scala separates for-loops and for-comprehensions by the yield keyword

def grid(width: Int, height: Int) =
  for {
    i <- 0 until width
    j <- 0 until height
  } yield (i,j)

C# goes for SQL'ish "from-select"

IEnumerable<Tuple<int,int>> grid(int width, int height) {
  return
    from i in Enumerable.Range(0,width-1)
    from j in Enumerable.Range(0,height-1)
    select Tuple.Create(i,j);
}

Both variation on haskells do-notation I think.

BR,
John


--

Eitan Chatav

unread,
Mar 24, 2014, 11:00:44 PM3/24/14
to elm-d...@googlegroups.com
I like for = flip map. In general, I like having both directions available, just like <| and |>. Left-to-right feels imperative and right-to-left feels declarative. The objection that newcomers will confuse it with impure for in imperative languages applies just as well to if statements. Eventually you can add sugar like in do notation where <- indicates a lambda:

for i <- [1..100] (f i) ~~desugar~> for [1..100] (\i -> f i)

O/T: What about list comprehensions?

Evan Czaplicki

unread,
Mar 25, 2014, 3:48:18 AM3/25/14
to elm-d...@googlegroups.com
The Haskell style guide recommends against list comprehensions. I feel that it normally makes my code a bit harder to read and a lot harder to refactor. So I think it's a really cool/fun thing, but I think writing it the "standard" way is almost always a better call (except in the one particular case of making matrices where it is just really great).

I find the same in Python, I always regret writing a list comprehension.

I'm not so into adding syntax for this, but it'd be cool to come up with a syntax for "purely functional" for loops :)


--

Jeff Smits

unread,
Mar 25, 2014, 9:37:34 AM3/25/14
to elm-discuss
I used to be a fan of list comprehensions, before I learnt about higher-order functions. Now I don't like it that much any more, most of the time I find the higher-order functions way easier to read.
I also believe in limiting the ways one can express simple problems in a programming language so that the best way is the easiest way. So I support not adding list comprehensions.

Dandandan

unread,
Mar 25, 2014, 11:15:33 AM3/25/14
to elm-d...@googlegroups.com
Yet another option:
A flat list using Applicatives is in Haskell as easy as

grid width height =
    (,) <$> [0..height-1] <*> [0..width-1]

(This style is also advocated by Learn You a Haskell as a replacement for list comprehensions). Could be (,) <~ [0..height-1] ~ [0..width-1] in Elm with type classes.

Op dinsdag 25 maart 2014 14:37:34 UTC+1 schreef Jeff Smits:

Sean Corfield

unread,
Mar 27, 2014, 7:26:45 PM3/27/14
to elm-d...@googlegroups.com
FWIW, as a comparison with Clojure:

(for [i (range width)
j (range height)]
[i j])

Sean
signature.asc
Reply all
Reply to author
Forward
0 new messages