
Understanding the Elm type system - jaxondu
http://www.adamwaselnuk.com/elm/2016/05/27/understanding-the-elm-type-system.html
======
spion
> 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.

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

~~~
klibertp
> whereas dynamic languages really need a JIT

Dylan?

Honestly asking - I used the language a bit but I didn't read anything on its
implementation(s).

~~~
pjmlp
Lisp also has good AOT compilers, but you need to provide the necessary
(optional) type annotations.

So in the end you end up with code that looks no different than from static
languages.

With ML-like type inference and dynamic type support, there is hardly any
disadvantage with static type languages and you get the tooling support.

Dynamic languages can have very nice tooling, and we are yet to regain what
was possible in Xerox and Genera environments, but your environment needs a
live image of the code to produce good results, hence JIT.

------
z1mm32m4n
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
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](http://learnyouahaskell.com/types-and-typeclasses)

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

~~~
elcapitan
Is Elm actually closer to Haskell or is it rather a typed Javascript in
Haskell syntax?

The documentation looks very accessible, there's a bit of introduction to the
language, and then hands-on, that's nice.

edit: by documentation I meant this gitbook:
[https://www.gitbook.com/book/evancz/an-introduction-to-
elm/d...](https://www.gitbook.com/book/evancz/an-introduction-to-elm/details),
but the official documentation looks similar.

~~~
catnaroek
Elm has two things in common with JavaScript: strict evaluation and being
designed for client-side Web development. In every other respect, Elm is
closer to Haskell and diametrically opposite to JavaScript:

(0) statically and strongly typed, with type inference

(1) algebraic data types with pattern matching and exhaustiveness checking

(2) purely functional

~~~
bootload
(3) immutability ~ [https://www.google.com/search?q=elm-
lang.org+immutabilty](https://www.google.com/search?q=elm-
lang.org+immutabilty)

~~~
catnaroek
I would've added it to (2), similarly to how (0) and (1) are each a group of
related features.

~~~
bootload
@catnaroek your right, it's really implied by being a FP language.

------
ralfd
> 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??

~~~
pka
Well, he said "strong" :) Probably meaning a ML-derived type system, not C's
or Java's.

~~~
catnaroek
Why should anyone's first statically typed language be a weakly typed one in
2016?

~~~
steveklabnik
As if "strong" and "weak" weren't overridden enough when it comes to type
systems, many people will use "stronger" and "weaker" when talking about the
relative amount of guarantees a static type system can give you. "C has a
fairly weak static type system, and Haskell has a very strong static type
system."

Since the "strong" vs "weak" axis of type systems is already not extremely
useful, as very few languages are truly weakly typed, it's usually fine.

~~~
catnaroek
Okay, then let me define a new axis: useful vs. useless type systems. A type
system is useful to the extent types rule out implementation errors and/or
miscommunication between the implementer and the user of an abstraction:

(0) Algebraic data types, pattern matching and exhaustiveness checking rule
out forgetting to handle all the qualitatively different possible outcomes of
an operation.

(1) Statically typed effects rule out sneaking effects into computations
without making them knowable to the user.

(2) Substructural type systems rule out resource leaks and uses after free.

(3) Rust's borrow checker rules out concurrent modifications of the same
object in memory.

How usefully typed are C and Java?

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

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

~~~
Kiro
Doesn't String -> String -> String mean it takes two arguments in this case
though?

> The type annotation for connectWords is telling us that connectWords is a
> function that accepts two strings and returns a string.

Or maybe I misunderstand this whole thing. I know nothing about Haskell/Elm.

~~~
ncd
It's a function that takes a String and returns a new function which takes a
String and returns a String. All functions in Haskell/Elm are arity 1.

So in order to construct functions that accept more than one argument, you
actually return successive functions that apply successive arguments, known as
currying.

~~~
Kiro
Thanks! Is this the case in Elm as well? This is the example they give:

    
    
      connectWords : String -> String -> String
      connectWords firstWord secondWord =
        firstWord ++ secondWord

~~~
elbenshira
In this case, you think of `connectWords` as a function that takes two
arguments. But since it is curried, you can also do this:

    
    
        let prefix = connectWords "Hello "
            world  = prefix "world"
            bob    = prefix "bob"
        in ...
    

`world` is "Hello world", and `bob` is "Hello bob". That is the power of
currying. A maybe more useful example is specifying the mapping function in
`List.map` without supplying the list to map over. This allows you to use the
same map with multiple lists.

------
lucio
Honest question: How useful is that all functions are curried by default? Does
this impose performance penalties?

~~~
dmix
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
Does Elm not have typeclasses?

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

~~~
ubertaco
I've heard that Elm is a little more like an ML that looks like Haskell than
it is like Haskell itself.

------
warfangle
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
I found many of the examples in the guides are now out of date since the
recent release, which is very unfortunate.

~~~
ocean3
[http://guide.elm-lang.org/](http://guide.elm-lang.org/) \- contains examples
that do work.

~~~
warfangle
They work in the live editor on elm-lang.org. But not locally.

Maybe my environment isn't set up correctly?

I tried the first one (the counter). Copy/pasted directly into a dir as
'counter.elm'. Initialized the dir with 'elm package install' to get the core
library.

    
    
        $ elm make counter.elm
        I cannot find module 'Html'.
    
        Module 'Main' is trying to import it.
    
        Potential problems could be:
          * Misspelled the module name
          * Need to add a source directory or new dependency to elm-package.json
    
    

Edit/update: source-directories is ["."] in elm-package.json. Dependency "elm-
lang/core": "4.0.1 <= v < 5.0.0" is in elm-package.json. elm-version is
"0.17.0 <= v < 0.18.0". elm-make is elm-make 0.17 (Elm Platform 0.17.0).

core is installed in elm-stuff/packages:

    
    
        macbook:core me$ pwd
        /Users/me/elm-learning/elm-stuff/packages/elm-lang/core
    

additionally,

    
    
        macbook:elm-learning me$ which elm
        /usr/local/bin/elm
    

Edit2: so, I then guessed that Html wasn't a part of the core lib, and tried
to install evancz/html, but apparently that doesn't work with 0.17 (I get an
error about version constraints). I'm guessing the elm-lang.org editor doesn't
use 0.17?

~~~
dywedir
In Elm 0.17 use elm-lang/html

~~~
warfangle
Thanks.

Kinda amateur hour with leaving that out of the guide. I wonder how many
others have gotten as frustrated and stopped trying to learn it!

I'll submit a PR to update the docs in a lil bit.

------
lucio
the elm debugger seems also impressive

[http://debug.elm-lang.org/](http://debug.elm-lang.org/)

~~~
iamcreasy
Wow! That's impressive.

Is this possible to do in imperative languages? i.e. in C++ or Java? If not,
why?

