Hacker News new | past | comments | ask | show | jobs | submit login
Elm by Example (elm-by-example.org)
127 points by brudgers on Dec 13, 2015 | hide | past | favorite | 37 comments

Elm is a profoundly interesting language, but I've had mixed experiences developing in it. I've been working on a small-but-hairy web app using Elm, and my first reactions are:

1. Elm encourages highly-rigorous state handling, thanks to the React-like "Elm Architecture" https://github.com/evancz/elm-architecture-tutorial/ and features like the Maybe type and ADTs. I rarely worry that I've overlooked some horrible, asynchronous corner case or that something might be null.

2. The Elm architecture requires a considerable amount of boilerplate code, at least relative to something like Ember.js. It's often necessary to thread significant amounts of application state through parameters, for reasons similar to the ones here: https://docs.google.com/presentation/d/1afMLTCpRxhJpurQ97VBH... But when you combine that with the need to use "case" matches everywhere to destructure ADTs, and the fact that you can't define guard expressions (or destructure things nested inside record types), it tends to result in a lot of extra typing.

3. The Elm tooling is a mixed bag. The error message are the best I've ever seen, but the sweet time-travelling debugger just refused to run when I tried to use it with the Elm architecture tutorials. This pattern of brilliance and brokenness feels fairly pervasive at this point.

4. The Elm community can occasionally be a bit frustrating to work with as an outsider. Sometimes, when I run into an issue, I eventually find a GitHub discussion like this: https://github.com/evancz/start-app/pull/37 , where somebody is trying to address an actual issue (one that was biting me, too), and the community is more-or-less throwing up its hands. Also, the Elm community has an occasionally surprising tendency to close GitHub issues quickly, even if it's known that they'll probably need to fix something someday: https://github.com/evancz/start-app/issues/35#issuecomment-1...

5. For me, Elm feels considerably more restrictive than programming in Haskell.

6. Elm's type checker and design do have one huge payoff: Even very complicated apps with lots of tricky asynchronous state tend to work on the first run after they make it past the type checker. This is hugely valuable, and it makes me more inclined to forgive Elm's limitations elsewhere.

Overall, Elm is exploring some really important ideas, and it's pushing the general React-style architecture to its logical limits. I think I'd consider it for small- to medium-sized apps with lots of tricky state, and with developers who are strong at functional programming.

I've had a similar experience. I was ok with it though, because the pure data -> DOM nature of Elm's architecture was better than anything else.

Until I found PureScript. Especially purescript-halogen [0], which is basically how one might make React in Haskell. Everything is still pure, but you can define nested state (not to be confused with React-style local mutable state!) without Elm's boilerplate wiring.

The "update" function runs in the continuation monad, which means that one can do something like this:

        modify (\(State s) -> State $ s { loadingUsers = true })
        users <- liftAff' $ fetchUsers
        modify (\(State s) -> State $ s { users = users, loadingUsers = false })
and this runs asynchronously, but it still modifies one global state. Compare with Elm's "return fetch effect, fire event, react on event in update function".

There's everything one might need in PureScript: functors, applicatives, monads, so the language feels much less constraining than Elm.


- they use bower for package management - it's ok, but elm-package's enforced semversioning is really nice.

- no default imports, so imports can get verbose [1]

- the error messages might not be as nice as Elm's, but I haven't had a problem so far, at all

- no elm-reactor - though since halogen is just a library, the UI is pure and there's no magic state-isolating runtime, you can probably roll your own relatively easily

- not as easy to pick up as Elm - but no problem for a Haskell programmer. Elm definitely focuses more on user-friendliness and Javascript converts.

...but these are minor nitpicks - I am in the process of making the switch and porting an app from Elm right now, and it definitely was the right decision.

[0] https://github.com/slamdata/purescript-halogen

[1] https://github.com/slamdata/purescript-halogen/blob/master/s...

One more thing that just made my life easier: Purescript recently got generic deriving [0], which means that one can derive Eq, Ord, JSON etc instances easily now!

[0] https://github.com/purescript/purescript-generics

With respect to (2), I've found non-trivial forms to be an especially annoying example. There seems to be lot of fairly trivial repetition in elm architecture code which I can't really manage to extract.

I've found lenses (for zooming state) and prisms (for zooming actions) to be very useful for solving this problem.

