Thoughts on 0.18

3,297 views
Skip to first unread message

Evan Czaplicki

unread,
Jul 27, 2016, 4:40:53 PM7/27/16
to elm-dev
In this post, I talked about a bunch of blog posts I was working on. The blog posts are almost all done, and I have had time to think about the upcoming technical projects more

Point is, I have a fairly concrete idea of what makes sense for 0.18, so I wanted to share these thoughts as soon as possible.


Server-Side Rendering

The implementation is quite simple, and has no real impact on public APIs.

It does change code-gen a little bit, so surprisingly, this requires major bumps to the platform, core, and html on its own.


Improved elm-reactor

Time travel went away in 0.17, but I have some plans to bring it back better than ever before.

This needs some compiler features, but I don't think it forces 0.18. By pairing server-side rendering and elm-reactor, the bump to 0.18 feels more justified. Lots of people do not care about server-side rendering, but I think the elm-reactor stuff will be very worth it.


Shuffling some Html functions

The elm-reactor improvements require a change to the Program type. (I need the Model and Msg types to safely implement certain features.) This technically will be a breaking changes in Html.App.program so it will force a major version in elm-lang/virtual-dom and elm-lang/html.

I think this is a good excuse to:
  1. Do some of this stuff.
  2. Move the contents of Html.App into Html.
I think using Html.program and Html.map will just be nicer. It does expand the scope of Html, but I think if the odd things are at the top of the docs, it won't feel too weird.

Finally, with Html.Keyed and Html.Lazy, I felt like things were looking kind of intense. Like, why so many modules to do HTML?! I think dropping Html.App will make it feel more streamlined.

I think I could be talked out of the Html.App stuff, but we should discuss that kind of thing in a new thread. Please root any comments in specific personal experience and concrete technical details.


Removing Backticks / Flipping andThen

Since server-side rendering and elm-reactor force some major changes, it's a good time to break things. The best time for that is always right this second, because it only gets harder over time. So there has been a decent amount of agreement that the order of arguments in andThen was a mistake, and if 0.17 had been more tame, it probably would have been on the table then.

The idea is to change it to:

andThen : (a -> Maybe b) -> Maybe a -> Maybe b

This order looks clearer to me as a type, and I have heard beginners do better with it because it looks like map more obviously. More importantly, this argument order means you can do things pipelined:

  something
    |> andThen doSomething
    |> andThen doAnotherThing
    |> andThen doThatLastThing

This requires folks to either define functions separately (ideal style) or to use parens, making it explicit where a closure ends (something people have been surprised by in the past, so this is better too).

This would replace the backtick infix style like this:

  something
    `andThen` doSomething
    `andThen` doAnotherThing
    `andThen` doThatLastThing

The backtick syntax is neat, but ultimately, it is redundant. In practice, it is recommended against in every case, with the one exception of andThen. By changing the argument order in andThen, we can just drop the backtick infix syntax entirely.

I know there are lots of libraries out there with andThen functions, so my advice would be keep them the same for now. If this makes 0.18, you should swap when you are upgrading. Keep it consistent in 0.17. Keep it consistent in 0.18.

If you want to discuss andThen and backticks more (how that impacts your library, etc.) let's start a new thread. Again, please root any comments in specific personal experience and concrete technical details.

Nick Hollon

unread,
Jul 27, 2016, 5:33:41 PM7/27/16
to elm...@googlegroups.com
That all sounds fantastic. Thank you for sharing!
--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elm-dev/CAF7GuPH0QadAxiAhj13Gnt_JLBXi%3DgvcXaNYUwxSRt7xkV1DJQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Daniel Bachler

unread,
Jul 27, 2016, 6:35:01 PM7/27/16
to elm...@googlegroups.com
Thank you for sharing your thoughts. Heads up like these are super confidence building :)

All of these sound like good ideas to me.

How do you feel about including other things? Would you like to push other things to the version after 0.18?

I personally would love to see some support for Blob/File and ArrayBuffer, even if only as opaque types for now, because it would enable passing these values through Elm and e.g. enable file uploads from elm at some point. Would you consider creating a meta-issue in one of the repos to track candidates for 0.18?




Evan Czaplicki

unread,
Jul 27, 2016, 6:51:04 PM7/27/16
to elm-dev
I don't want to solicit more stuff. There is always pressure to jam more into a release, but "one little thing" adds up when there are 30 of them. In this case, I'd argue that these are actually pretty massive projects from a design perspective.

I agree that Blob and ArrayBuffer are important, but I would not plan to have them in 0.18 for a few reasons:
  • They don't block 0.18 in any way.
  • They require some thoughtful design work.
  • They can be released independent of the compiler as platform libraries.
