Search code examples
elm

Composite Types and Html onInput


A week ago I finally found a way to split up my messages in different categories (Here is the SO question where I got my answer)

Now I have implemented this solution:

type Msg 
    = AMsg AMsg
    | BMsg BMsg
    | CMsg CMsg

and then I defined AMsg as follows

type AMsg
    = ActionOne Int String
    | ActionTwo Int

Everything works great except when I use ActionOne with a onInput Html.Event

input [onInput (AMsg (ActionOne model.id))] []

This errors out telling me that onInput needs a String -> Msg type but is getting a AMsg type.

This would work just fine if I was using onClick for example and pass in the second argument myself

input [onClick (AMsg (ActionOne model.id "hello"))] []

But since I need to use onInput and that is what passes the second String parameter to ActionOne I am stuck. If I change my types to adapt to this

type Msg 
    = AMsg AMsg String
    | BMsg BMsg
    | CMsg CMsg

type AMsg
    = ActionOne Int
    | ActionTwo Int

This works, but then i force ActionTwo to also take a string, which i don't want.

Otherwise I am stuck specifying ActionOne directly under Msg

type Msg 
        = AMsg AMsg
        | BMsg BMsg
        | CMsg CMsg
        | ActionOne Int String

type AMsg
    = ActionTwo Int

I really care about separating Msgs into different categories -- if I want to use elm for a bigger project this seems pretty crucial to me. Any ideas ?


Solution

  • The function that you pass to onInput has to have the type String -> msg (or more specifically in your case, String -> Msg.)

    You can accomplish that by using a lambda:

    input [onInput (\str -> (AMsg (ActionOne model.id str)))] []
    

    You can also use function composition (the << function) to achieve the same thing without needing to talk about the parameter str explicitly:

    input [ onInput <| AMsg << ActionOne model.id ]