Can you comment on your latest approach in purescript-thermite to use lenses (focus, foreach) vs halogen architecture? Pros / Cons?

I haven't used latest Halogen yet, so I can't give a detailed comparison, but the approach in Thermite [1] is indeed what I am referring to above. It was based on some work in the OpticUI library [2], and has worked out quite nicely for the few projects I have applied it to.

I will say that the types in Halogen seem more complex, but they can be justified by the fact that the coproduct machinery helps ensure all actions get handled somewhere. This may be possible with prisms, I'm not sure yet. This library [3] seems relevant.

One issue I've found, which I understand the Halogen folks are also finding, is that it is tricky to wire up action handlers such that a subcomponent can invoke an action and have a parent act on it and invoke its own action in response. Granted, this is a relatively infrequent use case, and you can always push such logic into some shared parent.

[1]: https://github.com/paf31/purescript-thermite/ [2]: https://github.com/zrho/purescript-optic-ui [3]: https://github.com/joneshf/purescript-totally

Does Elm have a lens library?

I know there is the Focus library [1]. I'm not sure about prisms, traversals etc.

[1] http://package.elm-lang.org/packages/evancz/focus/1.0.1/Focu...

Do you have experience with similar languages for frontend development? Like purescript or ghcjs? Did you chose elm just because it was the first thing you tried or because you had frustrating experience with others?

Elm has neat ideas, but there's really no need for an entirely new programming language just for functional reactive stuff, and I'm just not into languages that don't support dynamic and iterative development at the REPL. Hot swapping code is a poor replacement for a real REPL.

I think it's great to have more choices of purely functional, statically typed languages for web programming. The only other options I'm aware of are GHCjs and Haste (huge runtime size, incomprehensible code, laziness), and PureScript. PureScript to me seems like the ideal choice of the options available, with a more powerful type system and more of a general-purpose approach. But it doesn't have Elm's tooling, its nice error messages, or its wealth of FRP tutorials. Certainly, PureScript doesn't obviate Elm IMO.

As to the lack of a REPL, doesn't Elm have one? http://elm-lang.org/blog/announce/repl

From the linked page:

"Before explaining how that program is written, let’s first make a small detour, and introduce the REPL (Read Eval Print Loop) tool, that Elm — like many other functional languages — provides. The elm repl command starts it, like so:"

I started learning and tinkering with Elm back in early October and was impressed (still am). However, I quickly found myself wishing for more flexible type-level abstractions; namely type classes[1], though I didn't realize it yet because I didn't have a Haskell background. Also, I was a little wary of dipping too much into ffi territory (which I sensed I would need to do for my use cases), since that aspect of the language is still in flux (at least, that was my impression).

In November, I started learning PureScript, and I've found PureScript by Example[2] to be an excellent resource. There are a few sections where I think the author moves a little too quickly, but I found that by spending time in psci (PureScript's REPL) I could usually fill in what, for me, were gaps in the explanations. The REPL has further proved invaluable in my fledgling development efforts with PureScript, and the language's ffi is straightforward.

[1] https://en.wikipedia.org/wiki/Type_class

[&] https://leanpub.com/purescript/read#leanpub-auto-type-classe...

[2] https://leanpub.com/purescript/read

Elm with a magic backend could outshine meteor. Elm apps are fast.

Edit: magic translates to instant backend and using just one language for everything. Elm-native-ui seems like a step in the right direction: https://github.com/elm-native-ui/elm-native-ui

I don't know about magic, but Elm with an Elixir/Phoenix backend seems quite nice. http://www.cultivatehq.com/posts/phoenix-elm-1/

...and I have something new to my to-learn list

Too bad the Elm site is down - http://elm-lang.org.

That seems to happen a lot. Every time something gets posted it seems to get down. That said, I'm pretty sure it's just one guy managing it all as a side project, so it makes sense that he wouldn't want to pay for an expensive host for all the traffic he gets.

Evan Czaplicki was actually hired by Prezi to work exclusively on Elm, so the project has industry backing.

It was intermittently up and down last night (US Central Time) before I posted this.

every time there is an article about elm I have to fight my first impulse which is to wonder at it still being developed, I guess I spent way too many years typing elm to get to my email to ever be able to associate it to anything else

What is the history of the `.` notation? In Javascript

  obj={key: "val"};obj.key === obj["key"]; => true
