Proposal: Commands, managing effects

272 views
Skip to first unread message

Evan Czaplicki

unread,
Jun 4, 2014, 1:33:52 AM6/4/14
to elm-d...@googlegroups.com
Here is the proposal.

I tried teaching it to a JS person with no experience with ML-family languages, and it went super well! He immediately realized that it solved problems he had faced in writing atomic updates and dealing with promises that start running too soon. That it has obvious application to real life code is a really good sign!

This proposal is inspired by the recent post on "The ___ Monad" and related conversations. It also draws heavily upon F#'s computation expressions which I think has a very clever way of making code look nice for imperative people. F# also shares a desire to demystify things, but I tried to simplify the language and syntax even more.

Evan Czaplicki

unread,
Jun 4, 2014, 1:43:04 AM6/4/14
to elm-d...@googlegroups.com

Evan Czaplicki

unread,
Jun 4, 2014, 2:20:17 AM6/4/14
to elm-d...@googlegroups.com
And here are some additional comments that clarify my position on:
  1. using infix operators with commands
  2. how we should talk about monads / the relationship between commands and monads

Dobes Vandermeer

unread,
Jun 4, 2014, 2:28:49 AM6/4/14
to elm-d...@googlegroups.com

You can use the letters from command to make the word monad.  That's one way in which they are related :-P.

One thought I had reading through this one is that you're saying "command" but the example is just fetching information, it's not really "doing" much, it doesn't feel like a command.

I know HTTP requests are an effect, but it might be more concrete to make it send something back to the server or something, just to show it's a "command".  Or set a cookie or resize the window or update some state or something.

I imagined that if Element became a subclass of Command and main returned a Command instead of an Element then the whole thing suddenly becomes a bit more general purpose.  I'm not sure if that's a good idea, but it occurred to me as an interesting avenue of exploration.

Cheers,

Dobes




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

Shashi Gowda

unread,
Jun 4, 2014, 3:04:33 AM6/4/14
to elm-d...@googlegroups.com
This is smooth! I think the explanation is excellent for beginners.

So with this, we will be able to lift functions with commands in them to write or poll signals into / from a file / database.

Does this mean we can also have a function that looks like:

           a -> WebSocket Signal b

What happens when we lift the above function with a Signal a?

Evan Czaplicki

unread,
Jun 4, 2014, 3:23:45 AM6/4/14
to elm-d...@googlegroups.com
Dobes, haha, /r/haskell is always talking about comonads these days and I always misread it now :P I think of command as "a computation that can run later" which I agree is not the colloquial meaning, but I like going with the HTTP example because it's super relatable and the same code looks worse in JS even with all the new stuff on promises and generators. I think if this document turns into a "Learn Commands!" post it would make sense to have more examples as you suggest though. I haven't thought through a lot of other cases, but this paper has a bunch of wonderful examples and ideas that we can learn from.

Shashi, thank you! :D I don't understand you exactly, but this does not change the fact that signals are not monadic in Elm.

In terms of how these would be used, I was thinking of three ways. One is for commands that do not have effects:

State.run : state -> State state a -> (state,a)

For things that do have effects, we'd start with things like this:

Http.run : Signal (Http a) -> Signal a

So it is just introducing a node that permits asynchrony and possibly effects. The last would be sending them out to "port handlers" which is a concept explored here that needs more work. Creating a function like Http.run is equivalent to creating a port handler, which is why I feel it is an okay thing to do. But port handlers would be more general because they let you create signal loops if you want.

Alexander Berntsen

unread,
Jun 4, 2014, 4:36:25 AM6/4/14
to elm-d...@googlegroups.com
I don't like it. I think the colloquial term "command" is used to mean
too many things.

Then again I also dislike classes and interfaces, and prefer the more
rigid concepts of functors, monads, etc., so I might be the exact
opposite of the person you want to talk to...

I don't mind "computation that can run later", and I agree that you
don't need to teach monads to teach this "computation that can run
later" concept. I agree full on with your reasoning, and think of this
"computation that can run later" concept as having more in common with
IO than monads in general. Furthermore, I think giving it a name is
reasonable. But I don't think it should be "command".

So: I agree with everything but the name. :-]
--
Alexander
alex...@plaimi.net
https://secure.plaimi.net/~alexander

Shashi Gowda

unread,
Jun 4, 2014, 5:14:31 AM6/4/14
to elm-d...@googlegroups.com

Just like


getBestMovie : Http String

