“Elm requires functions to be total”
“Requires” in the sense that it’s culturally encouraged? Or did Elm get a totality checker since I last looked?
“Requires” in that the compiler rejects functions that don’t handle all constructors of their input types. For example this doesn’t compile:
f [x] = x
…it leads to the compilation error This pattern does not cover all possible inputs.
This pattern does not cover all possible inputs.
Haskell, on the other hand, would be okay with this (though it would give a warning). And PureScript would compile it but annotate its type as Partial.
Why does Elm discourage exposing type constructors? And how do they go about doing so?
They discourage it through their design guidelines, preferring “opaque” types, since constructors are the internal representation of a type, so if you expose it as an API then the internal representation and external API are coupled, which can be annoying if you want to change either.
The big downside is that you can only pattern match on constructors, so I have mixed feelings…
But it does encourage things like, for example, having two different “views” of a datatype, as shown in the article, where a non-empty list Cons a can be thought of either as (a, List a) or (a, Maybe (Cons a)) and you don’t need to know or care which one the internal representation is actually using.
(a, List a)
(a, Maybe (Cons a))
Why wouldn’t you use the (a, List a) representation?
That representation is difficult to recurse on, since if your function expects a non-empty list, then to recurse on the tail you need it to be expressible as a non-empty list.
But you’re right, you can use a fromList : List a -> Maybe (Cons a) function to achieve the same thing when working with the (a, List a) representation, it’s just slightly more verbose.
fromList : List a -> Maybe (Cons a)