top | item 11852293

Understanding the Elm type system

171 points| jaxondu | 9 years ago |adamwaselnuk.com | reply

74 comments

order
[+] spion|9 years ago|reply
> For example, when writing an Elm program I might at some point decide that users should have an admin flag. I will then try to use that flag in a function at which point the compiler will tell me that I have failed to add it to the User model. I will add it to the model at which point the compiler will tell me that I have failed to account for it in my main update function.

This beautifully explains why types are so useful. I don't know if the number of bugs goes down, but it sure is useful to have an assistant check all the implications of a change I want to make, and does that in a second.

[+] tommyd|9 years ago|reply
Absolutely agree, this is one of the biggest benefits I have found since I started working with Typescript (that, and the ease of refactoring).

Of course Flow/Typescript's approach to typing isn't as complete as Elm's, but you can still gain some of the benefits by using one of them; and for many of us, introducing Flow/TS at work is a lot more likely to happen than introducing Elm.

[+] pjmlp|9 years ago|reply
They go down and also allow for doing good AOT code generation to native code in the languages that have such toolchains, whereas dynamic languages really need a JIT.

After working on a startup that had a server architecture similar to AOL Server and also seeing some heavy Zope deployments (late 90's), I never got to understand the use of dynamic languages for large codebases.

[+] z1mm32m4n|9 years ago|reply
It's refreshing to see a piece written by someone who's new to functional programming, discovering the beauty of types and pure functions for the first time.

I very much agree with the author; learning functional programming idioms have had a profound impact on my ability to model problems in code, regardless of the language I'm using.

I admire Elm so much for putting an emphasis on the user experience, recognizing that it has been one of the biggest blockers to making functional programming mainstream.

[+] ghayes|9 years ago|reply
At first glance, Elm's type system looks like it borrows heavily from Haskell [0]. Learning Haskell's type system is a great mental exercise, even if you don't even up coding in the language.

[0] Basics: http://learnyouahaskell.com/types-and-typeclasses

[+] catnaroek|9 years ago|reply
Elm's type system is a lot simpler than Haskell's. No higher kinds, no type classes, and certainly no crazy GHC extensions. OTOH, Elm has much nicer records, though Haskell sets the bar very low.
[+] bootload|9 years ago|reply
"At first glance, Elm's type system looks like it borrows heavily from Haskell"

I stumbled on Elm looking for a good language (Haskell) to build a new language. Try compiling Elm. hint: it's written in Haskell and if you squint, the type system is a simpler derivation of Haskell.

[+] ralfd|9 years ago|reply
> Elm was my introduction to using a static, strong type system.

I think I am now officially feeling old. A language I never heard of is the introduction to programmers to static typing??

[+] klibertp|9 years ago|reply
I feel jealous instead. Being a bit old myself, I learned static typing from C and Pascal. It somewhat naturally led me to a conclusion that static type systems are these primitive, repetitive annotations which guarantee almost nothing while cluttering the code. It took me many years - and learning many languages - to recover from this perception.
[+] pka|9 years ago|reply
Well, he said "strong" :) Probably meaning a ML-derived type system, not C's or Java's.
[+] captainmuon|9 years ago|reply
> A big gotcha for me was understanding the -> syntax. How can a function that accepts two arguments possibly have a type annotation like this?

    connectWords : String -> String -> String
This is one of the most maddening things for me about Haskell-like languages. I can never remember if -> is left or right associative. I mean there is only one way that makes sense:

    # String -> (String -> String)
But it could also be

    # (String -> String) -> String
Of course you get used to it after a while, but a nagging feeling remains. I would really prefer a bit of syntactic sugar.
[+] moron4hire|9 years ago|reply
If you consider the case of currying and the fact that Haskell functions can be partially applied by just not providing all of the parameters, it becomes clear that only the first version you wrote is correct. It is a single-parameter function with a return type of another single-parameter function.
[+] bo1024|9 years ago|reply
I think when these type annotations are used, at least traditionally in e.g. Haskell, the functions are curried by default. In other words, all functions only take one argument. If all functions only take one argument, then it has to be the first interpretation, not the second.
[+] lucio|9 years ago|reply
Honest question: How useful is that all functions are curried by default? Does this impose performance penalties?
[+] dmix|9 years ago|reply
Having curry by default allows you to create very readable code using function composition, which is the primary way of transforming data through multiple steps in FP.

For example of a typical imperative approach to applying functions:

     function price(product) {
          product == 'book' && return 20
          product == 'laptop' && return 10
     }
     function addShipping(product, price) {
          product == 'book' && return price + 10
          product == 'laptop' && return price + 5
     }
     function addTax(price) {
         return price + (x * 0.13)
     }
     function total(product) {
          cost = price(product)
          subtotal = addShipping(product, cost)
          total = addTax(subtotal)
          return total
     }
Compared to a haskell-style JS which combines currying and function composition (. combines functions in Haskell):

     price :: Product -> (Int)
     price p =
          p == 'book' && () => 10
          p == 'laptop' && () => 20

     shipping :: Product -> (Int -> Int)
     shipping p =
          p == 'book' && (x) => x + 10
          p == 'laptop' && (x) => x + 25

     tax :: Int -> Int
     tax x = x + (x * 0.13)

     total :: Product -> Int
     total p = tax . shipping(p) . price(p)
Alternatively, you can easily create object specific total functions:

     totalBook = tax . shipping('book') . price('book')
     totalLaptop =  tax . shipping('laptop') . price('laptop')
Note how in the FP `total` version the data is not held in temporary variables but passed directly to the next function, which could be a performance gain.

Another benefit is how it's easier to work with curried functions when using map/reduce and list comprehensions - two other fundamental building blocks of FP programs. For example, you can pass a curried `shipping` function directly to map whereas `addShipping` would required an anonymous function (since it has two arguments).

      map(shipping('laptop'), [5, 10, 20, 30])
   
      vs

      map(function(price) { 
         addShipping('laptop', price)
      }, [5, 10, 20, 30])
The performance question is largely a question of the implementation and compiler optimizations. But considering we're working in a browser environment the "bottle-neck" is going to be DOM interaction not passing around curried functions everywhere.

Additionally, you are much more like to create functions in FP with single arguments rather than multiple in order for composition to work cleanly.

[+] mrkgnao|9 years ago|reply
Does Elm not have typeclasses?

(I looked at the pics and saw `List.map` everywhere.)

[+] ubertaco|9 years ago|reply
I've heard that Elm is a little more like an ML that looks like Haskell than it is like Haskell itself.
[+] ndr|9 years ago|reply
No, it doesn't.
[+] warfangle|9 years ago|reply
Any suggestions on where to start with Elm?

Learn You an Elm examples don't compile on the try-it-live console.

Official examples do, but fail to compile on a local install (after setting up elm package install etc).

Any suggestions on where to start troubleshooting?

It's failing to find modules that are in the core standard lib.

[+] mml|9 years ago|reply
I found many of the examples in the guides are now out of date since the recent release, which is very unfortunate.