As I’m reading through the docs and source code I’m often confused by some more “advanced” topics that probably stem from my poor understanding of type annotations and functional programming in general.
One such example is this:
type Foo bar = Foo
I don’t really understand the role of “bar” here and how to treat it.
Real world examples of that, from the docs:
type Maybe a = Nothing | Just a
or from the core module Sub:
type Cmd msg = Cmd
What is that supposed to mean? I am defining a type Cmd which is equal to itself? And what’s the role of msg?
Another example of code I don’t really understand (taken again from the docs):
type Msg = GotText (Result Http.Error String)
I am having problems reading this kind of code.
I’m defining a type Msg which can be GotText (okay, fair enough) but what’s that tuple after?
I am sorry if I duplicated a question, I tried googling and searching here on discourse but have had no luck.
So I think this is 3 different examples of 3 different things.
This is a “phantom type” and it’s an advanced trick to make sure you can only combine functions together in certain ways. If you’re still learning about Elm types I’d leave this till later. Here’s an article about them anyway:
This is not something you could ever define in your own code, that’s why it seems so weird. Cmd is special because it’s implementation is part of the Elm runtime and is implemented in JavaScript not in Elm. You will never be able to use this in pure Elm code so it’s not necessarily something you need to dig deep into. It’s basically saying “kernel magic here”.
There are two layers of “wrappers” here, which is what makes it tricky. Result Http.Error String is the result of a calculation that can succeed or fail. It can be either a String wrapped in an Ok constructor or an Http.Error wrapped in an Err constructor. See here: https://package.elm-lang.org/packages/elm/core/latest/Result
Then GotMsg is a wrapper around that (successful or error) value. In your example there’s only one constructor in the Msg type so it might seem pointless to have a wrapper at all. But in a real app you’d have lots of Msg constructors for all the different things that could happen in the app. This particular one can contain either successful data or error data.
@Brian_Carroll’s answer is great. Just to address the one other example you lifted from the docs…
This is a type variable as discussed in the guide here. It’s describing the fact that you can have a Maybe of any other kind of type, e.g. a Maybe Int which would have the value of either Nothing of Just <some Int value>, or a Maybe String which would have the value of either Nothing or Just <some String>.
I have still problem reading this kind of syntax tho but I guess this is personal.
How would I know by reading this that GotText is a type of type Result containing either an Http.Error or a String? If I had GotText (A B C D), would it mean I have a type GotText which is a type A containing B C or D as payloads? Would that be correct if A was defined like so?
type A b c d =
B b |
C c |
D d
?
So I can later do matching based on B C D values b c d?
type Result error success
= Err error
| Ok success
It’s using two type variables, allowing you to specify what kind of values will be in the Err and Ok wrappers.
I find it helpful to read the pipe | as OR.
So the type Result Http.Error String means “a value that is either Err wrapping an HTTP error or an Ok wrapping a string”. Some concrete examples would be:
Ok "hello"
Err NetworkError
Err (BadStatus 404)
Check out the Http.Error docs for more details on what might show up inside the Err here
Msg
You’ve defined your Msg type to look like:
type Msg = GotText (Result Http.Error String)
That is to say, all Msg values are one of the result values discussed above, wrapped in GotText.
Some concrete examples would be:
GotText (Ok "hello")
GotText (Err NetworkError)
GotText (Err (BadStatus 404))
Your further question on type variables
If you’ve never encountered Result before, you can’t know that just from reading Result Http.Error String. You can see that the Result type takes two parameters but you can’t know that the value will be one or the other (as opposed to one and the other) without looking at the definition in the docs.
In practice since Result is part of the core library and is heavily used in Elm code, most people just memorize that it means you’ll get one or the other of the type parameters wrapped in Err or Ok respectively
Yes. Using your type above and some more concrete types for the parameters, A String Bool Int would mean “either B wrapping a string or C wrapping a bool or D wrapping an integer”. One of the benefits of a type like this is the ability to pattern-match on the different branches.