and the same in many other languages, like Java, C#, Elm, Haskell (I think) et cetera. The only ones in common use I can think of now is Lisp & C (but of course there are thousands).

This pattern of putting the key to get after the object seems popular, but what is the history behind it?

And why is it uncommon to have something like get(obj, key)? Or like in Clojure, `(get {:key :val} :key)`? Seems more consistent, and uses less syntax.

> Haskell (I think)

Not Haskell, record fields are accessed via automatically generated accessor functions e.g. after defining

    data Car = Car {company :: String, model :: String, year :: Int}
you'd have

    company :: Car -> String
    model :: Car -> String
    year :: Car -> Int
> And why is it uncommon to have something like get(obj, key)? Or like in Clojure, `(get {:key :val} :key)`? Seems more consistent, and uses less syntax.

Isn't get for collections? While historical javascript conflates records and hashmaps, most languages have a rather strict difference between the two, including clojure I believe. And do provide some sort of `get` for maps (as a free function or as a method depending on the language's base paradigm). I know of few languages where accessing record attributes and map values uses the same syntax[0]

And in clojure get isn't even necessary as most collections also act as functions e.g. `(get {:key :val} :key)` can be replaced by `({:key :val} :key)`, or (for maps only, IIRC integers are not IFn) `(:key {:key :val})`

[0] natively anyway, I have seen e.g. Python libraries where maps were extended to provide attribute-access to their values.

I would agree with the preference for Clojure-style syntax, though using a getter function makes more sense in a language like Clojure with immutable maps (as opposed to most of the languages you mentioned). At least in OO languages the dot operator feels too much like a class rather than a map/dictionary. An acceptable alternative is the C++ STL map square bracket indexing.

In Vb.NET there's a ! operator for name-like dictionary lookups, so Foo!Bar is equivalent to Foo("Bar") where Foo is a dictionary.

Because they're not the same thing. One is a record/field lookup, the other is a map lookup.

For record lookups, there is no key. What would you pass to the get function as the key? A string? Now the compiler can't reason about it at compile time. It turns into a runtime lookup. In languages like OCaml, there is no reflection, so there is no way to do a runtime lookup of a key. It has to know the name at compile time, and the name is then translated to a byte offset into the record.

In Elm you use static types to think of Javascript objects as static, anonymous records instead of dynamic dictionaries. There's a different type for that second use case with no syntax (just `get`).

Theoretically this could enable a lot of optimization for Elm, but at the moment I don't think the compiler takes advantage of this. It's the same thing that Google's Closure compiler does.

Just a nit; Java does not use dot notation for map access. (Groovy can, but can also use Java notation.)

Not that it matters, but how do you reckon

`get(obj, key)` is less syntax than `obj.key`?

I'm guessing they're talking about the syntactic footprint of the language. Of course that assumes:

1. you have free functions at all (wasn't the case in Java until 1.5 and static imports IIRC, still isn't the case in Smalltalk, and seemingly free functions in Ruby are actually methods of Kernel, an actual free function would be a Proc bound to a global name)

2. you only have free functions, or all your syntax is expressed with free function syntax (which is fairly specific to Lisps, Julia also has generic functions but afaik still uses dotted attribute access)

Except that `obj.class` gets the class for the object, instead of `get(obj, "class")` or `obj["class"]`. So many little special cases in Groovy's huge grammar, which is still written in Antlr 2.x when both Antlr 3 and 4 shipped long ago.

Yeah, the concepts are different, but usage overlaps. That is why I consider them equal.

IMHO elm is for haskell, what meteor is for javascript. Even without support from purists, they conquered their market share. This irritated a lot of people.

Politics are boring but they are important. Imagine if all these smart people from haste, fay, purescript, ghcjs could join elm and extend it. Haskell could finally get a (deserved) mainstream role in CS. I don't know Evan (elm's creator) but he seems to have enough charisma for the job. Let's see if he can play the politics.

HelloWorld1.html of 33KB in size irritates me. Elm should have something like


Isn't it because all of the Elm runtime and all linked libraries are embedded in the page, and Elm doesn't have DCE yet? I don't think there is much (if any?) optimisation in Elm at that point.

Is the CSS host down? Because I'm used to Elm tutorials being very nicely styled, and this is... not so much.

Today I learned people still use full text justification. Interesting content, but I found the actual readability a bit rough for me.

Applications are open for YC Winter 2022

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact