Elm Language What do the Multiple Types in a row (without the arrow) in a signature mean? - elm

In the Elm language, I'm having a hard time explaining my question...
In these snippets in elm:
I understand the signature in something like
update : Msg -> Model -> Model
where the parameters / output is separated by arrows, but how do I read / grok things like:
Sub Msg
Program Never Model Msg
In:
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none

In a type signature, parameter types are separated by ->, with the last type being the return value.
If there are no -> symbols, then it means it is a value of that type. In your main example, the type of main is Program Never Model Msg. It has no arrows, so it takes no parameters.
Now, each parameter and the return value in the type annotation may have several things separated by spaces, as in your main example. The leftmost is the type, followed by type parameters separated by spaces.
Program Never Model Msg
| | | |
| ------|-----
type type parameters
A type parameter is similar to Generics in a language like C#. The equivalent syntax in C# would be:
void Program<Never, Model, Msg>()
C# doesn't directly correlate because it has a different way of constraining generic type parameters, but the general idea holds.
The Elm guide doesn't currently have a great deal of info, but here is the section talking about types.

Sub Msg, List Int, Program Never Model Msg
Sub, List and Program are type constructors. You can think about them as functions that take a type and return another type.
By themselves, Sub, List and Program are not complete type. They are like a puzzle missing a piece. When one adds the missing piece, the puzzle is complete.
I usually read them in my head using the word of as in a List of Ints, a Program of Never, Model and Msg.

Related

what does the elm signature mean in "Program Never Model Msg"?

I'm just starting on elm and without understanding Haskell and its compiler
I'm trying to grasp what the signature mean in Html.program
func: (a -> String) -> String -- this means expects a function and return a string
main: Program Never Model Msg -- What does this mean?
Program is a type parameterized by three type variables: flags, model, and msg. Never is a type that cannot have any value (see the link for a good explanation of what this means and how it differs from the unit type ()).
Program Never Model Msg therefore is the type of a program that doesn't have any flags (Never), has a model of type Model, and passes messages of type Msg.

Elm doesn't deduce type of Nothing value properly

I have a following piece of Elm code (getProjectView function is omitted for brievity):
type Model = Maybe List Project
model : Model
model = Nothing
getView : Model -> Html any
getView model =
case model of
Just projects ->
ul [] (List.map getProjectView projects)
Nothing -> p [] [ text "Still loading..." ]
When I try to compile the following snippet, the compiler fails with the following error:
-- TYPE MISMATCH --------- E:\dev\irrelephant-code\client\elm\Views\Projects.elm
Tag `Maybe.Just` is causing problems in this pattern match.
32| Just projects ->
^^^^^^^^^^^^^
The pattern matches things of type:
Maybe a
But the values it will actually be trying to match are:
Model
Indicating that compiler can't deduce that this Nothing is a value of type Model (which in turn is an alias of type Maybe List Project).
What am I doing wrong here? Is there a way to explicitly mark this Nothing as a value of the Model type?
I am using elm v0.18.0
You want to define model as a type alias of Maybe (List Product). Right now, with the type keyword, you are defining a new union/tag type with one value, Maybe, which expects to arguments, of type List and Product.
A working example is https://ellie-app.com/3MhPcgGzXqRa1/0

Using a single constructor union type

In Elm, I sometimes find myself defining a record type by using a union type with only a single constructor. I then use a case-statement with only a single case to unpack the record fields:
type Model =
Model { stack: List Int }
update msg model =
case model of
Model { stack } ->
...
Is there a shorter way to unpack this? The single-case case looks kind of silly. It causes unnecessary indentation and verbosity.
Also: I'm using type instead of type alias to be more explicit when dealing with the central data structures in a program (which tend to have few fields and be used in only a few places in the program, so the extra verbosity is not that much of a problem). Would it be better style to just give up on this approach and use a type alias for records whenever possible?
You can pattern match in the function signature itself:
update msg (Model model) =
model.stack ...
or, if you want to use stack directly:
update msg (Model { stack }) =
...
As for whether you should do this type of thing? I've used it in the past when explicitly trying to hide implementation details from a model. You just have to be willing to deal with the extra verbosity that it inevitably introduces. It really is up to you to decide whether it is necessary. I would probably lean towards avoiding it unless there's an explicit need.

In Elm, how to use comparable type in a tagged unions types?

I can define a tagged unions type like that:
type Msg
= Sort (Product -> Float)
But I cannot define it like:
type Msg
= Sort (Product -> comparable)
The error says:
Type Msg must declare its use of type variable comparable...
But comparable is a pre-defined type variable, right?
How do I fix this?
This question feels a little like an XY Problem. I'd like to offer a different way of thinking about passing sorting functions around in your message (with the caveat that I'm not familiar with your codebase, only the examples you've given in your question).
Adding a type parameter to Msg does seem a bit messy so let's take a step back. Sorting involves comparing two of the same types in a certain way and returning whether the first value is less than, equal to, or greater than the second. Elm already has an Order type using for comparing things which has the type constructors LT, EQ, and GT (for Less Than, EQual, and Greater Than).
Let's refactor your Msg into the following:
type Msg
= Sort (Product -> Product -> Order)
Now we don't have to add a type parameter to Msg. But how, then, do we specify which field of Product to sort by? We can use currying for that. Here's how:
Let's define another function called comparing which takes a function as its first argument and two other arguments of the same type, and return an Order value:
comparing : (a -> comparable) -> a -> a -> Order
comparing f x y =
compare (f x) (f y)
Notice the first argument is a function that looks similar to what your example was trying to attempt in the (Product -> comparable) argument of the Sort constructor. That's no coincidence. Now, by using currying, we can partially apply the comparing function with a record field getter, like .name or .price. To amend your example, the onClick handler could look like this:
onClick (Sort (comparing .name))
If you go this route, there will be more refactoring. Now that you have this comparison function, how do you use it in your update function? Let's assume your Model has a field called products which is of type List Product. In that case, we can just use the List.sortWith function to sort our list. Your update case for the Sort Msg would look something like this:
case msg of
Sort comparer ->
{ model | products = List.sortWith comparer model.products } ! []
A few closing thoughts and other notes:
This business about a comparing function comes straight from Haskell where it fulfills the same need.
Rather than defining the Sort constructor as above, I would probably abstract it out a little more since it is such a common idiom. You could define an alias for a generalized function like this, then redefine Msg as shown here:
type alias Comparer a =
a -> a -> Order
type Msg
= Sort (Comparer Product)
And to take it one step further just to illustrate how this all connects, the following two type annotations for comparing are identical:
-- this is the original example from up above
comparing : (a -> comparable) -> a -> a -> Order
-- this example substitutues the `Comparer a` alias, which may help further
-- your understanding of how it all ties together
comparing : (a -> comparable) -> Comparer a
The error you're getting is saying that comparable is an unbound variable type. You need to either fully specify it on the right hand side (e.g. Product -> Int) or specify you would like it to be polymorphic on the left hand side. Something like this:
type Msg a = Sort (Product -> a)
The question you ask about comparable is answered here: What does comparable mean in Elm?

Why is this structure declaration allowed in a built-in Function Module but not in a new one?

I'm working on a Function Module to assist with dealing with included text with logic embedded. While looking into the way SAP handles SAPScript files and parses the logic I found a structure that is declared as so:
DATA BEGIN OF events OCCURS 100.
INCLUDE STRUCTURE ITCCA.
DATA: command LIKE BOOLEAN,
template LIKE BOOLEAN,
mask LIKE BOOLEAN,
END OF events.
This obviously works, as I can trace through it while it is running a print program. So I thought I would try a similar structure in my own code but even when I copied the code 1 for 1 like above I get an error during activation. The error is
"BOOLEAN" must be a flat structure. Internal tables, references,
strings and structures are forbidden as components.
Can someone explain to me why this structure is valid in one program and not mine?
To explain the actual effect: LIKE usually refers to a data object (an actual variable) on the right-hand side, not a data type. As you rightly discovered, once you provide a data object named BOOLEAN, that is used to construct the type. If a data object of that name is not present and you're not within a class or an interface, an obsolete variant of the LIKE statement will be triggered that also takes data types into account, but only allows for certain elements on the right-hand side - namely only flat structured objects or their components. LIKE DATATYPE-BOOLEAN should have worked. As usual, the error message is somewhat less than helpful.
It seems during my initial investigation I missed a declaration for the BOOLEAN type. In the STXC function group SAP decided to declare their own variable for boolean in a different include file like this:
data: boolean(1) type c.
I had originally assumed that they were doing this with the dictionary defined type which has a similar name and is a 1 character long string. What I also found is that if I were to change my structure declaration like this:
DATA BEGIN OF events OCCURS 100.
INCLUDE STRUCTURE ITCCA.
DATA: command TYPE BOOLEAN,
template TYPE BOOLEAN,
mask TYPE BOOLEAN,
END OF events.
My code would be valid because it would then be using the dictionary defined value. So either I have to add a declaration for my own definition of boolean so that I can use the LIKE keyword or I have to use the TYPE keyword to use the dictionary definition.