If you are interested in these features, I suggest you build an example set of specific things people want to do. Organize it. Share it with folks. Basically, do the exploration work required for the design process. Once people have blobs, what exactly do they want to do with them? What particular contexts do current Elm users need these features? I believe "I want ArrayBuffer" is a messy shorthand for "I want to do X". If you want to design things well, you need to know what X is.

If you want to do that exploration work, please share your work in a separate thread.

Yosuke Torii

unread,
Jul 27, 2016, 11:35:32 PM7/27/16
to elm...@googlegroups.com
Thank you for sharing this. All look great to me.
  • They can be released independent of the compiler as platform libraries.
So, are these web things going to be done in parallel with 0.18? Is LocalStorage before 0.18 at least? One of my library needs to have ability to save the last state. (Maybe your awesome sortable table would need that too?)


Laszlo Pandy

unread,
Jul 28, 2016, 7:30:09 AM7/28/16
to elm-dev
In Python 3, they removed backticks too and I believe it was the right choice. They removed it because it was redundant but Guido also gave other reasons for why it will never come back for another use:

Great plans Evan. 

Dave Keen

unread,
Jul 30, 2016, 3:15:40 AM7/30/16
to elm-dev
My personal experience:

- I can't remember a case where I've seen the <map> tag be used in a real webpage since the days of Netscape.  If you use Html.map for the functor and Html.map' for the tag I'm sure everyone will be happy.

- It would be wonderful to be get #19 from of 'this stuff' in to 0.18, although obviously I would say that since I raised the issue ;)

Dave

Robin Heggelund Hansen

unread,
Aug 8, 2016, 12:29:24 AM8/8/16
to elm-dev
I'm all for removing backticks, but if that does happen, could we flip the operands in the Bitwise module?

Short example from my array re-write:

        ((len - 1) `Bitwise.shiftRightLogical` 5) `Bitwise.shiftLeft` 5

Ideally I would want to write this with pipes:

  (len - 1)
    |> Bitwise.shiftRightLogical 5
    |> Bitwise.shiftLeft 5

(I wonder why I didn't use Bitwise.and here... note to self to investigate)

Richard Feldman

unread,
Aug 8, 2016, 12:36:13 AM8/8/16
to elm-dev
Good point! Here are the functions I can think of that are currently backtick-friendly and not pipeline-friendly:
  • andThen
  • andMap
  • onError
  • Bitwise.shiftLeft, shiftRight, shiftLeftLogical, shiftRightLogical
Can anyone think of any others?

john....@gmail.com

unread,
Aug 8, 2016, 2:02:27 AM8/8/16
to elm-dev
>Can anyone think of any others?
Most of the basic arithmetic functions. (-, /, //, ^, %) (Not (+), because it's commutative.) I don't expect them to change, because they're taught that way everywhere in the world since grade school, but what about changing `rem`? And what about just replacing (%) with `mod`? (%) is opaque and confusing to beginners, and experts are just going to ask whether (%) is `mod` or `rem`, because it's not consistent between languages. Look at the table here: en.wikipedia.org/wiki/Modulo_operation . In most (but not all) languages (including JavaScript), (%) takes the same sign is the dividend (`rem` behavior), but in Elm, it takes the same sign as the divisor (`mod` behavior). I think that the best solution is to just stop using (%) all together, and use the clear names `mod` and `rem` instead.

Also, note that `logBase` already takes it's arguments in the "right" order:
>logBase 10 100 == 2
>logBase 2 256 == 8

Richard Feldman

unread,
Aug 8, 2016, 2:30:54 AM8/8/16
to elm-dev
 
what about changing `rem`?

Yep, that one makes sense to me!
 
And what about just replacing (%) with `mod`? (%) is opaque and confusing to beginners

As someone who has taught Elm to many beginners, this hasn't been my experience. JavaScript, Java, C, C++, C#, Ruby, and Python all have it. I don't think Elm should deviate from the norm on that one.
 
experts are just going to ask whether (%) is `mod` or `rem`, because it's not consistent between languages.

As that table shows, mod and rem are inconsistent across languages as well. The fact that they're less inconsistent doesn't change the fact that you can't rely on them working the One True Way, and thus will still need to look them up.

So removing % would not save experts from having to look things up. :)

Also, note that `logBase` already takes it's arguments in the "right" order:
>logBase 10 100 == 2
>logBase 2 256 == 8

Agreed! 

Ian Mackenzie

unread,
Aug 8, 2016, 4:08:26 AM8/8/16
to elm-dev
I posted a thread a while ago in which I proposed making two changes that would in most cases cancel out, leaving much client code unchanged: swap the argument order for all operator functions, but then also make it so that (e.g.) a - b is interpreted as (-) b a instead of (-) a b. This means that you could continue to use a - b as you'd expect, but also that partially-applied things like (-) 2 would work properly. (The number of times I've seen (+) 2 used in sample code and worried that some people might not realize that only happens to work because + is commutative...)