getBestMovie = send (get “http://www.example.com/movie?best”)

I was wondering if it is possible to have

getNewMovies : WebSocket Signal String
getNewMovies = readWebSocket "ws://example.com/movie?new"

where getNewMovies would be a signal that updates whenever a new movie name is received on the websocket.

By signals remain non monadic I understand you mean I can not have

Signal a -> (a -> Signal b) -> Signal b

(which was kind of the final part of the question) that's fair, I think it will make my code harder to understand.

Port handlers is a nice concept! Some heresy: it would be nice to generalize Graphics.Input and ports into one construct.
I have found the handle, signal pair:
      { handle: Handle a, signal: Signal a }
to be a more generally useful pattern while doing MongoDB integration stuff. e.g. I could send my DB requests to a Query -> Handle a -> () function and have the results arrive at the signal field.

Evan Czaplicki

unread,
Jun 4, 2014, 5:39:53 AM6/4/14
to elm-d...@googlegroups.com
Shashi, lets put the WebSockets stuff in a separate thread. (WebSocket Signal a) does not mean anything to me, so I need more explanation to know if this is relevant.

Alexander, I don't want to debate the name, but I at least wanted to explain my position and the reasoning behind it:

Every choice is going to have tradeoffs for different groups of people, and I think command is helpful for pretty much all people who are doing JS every day. This is the group of people that matter most for Elm's success. FP people are already on board with all this stuff on some level.

The name is inspired by the Command Pattern which I think no one really knows about, but major benefit is that we can say "Java has this too, but we did it better." Immediately, the concept feels familiar. Everyone has heard of Java and knows that it is widely used in industry, and everyone will have some conception of what a command might be. So this is about building trust and intuition. People feel comfortable and therefore confident that they can learn the concept. That confidence is really powerful.

I imagine a newb coming and asking "How do I read a file?" We can say "with a command" which seems like a tautology. Like, yeah, how else could it be? You run some command. The point is to make it clear that this is not something new or crazy or hard to learn.

Now imagine how a typical Haskeller might answer the question "How do I read a file?" Monads and category theory will probably come up in the first or second sentence. That kind of stuff alienates a lot of people, and I have found that it is a way slower way to learn about monads! :P


Alexander Berntsen

unread,
Jun 4, 2014, 5:44:33 AM6/4/14
to elm-d...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On 04/06/14 11:39, Evan Czaplicki wrote:
> Now imagine how a typical Haskeller might answer the question "How
> do I read a file?" Monads and category theory will probably come up
> in the first or second sentence. That kind of stuff alienates *a
> lot* of people, and I have found that it is a way slower way to
> learn about monads! :P
FWIW I'd say "with readFile", and so would, I believe, most of the
Haskellers I know. If someone asked what readFile is, I imagine the
most advanced terminology I could think of using is "an IO action".

And in fact, to me, this "command" business sounds a lot like actions
in Haskell. :-]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iF4EAREIAAYFAlOO6ncACgkQRtClrXBQc7Xu+gD/XT8lpq/nOCT3jH+DXsUFPJ3I
/2yEeprukSQ3ZOdCG/IBAJNjBaWhUwdCkSql6xqK5O9KgnBCCKHk2ejmgiURjii3
=O9lh
-----END PGP SIGNATURE-----

Shashi Gowda

unread,
Jun 4, 2014, 5:53:12 AM6/4/14
to elm-d...@googlegroups.com
On Wed, Jun 4, 2014 at 3:09 PM, Evan Czaplicki <eva...@gmail.com> wrote:
Shashi, lets put the WebSockets stuff in a separate thread. (WebSocket Signal a) does not mean anything to me, so I need more explanation to know if this is relevant.

I meant WebSocket (Signal String) sorry. WebSocket is the Command.

Kim-Ee Yeoh

unread,
Jun 4, 2014, 7:40:18 AM6/4/14
to elm-d...@googlegroups.com


In Elm, the with keyword starts a “command block”. The following value, in this case http, is a record that indicates the particular meaning of the reverse arrow (<-). In this case it means chaining asynchronous calls together.

This is an interesting design choice.

On the one hand, the influence of Haskell and its type inferencing leads me to consider this painfully redundant. Why inflict the cost of additional carpal tunnel?

On the other hand, it's like adding a type signature, specifically: monomorphizing the monad. Self-documentation advantages, etc.

Overall, I think redundant wins out. Notice how getBestDirector, as a top-level definition, already practices good form with a hand-written type signature that has a "http" in it:

getBestDirector : Http String
getBestDirector = with http
  let movie    <­ getBestMovie
      director <­ getDirector movie
  return director

-- Kim-Ee

Evan Czaplicki

unread,
Jun 4, 2014, 8:50:58 AM6/4/14
to elm-d...@googlegroups.com
The Haskell way relies on type classes, which Elm does not have for now. So in Haskell, you start with a constraint like this:

getBestDirector :: (Monad m) => m String
getBestDirector = do ...

So when the m is filled in during type inference, you also deduce the implementation you need to use in the do block. F# and Elm force you to be explicit about the fact that you are providing an implementation for this special syntax, which I think has some virtues.

In a world with instance arguments, you could do something like this:

getBestDirector : {{cmd}} -> cmd String
getBestDirector = with cmd ...

It's unclear whether Elm will have instance arguments or type classes some day, but however it goes down I can imagine having a command keyword that works just like do in Haskell. I am actually pretty fond of the explicit way though :) Seeing async and seq and asyncSeq in the F# paper really helped me understand the code even though I hadn't yet figured out exactly how things work in the implementation and types.


--

John Mayer

unread,
Jun 4, 2014, 10:24:13 AM6/4/14
to elm-d...@googlegroups.com

I like this a lot as a lightweight, inline  declaration of "OK, I'm going to work in this command context now". I was doing it already a bit by explicitly let-binding >>=.

I dislike the use of the let keyword, personally

Kim-Ee Yeoh

unread,
Jun 4, 2014, 11:37:28 AM6/4/14
to elm-d...@googlegroups.com
On Wed, Jun 4, 2014 at 7:50 PM, Evan Czaplicki <eva...@gmail.com> wrote:
The Haskell way relies on type classes, which Elm does not have for now.

Yes, that's true. I'm not au courant with the discussion over classes in Elm, so I remain sheepishly indifferent about this.

So in Haskell, you start with a constraint like this:

getBestDirector :: (Monad m) => m String
getBestDirector = do ...

But that would be the wrong type, no?

It's like trying to polymorphize getLine :: IO String into something it's not, viz.

wontTypecheck :: Monad m => m String
wontTypecheck = getLine

So to reprise (sorry for not being clear earlier), it seems that in

getBestDirector : Http String
getBestDirector = with http
  let movie <­- getBestMovie
      director <­-  getDirector movie
  return director

there are at least 3 pieces of information that allows Elm to infer the http in "with http":

(1) the explicit type signature of getBestDirector,

(2) getBestMovie of explicit type Http String, and

(3) getDirector of explicit type String -> Http String.

Because of the presence of 1..3, it shouldn't be necessary to re-specify that the code is in the http command space.

Is there something I've missed in the reasoning above?

-- Kim-Ee

Evan Czaplicki

unread,
Jun 4, 2014, 2:55:30 PM6/4/14
to elm-d...@googlegroups.com
John, part of the reason for using let is that you can have type annotations on the values.

getBestDirector : Http String
getBestDirector = with http
  let movie : String
      movie <- getBestMovie

      director : String
      director <- getDirector movie

  return director

I really dislike that you can't add type annotations to these things in Haskell. It also looks more like a variable assignment like in all languages (except CoffeeScript :P).

Evan Czaplicki

unread,
Jun 4, 2014, 2:57:15 PM6/4/14
to elm-d...@googlegroups.com
So in Haskell, you start with a constraint like this:

getBestDirector :: (Monad m) => m String
getBestDirector = do ...

But that would be the wrong type, no?

I meant that the do block itself creates a constraint independent of all the other stuff.

Is there something I've missed in the reasoning above?

You are correct, but the mechanism that makes that all work well is type classes :) 

Sean Corfield

unread,
Jun 4, 2014, 6:05:46 PM6/4/14
to elm-d...@googlegroups.com
That seems to assume there's only one possible matching implementation which isn't necessarily true tho', right?
signature.asc

Kim-Ee Yeoh

unread,
Jun 4, 2014, 9:49:45 PM6/4/14
to elm-d...@googlegroups.com

On Thu, Jun 5, 2014 at 5:05 AM, Sean Corfield <se...@corfield.org> wrote:
That seems to assume there's only one possible matching implementation which isn't necessarily true tho', right?

Dunno. Could you have "with http1" and "with http2"? What would they mean?

-- Kim-Ee

Kim-Ee Yeoh

unread,
Jun 4, 2014, 10:19:17 PM6/4/14
to elm-d...@googlegroups.com

On Thu, Jun 5, 2014 at 1:55 AM, Evan Czaplicki <eva...@gmail.com> wrote:
getBestDirector : Http String
getBestDirector = with http
  let movie : String
      movie <- getBestMovie

      director : String
      director <- getDirector movie

  return director

I really dislike that you can't add type annotations to these things in Haskell. It also looks more like a variable assignment like in all languages (except CoffeeScript :P).

Oh but in Haskell you can! In two ways in fact:

(1) do { s :: String <- getLine; return s }

(2) do { s <- getLine :: IO String; return s }

Personally, I begrudge the expenditure of whitespace in a let block. The let is wasteful in that regard.

-- Kim-Ee

Evan Czaplicki

unread,
Jun 4, 2014, 10:47:50 PM6/4/14
to elm-d...@googlegroups.com
In Haskell there is only one implementation per type. The type class mechanism will yell at you if you have more than one instance of a particular type class in scope. If you want two different monads on lists you need to create a newtype for each one. This is cited as a weakness or strength of type classes depending on who you ask. It makes it possible to always derive "the one true instance" but my impression is that instance arguments may be a better way to do this without the one-instance-per-type limitation.

In F# there can be multiple implementations, but in a crazier way. They actually permit the meaning of the syntax itself to vary based on the implementation you provide. As described in 2.4, f you define merge but not andThen, that's fine but you can only use certain parts of the notation and the meaning of let! is no longer monadic bind! I thought they had trouble writing generic monadic code because they don't have higher kinded polymorphism, but the more important reason is that the conversion from syntax to meaning seems to be guided by the implementation that is provided.

Max Goldstein

unread,
Jun 4, 2014, 10:52:26 PM6/4/14
to elm-d...@googlegroups.com
I know I was pretty vocally negative on the original threads a few weeks ago but I like this proposal. If the heart of "thinking in Elm" is to define pure functions that then get lifted, there's something like that in using <- to unwrap the commands so that the variable have pure types. It kind of lets you think about your code as if the action happened instantaneously, or if blocking was okay.

I think return needs to be talked about a little bit, perhaps as the "simplest possible command - just return the value". I would avoid the word "trivial". Point out that it doesn't have to be the last thing that happens, like in C-style languages. That said, I like the name, and I like andThen as well. Do we have an fmap or apply for commands, or do we let signals handle those?

My concern over let is that there's no in. This makes it hard to tell where to block ends. I can only guess, but I suspect this will help both the programmer and the parser (no need to have two versions of let binding). On the second-to-last page, you use let twice in one block. I think this is a mistake, and that let begins a block, not declares a single constant like Swift. If it's not a mistake, or if you have reasons to not use in, I'd like to hear the explanation.

Non-HTTP examples would be nice. I'm not really sure what the WebSocket comments were (another thread please) but that library should probably be rewritten as a command. That would allow you to write a node server to access databases and whatnot.

Evan Czaplicki

unread,
Jun 4, 2014, 10:53:25 PM6/4/14
to elm-d...@googlegroups.com
Oh but in Haskell you can! In two ways in fact:

I had no idea about that! I just tried it out and got:

    Illegal type signature: `Int'
      Perhaps you intended to use -XScopedTypeVariables

So it takes a (possibly non-standard?) language extension. Also, does this syntax mean I can write this:

do { f :: Int -> Int -> Int <- return (+) ; return (f 3 4) }

I think this is not an ideal syntax, partly because of the -> <- but especially given that it clashes with the type annotation style used everywhere else.

Evan Czaplicki

unread,
Jun 4, 2014, 11:32:19 PM6/4/14
to elm-d...@googlegroups.com
I know I was pretty vocally negative on the original threads a few weeks ago but I like this proposal. If the heart of "thinking in Elm" is to define pure functions that then get lifted, there's something like that in using <- to unwrap the commands so that the variable have pure types. It kind of lets you think about your code as if the action happened instantaneously, or if blocking was okay.

Cool, I'm glad! :D The "as if blocking was okay" framing is interesting to me because I think you could use this to make a message passing concurrency library that looks pretty slick. There are serious problems with this one regarding laziness, but it has the key parts of the API. I haven't thought it through much more than that though :)
 
I think return needs to be talked about a little bit, perhaps as the "simplest possible command - just return the value". I would avoid the word "trivial". Point out that it doesn't have to be the last thing that happens, like in C-style languages. That said, I like the name, and I like andThen as well. Do we have an fmap or apply for commands, or do we let signals handle those?

This is a great point about return! Thank you! That's exactly how I think about it, so I'll add a section about that.

You have map and apply if the library provides them. It is not related to command syntax or commands directly though. If you have (Http.map : (a -> b) -> Http a -> Http b) then it's totally fine to use in command syntax, but it does not need to be built-in in any way.

My concern over let is that there's no in. This makes it hard to tell where to block ends. I can only guess, but I suspect this will help both the programmer and the parser (no need to have two versions of let binding).

It's indentation sensitive, just like typical let blocks. As long as your definitions are aligned, you are within that let. It's a bit like this syntax from JS:

var a = ... ,
    b = ... ,
    c = .... ;

I think I left out in because I wanted commands to read straight down rather than slowly creeping across the page. This gets rid of callback hell, so it's weird if it still gets the indentation problems from JS.
 
On the second-to-last page, you use let twice in one block. I think this is a mistake, and that let begins a block, not declares a single constant like Swift. If it's not a mistake, or if you have reasons to not use in, I'd like to hear the explanation.

It begins a block, but it's perfectly fine to have two blocks in a row. It's just like normal let-expressions in this regard. Does that make sense?
 
Non-HTTP examples would be nice. I'm not really sure what the WebSocket comments were (another thread please) but that library should probably be rewritten as a command. That would allow you to write a node server to access databases and whatnot.

Yeah, it would probably be helpful to write some libraries/examples that we run in our heads and see how this syntax works for them. I suspect that'd be a good way to stress test this :)

Kim-Ee Yeoh

unread,
Jun 5, 2014, 12:55:07 AM6/5/14
to elm-d...@googlegroups.com
On Thu, Jun 5, 2014 at 9:53 AM, Evan Czaplicki <eva...@gmail.com> wrote:
I had no idea about that! I just tried it out and got:

    Illegal type signature: `Int'
      Perhaps you intended to use -XScopedTypeVariables

So it takes a (possibly non-standard?) language extension.

Whoops, yes, it takes a GHC extension. But scoped type variables is among the most benign and the most conservative of extensions, up there with MPTC.

(I'm of the skeezy old skool that always runs ghci with the démodé -fglasgow-exts.)

Also, does this syntax mean I can write this:

do { f :: Int -> Int -> Int <- return (+) ; return (f 3 4) }

I think this is not an ideal syntax, partly because of the -> <- but especially given that it clashes with the type annotation style used everywhere else.

No it's not ideal syntax. That would indeed look odd! You could add parens around (f :: Int -> Int -> Int).

But I'm not sure what you're trying to do here. Monomorphizing (+) can be done in multiple ways, e.g. turn "return (f 3 4)" into "return (f 3 4 :: Int)".

Perhaps you want to communicate the type to the reader? Will a comment work?

-- Kim-Ee

Evan Czaplicki

unread,
Jun 5, 2014, 1:55:15 AM6/5/14
to elm-d...@googlegroups.com
On the question of "why have let?" I grabbed a snippet of code I'm re-understanding right now in the Elm compiler and did a comparison of do-notation vs command-syntax. Details are not all right, but just glancing at the code, I think the code texture is waaay nicer in the Elm version. It reads much more like traditional imperative code, and it feels easier to understand to me.


--

John Mayer

unread,
Jun 5, 2014, 2:03:43 AM6/5/14
to elm-d...@googlegroups.com

Does the last line always need to be a return. What if the command is already a Command type?

Evan Czaplicki

unread,
Jun 5, 2014, 2:40:22 AM6/5/14
to elm-d...@googlegroups.com
As long as the last thing is a command, it'll work. I didn't realize my examples skip this case entirely. Here's a contrived example:

-- ping a particular address
ping : String -> Http ()

ping3 : String -> Http ()
ping3 url = with http
  ping url
  ping url
  ping url

This kind of thing desugars to (cmd1 `andThen` (\_ -> cmd2)) so as long as the last thing is a command, you are all set. Does that make sense? Also, can anyone think of a nicer example for this? Here's another with state that I think kind of sucks.

Max Goldstein

unread,
Jun 5, 2014, 10:24:11 AM6/5/14
to elm-d...@googlegroups.com
Return: if the end of your command block looks like

foo <- mkCommand bar
return foo

you could replace it with mkCommand bar. The <- unwraps the command and return re-wraps it. The arrow is only available in command blocks in which it is safe to unwrap the command, where it can happen asynchronously or not at all in the debugger or whatnot.

Let: I agree that the Elm version is easier to read since everything is let-bound and indented together. It's definitely an improvement on the Haskell. It just seems weird coming from the rest of Elm, where let is always followed by in. Alternatively, if we're introducing the with keyword, is let necessary at all? And now that I think of it, I do see that the entire sequence is the command, not the last result. Although a run function would produce a signal derived for the last line's value.

I agree that the next step is to write APIs for everything and see if we hit any snags.

John Nilsson

unread,
Jun 5, 2014, 2:32:24 PM6/5/14
to elm-d...@googlegroups.com
On Thu, Jun 5, 2014 at 4:52 AM, Max Goldstein <maxgol...@gmail.com> wrote:
I think return needs to be talked about a little bit, perhaps as the "simplest possible command - just return the value". I would avoid the word "trivial". Point out that it doesn't have to be the last thing that happens, like in C-style languages. That said, I like the name, and I like andThen as well. Do we have an fmap or apply for commands, or do we let signals handle those?

It might be better to go with yield. In C# you have select[1] but also yield return/yield break[2], also in ES6[3], scala uses yield in for comprehensions[4]


BR,
John

p.s. On the topic of Scala
In Scala you can use pattern match expression in for comprehensions to match and extract values
for ((x:Int,y:Int) <- list) yield x + y
Desugars to
list map { case (x:Int,y:Int) => i+y }
Is this something you would do with commands?

BR,
John

Raoul Duke

unread,
Jun 5, 2014, 2:35:31 PM6/5/14
to elm-discuss
"return" wrt monads is the worst term anybody could have ever come up
with when trying to explain things to a person with only an imperative
background. just utterly horrible. utterly opposite meaning. evil.
wrong. dumb. confusing. confabulating. misleading. doing everything
possible to make monads utterly unsaleable to anybody.

Raoul Duke

unread,
Jun 5, 2014, 2:35:46 PM6/5/14
to elm-discuss
oh wait... was that out loud? or even the right thread?

Evan Czaplicki

unread,
Jun 5, 2014, 3:41:00 PM6/5/14
to elm-d...@googlegroups.com
John, F# uses both return and yield to do some really cool stuff! See section 2.3 for an example. I think it makes sense to have both. It may be possible to only require a definition of andThen for command syntax, so people could use whatever name they want for return.

Raoul, please try to be more productive. I also struggled with the terms do and return in the context of Haskell. There are a whole set of pedagogical issues that surround monads though. I don't think it's the same in my proposal, though I may just be blind to the problem by now. The JS people I showed it to seemed to have no issues though. In any case, if you think it could be improved, try to say specifically what problems you perceive with the proposal we are discussing.


John Mayer

unread,
Jun 5, 2014, 3:44:57 PM6/5/14
to elm-d...@googlegroups.com

That's a good point. "return" is terrible. How about lift0 ?

Raoul Duke

unread,
Jun 5, 2014, 3:57:11 PM6/5/14
to elm-discuss
> That's a good point. "return" is terrible. How about lift0 ?

i can't tell if that's a joke or not :-) because lift0 sounds too
nerdy :-) but yeah i usually think of just "lift" as an alternative
but of course that term is already taken by lots of other functional
languages and purposes.

* naming is hard
* naming is important
* you can't ever get it right for everybody
* who is your audience? know your audience.
* 'return' is the classic name from e.g. haskell for monads, i blame
haskell or whoever thought of it before then, i don't blame elm. (and
i don't blame i just think that it might have been ok historically but
now that it is about selling things to imperative programmers it is to
be avoided.)
* i don't have a concrete answer. promote? inject? raise? (no that's
for exceptions, duh.) wrap? frobulate? mwrap? (as in "monad"-wrap.)
cwrap? (as in "command"-wrap.) (cwrap is a joke since it is too close
to crap of course.) new? build? construct? englob? (unix users will
think it is a file glob then ha ha.) etc. ad nauseum :-( :-(
* but having said all that, at least lift0 is less confusing than
return i think.
¥2

Raoul Duke

unread,
Jun 5, 2014, 3:59:54 PM6/5/14
to elm-discuss
> proposal, though I may just be blind to the problem by now. The JS people I
> showed it to seemed to have no issues though. In any case, if you think it

maybe the world of programmers has evolved enough that return is not
so bad any more; maybe the audience you have is people who already are
willing to try to learn about these things or have been exposed to it
before. so maybe return is actually ok and i'm just stuck on my own
experience.

for me even when i was willingly trying to learn about it all on my
own for fun (!) then the return thing just constantly threw me for a
loop and utterly freaked me out. return in my mind is getting *out* of
something, namely a procedure/method/function, where as this all about
getting *into* something, namely the
monad-wrapper-command-datatype-container-holder-thingie.

Joey Eremondi

unread,
Jun 5, 2014, 4:07:18 PM6/5/14
to elm-d...@googlegroups.com, john.p....@gmail.com
Ooh, I really like lift0. It emphasizes the idea that a value is a 0-argument function.

Sean Corfield

unread,
Jun 5, 2014, 5:16:55 PM6/5/14
to elm-d...@googlegroups.com
Ah, I was thinking that there could be other implementations for http, such as proxiedHttp and cachedHttp, that still satisfied the Http type by providing a record with the same fields with the same types but different implementations. It seemed like a natural, substitutable polymorphism to me - are you saying Elm's type system would prevent that? Or have I completely misunderstood what the http record actually is?

Sean
signature.asc

John Mayer

unread,
Jun 5, 2014, 5:32:00 PM6/5/14
to elm-d...@googlegroups.com
I definitely think that the higher-kinded polymorphism is the way to go here, at least first steps. That helps in providing Command-like functionality without the syntax sugar.

Raoul Duke

unread,
Jun 5, 2014, 5:36:35 PM6/5/14
to elm-discuss
> In F# there can be multiple implementations, but in a crazier way.

i am sad that my "i've never used it much to really know, but i
loooove it" fanboy attitude about F# has just been dealt a torpedo
blow.

Max Goldstein

unread,
Jun 5, 2014, 5:49:05 PM6/5/14
to elm-d...@googlegroups.com
return is associated with early termination in C, and so having it in the middle of do-block without those semantics could be troublesome. This is offset by the fact that you'll usually be assigning to something, which is weird enough to draw attention to itself. I think we shouldn't use return at the end of do-blocks in examples because it will confuse people there. They'll think it's a necessary keyword.

I strongly oppose lift0 because these new commands are separate from signals.

I could go with yield though. I'm not familiar with F# but in Python it has to do with generators and in Ruby it calls an argument block. The way to think about this is that the yielding/returning is happening immediately when the command is called, but has no effect on the control flow in the do-block. I guess I have a slight preference for yield.

Speaking of control flow in the do-block, are we going to have when and unless?

Joey Eremondi

unread,
Jun 5, 2014, 5:58:01 PM6/5/14
to elm-d...@googlegroups.com
What about pure, like from Haskell's applicatives?
Or something like Wrap or Box or Insert? Something that makes it clear we're taking X and wrapping into some type Command X?

John Mayer

unread,
Jun 5, 2014, 6:20:59 PM6/5/14
to elm-d...@googlegroups.com
Yeah, pure is nice.


--

Daniël Heres

unread,
Jun 5, 2014, 6:21:56 PM6/5/14
to elm-d...@googlegroups.com

Yield so far makes most sense to me: a computation/command yields a result.

--

Raoul Duke

unread,
Jun 5, 2014, 6:33:11 PM6/5/14
to elm-discuss
> Yield so far makes most sense to me: a computation/command yields a result.

'yield' to me is too much like 'return': the sense of it is backwards
to imperative programmers. that's just my take on this.

Sean Corfield

unread,
Jun 5, 2014, 7:26:59 PM6/5/14
to elm-d...@googlegroups.com
On Jun 5, 2014, at 3:20 PM, John Mayer <john.p....@gmail.com> wrote:
> Yeah, pure is nice.

How does 'pure' signify anything about wrapping a value in a command?

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)



signature.asc

Sean Corfield

unread,
Jun 5, 2014, 7:43:22 PM6/5/14
to elm-d...@googlegroups.com
On Jun 5, 2014, at 2:49 PM, Max Goldstein <maxgol...@gmail.com> wrote:
return is associated with early termination in C, and so having it in the middle of do-block without those semantics could be troublesome. This is offset by the fact that you'll usually be assigning to something, which is weird enough to draw attention to itself. I think we shouldn't use return at the end of do-blocks in examples because it will confuse people there. They'll think it's a necessary keyword.

When I read Evan's proposal `return` seemed natural in those examples but given some of the points made in this thread, I'm feeling it's natural than it first appears, and Max's comment get to the heart of why (I think). It looks like flow control, but it really isn't, and now I think I can imagine complex Command chains which end up with return foo in the middle of them in order to wrap values in Commands..

I strongly oppose lift0 because these new commands are separate from signals.

Agreed. And, frankly, `lift0` is too 'clever' (and it reminds me strangely of Scala which I don't think is a good thing).

I could go with yield though. I'm not familiar with F# but in Python it has to do with generators and in Ruby it calls an argument block. The way to think about this is that the yielding/returning is happening immediately when the command is called, but has no effect on the control flow in the do-block. I guess I have a slight preference for yield.

I think `yield` is better than `return` but, like others in this thread, I also have some slight misgivings about it (but not enough to argue against it unless something obviously far better is proposed).

I do feel something that indicates boxing or wrapping would be more suggestive of what's really happening but I'm at a loss for a good word that is both suggestive and readable in the context of the syntax.

So my vote would be for `yield` at this point, with `return` second since I haven't seen anything else I prefer to either of those.
signature.asc

Eitan Chatav

unread,
Jun 6, 2014, 1:19:33 AM6/6/14
to elm-d...@googlegroups.com
I really like this proposal. Here's a slight extension in the same mold. In the proposal Evan defines a `for` function,

for : Command cmd -> [a] -> (a -> cmd b) -> cmd [b]

This looks similar to the type signature for `andThen` generalized for commands.

andThen : Command cmd -> cmd a -> (a -> cmd b) -> cmd b

Thus, I would suggest giving `for` a similar imperative syntax, something like

getActionDirectors : Http [String]
getActionDirectors = with http
for movie in ["Terminator","Predator","Alien"]
getDirector movie

This can be mixed together with `let`s easily. This is where asynchronous requests would be wonderful, as there is no dependency between the requests unlike the `andThen` case where you must perform the requests in correct order (you must find the best movie before you find its director). That's because `for` is actually `traverse` and can work for any Traversable container and Applicative effect.

On Tuesday, June 3, 2014 10:43:04 PM UTC-7, Evan wrote:


On Wed, Jun 4, 2014 at 7:33 AM, Evan Czaplicki <eva...@gmail.com> wrote:
Here is the proposal.

I tried teaching it to a JS person with no experience with ML-family languages, and it went super well! He immediately realized that it solved problems he had faced in writing atomic updates and dealing with promises that start running too soon. That it has obvious application to real life code is a really good sign!

This proposal is inspired by the recent post on "The ___ Monad" and related conversations. It also draws heavily upon F#'s computation expressions which I think has a very clever way of making code look nice for imperative people. F# also shares a desire to demystify things, but I tried to simplify the language and syntax even more.

Evan Czaplicki

unread,
Jun 6, 2014, 4:06:08 AM6/6/14
to elm-d...@googlegroups.com
Sean, we can have multiple implementations! Would your idea be that you'd start a with cachedHttp block and then switch to a with proxiedHttp block and have it all chain nicely? I'm pretty sure that'd be possible.

As for higher kinded polymorphism, that's very doable at this point, though I'd prefer not to turn it on for now.

Perhaps noop? Sort of makes it clear that it's a command that does not do anything.

P.S. Sorry if this email is a duplicate. My phone says it's a draft but I'm pretty sure I sent it.


On Thu, Jun 5, 2014 at 11:16 PM, Sean Corfield <se...@corfield.org> wrote:



--
Sent from Gmail Mobile

Sean Corfield

unread,
Jun 6, 2014, 5:53:49 PM6/6/14
to elm-d...@googlegroups.com
On Fri, Jun 6, 2014 at 1:06 AM, Evan Czaplicki <eva...@gmail.com> wrote:
> Sean, we can have multiple implementations! Would your idea be that you'd
> start a with cachedHttp block and then switch to a with proxiedHttp block
> and have it all chain nicely? I'm pretty sure that'd be possible.

OK, good. That's what I had expected, which is why I was puzzled about
Kim-Ee's suggestion to remove `with http` and infer it from the
types...
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

Sean Corfield

unread,
Jun 6, 2014, 5:56:19 PM6/6/14
to elm-d...@googlegroups.com
On Fri, Jun 6, 2014 at 1:06 AM, Evan Czaplicki <eva...@gmail.com> wrote:
> Perhaps noop? Sort of makes it clear that it's a command that does not do
> anything.

That won't read as "no op" for a lot of people...

Raoul Duke

unread,
Jun 6, 2014, 5:58:36 PM6/6/14
to elm-discuss
>> Perhaps noop? Sort of makes it clear that it's a command that does not do
>> anything.
> That won't read as "no op" for a lot of people...

no-op?
do-nothing?
good-for-nothing?
zilch?
dunsel? ha ha.
or: too bad, if you don't get what "noop" is, you aren't cool enough anyway. :-}

Evan Czaplicki

unread,
Jun 6, 2014, 6:26:21 PM6/6/14
to elm-d...@googlegroups.com
I actually think return is pretty good if we frame it right. I'm going to user test it on prezi folks who haven't done Haskell and see how it goes :)
--
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.

