What is the correct type annotation?


#1

See also this Ellie: https://ellie-app.com/4fMnyrx7KLra1

I am using Elm-markup (great library!) and I am studying the example in the source code. The type annotation of document is document : Mark.Document (model -> Element msg). So, the result is a view function.

So, this code works just fine:

show : Html msg
show =
    case Mark.parse document content of
        Ok element -> element () |> Element.layout []
        Err errors -> errors |> Debug.toString |> Element.text |> Element.layout []

For my use case I will be using several types of documents. So, I thought of creating a generic show function that accepts a document of type Mark.Document (model -> Element msg). Like this:

showGeneric documentType str =
    case Mark.parse documentType str of
        Ok element -> element () |> Element.layout []
        Err errors -> errors |> Debug.toString |> Element.text |> Element.layout []

show1 =
    showGeneric document content

And this code also works just fine. However, showGeneric does not have a type annotation. So, I added:

showGeneric : Mark.Document (model -> Element msg) -> String -> Html msg

But, then the compiler throws an error:

The 1st argument to `element` is not what I expect:

51|         Ok element -> element () |> Element.layout []
                                  ^^
This argument is a unit value:

    ()

But `element` needs the 1st argument to be:

    model

I am struggling to understand what the correct type annotation for showGeneric should be or why showGeneric does work without a type annotation and not with a type annotation.

Can someone help or explain this?

Thanks.


#2

You need to replace model with ()

https://ellie-app.com/4fQT59HZPJha1

The lowercase names in type signatures are type variables. So a function might take List a for any a but a particular list value must be List Int or List String. A function that has List a in its signature is saying “I will work for any list, regardless of what it contains!”

In this case model and msg are type variables because they’re lowercase. So it’s saying the function should work for any model type. But the code actually only works if the model type is the empty tuple (). That’s a type error.

My advice would be to completely avoid type variables in app code, at least until you’re more comfortable with this stuff. Libraries have to use type variables because they can’t dictate the model and message types for your app. Those are different for everyone.

But your app will have a specific model type and message type so make your type signatures more specific by using those.


#3

Thanks @Brian_Carroll for the clear explanation. Especially, the distinction between the use of type variables in libraries and the use of specific types in app code made it very clear.


#4

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.