In that thread I actually proposed this as a solution to the pipe-vs-backticks problem - if all current 'backtick-friendly' functions had their arguments swapped, and things like a `andThen` b translated to andThen b a instead of andThen a b, you could keep the backtick syntax while also having the same functions be pipe-friendly. But I'm all for removing backticks entirely.

Mark Hamburg

unread,
Aug 8, 2016, 1:19:02 PM8/8/16
to elm...@googlegroups.com
I like this. I have no idea how severe the transition would prove to be, but I like this in principle. Thinking about how people read mathematical expressions as a sequence of operations — e.g., "minus two" or "divide by 3" — and it becomes clear that a currying friendly language should do what Ian proposes.

Mark
--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+u...@googlegroups.com.

john....@gmail.com

unread,
Aug 8, 2016, 2:40:33 PM8/8/16
to elm-dev
>As someone who has taught Elm to many beginners, this hasn't been my experience. JavaScript, Java, C, C++, C#, Ruby, and Python all have it. I don't think Elm should deviate from the norm on that one.
I meant beginners to programming in general, not just to Elm.

>As that table shows, mod and rem are inconsistent across languages as well. The fact that they're less inconsistent doesn't change the fact that you can't rely on them working the One True Way, and thus will still need to look them up.
`rem(ainder)` is consistent; It always takes the sign of the dividend. `mod(ulo)` overwhelmingly either takes the sign of divisor, or is always positive, and is taught that way in math class. (Mod(ulo) is like going around a clock!) Furthermore, there's no language that has both `rem(ainder)` and `mod(ulo)` and doesn't do it The One True Way.

Richard Feldman

unread,
Aug 8, 2016, 2:45:58 PM8/8/16
to elm-dev
>As that table shows, mod and rem are inconsistent across languages as well. The fact that they're less inconsistent doesn't change the fact that you can't rely on them working the One True Way, and thus will still need to look them up.
`rem(ainder)` is consistent; It always takes the sign of the dividend. `mod(ulo)` overwhelmingly either takes the sign of divisor, or is always positive, and is taught that way in math class. (Mod(ulo) is like going around a clock!) Furthermore, there's no language that has both `rem(ainder)` and `mod(ulo)` and doesn't do it The One True Way.

That's true of sign but not of argument order. Languages are inconsistent about which is the divisor and which is the dividend, so there are no safe assumptions to make about how it will work; you need to look it up, period, for any language that supports this functionality.

Joey Eremondi

unread,
Aug 8, 2016, 2:58:12 PM8/8/16
to elm-dev
Here's my main hesitations for removing backtick operators.

1. They're better than symbolic infix operators, and if we take them away, we're going to see people using more <$> and <*> and <* and <<< and other awful, hard to google operators.

2. Languages like Lisp get away without them because they have functions with variable numbers of arguments. You can do (times m1 m2 m3) in a language like Lisp. This doesn't generalise well to a typed language, especially if the two arguments aren't of the same type. Then you can't even put them in a list.

3. Not having them really hurts left-to-right, non-commutative operations that we like to chain.

Some examples:

Matrix multiplication: If (m1 |> Matrix.times m2 |> Matrix.times m3) is equal to what math calls m3*m2*m1, we're going to confuse anyone who's ever taken a linear algebra class. What's worse, this kind of error can't be caught by the type system, since, if your matricies are square, changing the order is always well typed.

But conversely, saying that (times m1 m2) is equal to m2*m1 will confuse anyone who is using the function, especially if they're currying it.

List cons: for example, in my (out of date) SafeList library, we've defined a custom cons. Without infix operators, we'd need one of the following:
    (SafeList.null |> SafeList.cons 4 |> SafeList.cons 3) to get the list [3.4]. This is totally backwards.
    OR, we have reversed, so cons [3,4] 2 gives you [2,3,4]

Any List-like structure: where we want to append chains of things like we do with ++. Maybe someone writes an ArrayList library or something.

Potential Solutions (that I can think of)

1. We use "flip" everywhere. It's awkward and verbose, and newcomers will wonder why we have to do it when their langauge (every other language?) lets them write things that look like math.

2. We have some shorthand operator for flip. So instead of (m1 |> (flip Matrix.times) m2), you can do (m1 |> ?Matrix.times m2.). Still arbitrary, and more cryptic.

3. A ternary operator. This is probably a *really* bad idea, but I think it's worth discussing. If we had something like this
  m1 |> Matrix.times ||> m2  |> Matrix.times ||> m3, where (x |> f ||> y = f y x )