nto...@gmail.com

unread,
Jun 7, 2014, 2:50:20 PM6/7/14
to elm-d...@googlegroups.com
I also don't like the `return` name.  These should be equivalent commands:

    getBestDirector : Http String
    getBestDirector = with http
      let title <- return "Title"
          director <- getDirector title

      return getWorstDirector
      return director

    getBestDirector : Http String
    getBestDirector = with http
      let title = "Title"
          director <- getDirector title

      return director

But to an imperative programmer, the first one definitely looks like it will get the worst director.  `Yield` is a bit better since it doesn't have the same (and misleading) meaning in every imperative language like `return` does.

Ideally the name should imply both "simplest possible command --just return the value", and "causes no effect".  But both these properties are easily explained when introducing the concept, so I think it's more important that the name not have other misleading implications.  With`return`, we have to explain these traits and also point out that it doesn't have any effect on flow control.  And it's worse if someone comes across the code in the wild without any introductory explanation.

Other name ideas: pure, command, give, simply, trivial

John Nilsson

unread,
Jun 7, 2014, 3:21:02 PM6/7/14
to elm-d...@googlegroups.com
Another name to consider is select. I think it was a fantastic decision for Linq, decaded of experience with SQL to leverage in that case. OTOH people still think it's only useful for querying...

BR
John
--

Dobes Vandermeer

unread,
Jun 8, 2014, 12:23:47 AM6/8/14
to elm-d...@googlegroups.com
When words fail, maybe a symbol would work?



--

Evan Czaplicki

unread,
Jun 8, 2014, 3:05:33 AM6/8/14
to elm-d...@googlegroups.com
Perhaps result. It is very close to return, but different enough to indicate to people that things are not exactly the same. A newb would think "that's a weird syntax change" and as they get more advanced they'll see that it's subtly different.

John Nilsson

unread,
Jun 8, 2014, 8:00:13 AM6/8/14
to elm-d...@googlegroups.com
I tend to think it goes with the start word: from...select, for...yield
So maybe just let...in ?

Oh well I think we're way into bike shedding at this point, I'm sure anything will be fine.

BR
John

Alexander Noriega

unread,
Jun 8, 2014, 8:26:59 AM6/8/14
to elm-d...@googlegroups.com
What's the goal of the naming choice for this operation?

If it's to describe the resulting value, then `result`, `trivial` and `pure` are sound choices. They also express the operation in context of its relation with the other operations of the algebra.

OTOH if the goal is to describe what the operation "does", and/or what will "happen" at some point, then the other ones – `return`, `yield`, etc. – may be more suitable.

If that doesn't make sense, nevermind.

In any case, I don't have a preference. I mainly just expect that Elm remains semantically consistent with its own concepts and vocabulary.

--Alexander

Jeff Russell

unread,
Jun 8, 2014, 11:43:52 AM6/8/14
to elm-d...@googlegroups.com
What about "just"? That encourages thinking of Maybe as the simplest (non-trivial) Command.

Jeff Russell

unread,
Jun 8, 2014, 11:56:16 AM6/8/14
to elm-d...@googlegroups.com
Meant to add: also, unlike "return", "just" sounds sensible for the less "imperative-like" Command instances, too: lists (just 2 = [2]), probability (just 2 = certainly 2), writer (just 2 = 2 with null annotation), term algebras (just x = the variable labelled by x), etc. To my ear, it strikes a good balance between being informative while neutral between different applications. And the overlap with Maybe isn't really bad, since Just = just.