(Alternately, could this be simulated with an infix operator with low precedence versus function application? I'm not up on my parsing.

4. We leave back-ticks in.

To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elm-dev/2AE4D567-52FF-448C-91E9-39A31232EE3A%40gmail.com.

Richard Feldman

unread,
Aug 8, 2016, 4:54:56 PM8/8/16
to elm-dev

1. They're better than symbolic infix operators, and if we take them away, we're going to see people using more <$> and <*> and <* and <<< and other awful, hard to google operators.

Pipeline-friendly functions are better and more popular than either. Shouldn't we expect to see more pipeline-friendly functions instead?

2. Languages like Lisp get away without them because they have functions with variable numbers of arguments. You can do (times m1 m2 m3) in a language like Lisp. This doesn't generalise well to a typed language, especially if the two arguments aren't of the same type. Then you can't even put them in a list.
 
3. Not having them really hurts left-to-right, non-commutative operations that we like to chain.

Some examples:

Matrix multiplication: If (m1 |> Matrix.times m2 |> Matrix.times m3) is equal to what math calls m3*m2*m1, we're going to confuse anyone who's ever taken a linear algebra class. What's worse, this kind of error can't be caught by the type system, since, if your matricies are square, changing the order is always well typed.

But conversely, saying that (times m1 m2) is equal to m2*m1 will confuse anyone who is using the function, especially if they're currying it.

If you're doing a bunch of matrix math, introducing a |*| operator seems like a fine approach to me. :)
 
List cons: for example, in my (out of date) SafeList library, we've defined a custom cons. Without infix operators, we'd need one of the following:
    (SafeList.null |> SafeList.cons 4 |> SafeList.cons 3) to get the list [3.4]. This is totally backwards.

If maintaining order is the goal, writing SafeList.cons 3 <| SafeList.cons 4 <| SafeList.null seems straightforward enough, yeah?

Joey Eremondi

unread,
Aug 8, 2016, 4:59:33 PM8/8/16
to elm-dev
If maintaining order is the goal, writing SafeList.cons 3 <| SafeList.cons 4 <| SafeList.null seems straightforward enough, yeah?
 
Well... I guess that should have been obvious.

Never mind, carry on.

--
You received this message because you are subscribed to the Google Groups "elm-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-dev+unsubscribe@googlegroups.com.

Max Goldstein

unread,
Aug 9, 2016, 12:44:26 AM8/9/16
to elm-dev
I think it's fair to say that backticks will not be removed in 0.18, so I don't want to get distracted with rem/mod (handing division by zero consistently is another discussion) and bitwise ops. (Well, just a bit on that: I find myself writing Bitwise.rightShift or Bitwise.rightshift instead of Bitwise.shiftRight.)

Robin Heggelund Hansen

unread,
Aug 9, 2016, 12:49:14 AM8/9/16
to elm-dev
Why do you think that's fair to say?

Simon Hampton

unread,
Aug 10, 2016, 2:05:56 PM8/10/16
to elm-dev
I'm with Daniel here - making more progress on WebApis feels like the higher priority at present so enable apps to be built that can compete on functionality with React that got the WebApis 'for free'.

James Wilson

unread,
Aug 10, 2016, 7:25:18 PM8/10/16
to elm-dev
I'd agree on the web APIs being a fairly high priority; ports just aren't as nice to compose and work with as tasks so every time I bump into a thing that there isn't a task for (yet) eg localStoage, I get a little frustrated.

Tim Stewart

unread,
Aug 22, 2016, 8:09:04 AM8/22/16
to elm-dev
The ideal complement to Evan's proposed andThen changes would be this other proposal for Task Ports:


As James says, ports - or rather Cmds and Subs - aren't as nice to compose as Tasks. Don't get me wrong, they are absolutely great for many things, particularly pub/sub style communications like RethinkDB's recent Horizon. But they become awkward when you have to chain several asynchronous calls.

Having task-based-ports available would reduce the barrier to integrating with a wide range of libraries and platform features. Sure, it's great to plug direct to "the web platform" in Elm, particularly if you're privileged enough to publish native libraries, but imagining full coverage of even the standards-driven aspects of the platform, let alone proprietary extensions such as the aforementioned Horizon library, is a pipe dream similar to boiling the ocean.

Elm will be a great application programming language, but there will always be a need for "native drivers" written in JavaScript - or in the future C, Erlang, whatever. Tight coupling to these drivers, as in native libraries, is brittle and opaque. The proposal for task ports would provide a great delineation between the Elm application and native drivers for composed asynchronous operations.

The general themes of the other 0.18 proposals are great, as always, and reflect well on Elm's leadership and community engagement.  

Johny Why

unread,
Aug 8, 2017, 1:40:50 PM8/8/17
to elm-dev
Richard Feldman wrote:
removing % would not save experts from having to look things up

and keeping it reduces keystrokes. Very important!

% might not be the only one that differs across languages. Devs need to master the operators and keywords of that language. Can't let other languages define what Elm is and isn't :)
Reply all
Reply to author
Forward
0 new messages