Christian Stork

unread,
Jun 9, 2014, 8:28:40 AM6/9/14
to elm-d...@googlegroups.com
Just want to say that independently of Jeff I thought the same thing:  "just" seems to be the word you're looking for.

Even for imperatively inclined folks the double use as in 

    just getWorstDirector
    just director

should be easy to interpret correctly as in the end "just director". ;-)

-Chris

John Mayer

unread,
Jun 9, 2014, 9:46:44 AM6/9/14
to elm-d...@googlegroups.com

Something occurred to me: why even have a keyword? Is it possible to desugar the syntax without requiring an implementation of return?

--

Evan Czaplicki

unread,
Jun 9, 2014, 10:13:57 AM6/9/14
to elm-d...@googlegroups.com
John, I think that's equivalent to type casting, where the compiler automatically inserts a return based on the results of type inference. Based on what I have read about type casts in ML-family languages, that would be a mess. But even if it was easy, I think making things more implicit is going to make things a bit more confusing.

Let's stop with the suggestions here. There's a lot of them, none with large examples, none with testing on people new to the idea of commands / monads. I am in the process of testing out the syntax on people who are new to typed functional languages, so they have no Haskell or Elm experience to cloud their assessment. return and result are both in the running, and I'll report back summarizing the feedback I get.

Evan Czaplicki

unread,
Jun 9, 2014, 10:16:13 AM6/9/14
to elm-d...@googlegroups.com
Sorry, I think that sounded mean. I don't mean to stop discussion! I just mean to say that I want to be data driven on this, and we are all biased enough to be unreliable namers. Things that are clear to us may be super confusing to others, so unless we are writing examples or bringing in outside opinions, I don't think there's a lot we can do.

Kim-Ee Yeoh

unread,
Jun 9, 2014, 10:16:36 AM6/9/14
to elm-d...@googlegroups.com

On Mon, Jun 9, 2014 at 8:46 PM, John Mayer <john.p....@gmail.com> wrote:
Something occurred to me: why even have a keyword? Is it possible to desugar the syntax without requiring an implementation of return?

YES. That's the way to go: Cleaner, Sleeker, Swifter.

The wart is not in the syntax management but the types. Can Elm craft a type system that guards against misuse and buggy code so that clean code in modern syntax can see the light of day?

For all of Haskell's talk about optional type signatures, the cruft of return and (>>=) signify tedious, manual effect signatures on the programmer and visual noise on the reader.

Do-notation ameliorates the (>>=) line-noise, but you pay for it with point-fulness and decreased functional compositionality. Moreover, do-notation doesn't solve return.

Elm needs to roll herself a better type system for greater good.

-- Kim-Ee

John Mayer

unread,
Jun 9, 2014, 11:17:36 AM6/9/14
to elm-d...@googlegroups.com

I think you misunderstand. I mean to say that the final statement will still have the Command type, but there is no such "class function" return. So in a Maybe command, the last command might be "Just foo", rather than " return foo"

Evan Czaplicki

unread,
Jun 9, 2014, 11:46:44 AM6/9/14
to elm-d...@googlegroups.com
Ohhh, smart! :D I'm not sure what the Http version would be, but that is a good idea!

On that note, I think this proposal means it may be time to introduce an ADT like this:

data Result error a = Result a | Failure error

andThen : Result e a -> (a -> Result e b) -> Result e b

formatError : (e -> e') -> Result e a -> Result e' a

...

getIfDog : String -> Result String String
getIfDog name = with result
  let animal <- readEnumFromDictionary
  case animal of
    Dog -> Result "Dog"
    _   -> Failure (name ++ " found but not a dog")

And possibly get rid of the Either library entirely? My personal feeling is that a custom ADT is always better for reading, though I have used Eithers in the Elm compiler just because it was possible. I do think that generally hurts readability where I have done it though.

Kim-Ee Yeoh

unread,
Jun 9, 2014, 11:52:01 AM6/9/14
to elm-d...@googlegroups.com

On Mon, Jun 9, 2014 at 10:17 PM, John Mayer <john.p....@gmail.com> wrote:
I think you misunderstand. I mean to say that the final statement will still have the Command type, but there is no such "class function" return. So in a Maybe command, the last command might be "Just foo", rather than " return foo"

1. Suppose you wanted the equivalent of sequence and mapM in Elm. These functions are command polymorphic. How would you write them?

2. For datatype monads like Maybe and Either, you've got Just foo and Result foo, respectively. Fine, so far. What about Http and FileIO?

-- Kim-Ee

John Mayer

unread,
Jun 9, 2014, 12:00:59 PM6/9/14
to elm-d...@googlegroups.com

Yeah, are we going to be able to support command polymorphism? If so, yeah, you're right.

I'm much more open to return than I was earlier. The alternatives are all worse :-)

--

Dobes Vandermeer

unread,
Jun 13, 2014, 1:31:19 AM6/13/14
to elm-d...@googlegroups.com

I was just reading (maybe for the second or third time) this thought-provoking blog post by Conal Elliot:


It reminded me of this discussion and I wondered if we're falling into the same trap of embedding imperative code into the pure functional paradigm.

Are we missing an opportunity to think about this in a different way to leads to an even better solution?

I don't necessarily have the answer, maybe Evan can email Conal about it and see what he has been thinking of to resolve this kind of thing.

Perhaps in the pure sense instead of commands we need a function from time to a value.  So for http what is that?  From time to the status of the http request?

I'll let you know if I get any great flashes of insight to the solution for this.
Reply all
Reply to author
Forward
0 new messages