Search code examples
httpelm

Elm http 2.0.0 functions expect constructor function


For the http request functions, both the Elm tutorial and the docs suggest passing a constructor function (e.g. GotText) to an expect function (e.g. expectString), for the expect field, e.g:

type Msg
  = GotText (Result Http.Error String)

getPublicOpinion : Cmd Msg
getPublicOpinion =
  Http.get
    { url = "https://elm-lang.org/assets/public-opinion.txt"
    , expect = Http.expectString GotText
    }

I understand this, but it seems to me that constraining the API to require a constructor function (e.g. GotText) is overly restrictive.


For example, it is possible to use identity to extract the constructor function GotText from the request function get:

getPublicOpinion = Cmd.map GotText (
  Http.get
    { url = "https://elm-lang.org/assets/public-opinion.txt"
    , expect = Http.expectString identity
    })

But that begs the question: Why does the http API require the constructor function at all*?

* Or at least allow us to omit the expect field and return Result Http.Error String.


Solution

  • This isn't a restriction, it's actually a convenience.

    If Http.expectString didn't take a function (Result Http.Error String -> msg) then Http.get would return a Cmd (Result Http.Error String) which is what it does if you pass in identity.

    Since the result of all Cmds always needs to be a Msg that the runtime can pass in to your update function you would always have to Cmd.map the result of every call Http.get to convert the Cmd (Result Http.Error String) to a Cmd Msg.

    To avoid having to call Cmd.map every time you call Http.get the API allows you to pass the function that will do the conversion straight to Http.expectString. This is less typing, involves less nesting so it's clearer to the reader.

    You'll see this convention repeated across lots of modules. eg.:

    • Json.Encode.list has the type list : (a -> Value) -> List a -> Value it takes a function to do the JSON encoding of the elements of the list, this saves you from having to use List.map to JSON encode the element of the list first.

    • Html.Events.onInput has the type onInput : (String -> msg) -> Attribute msg it takes a function to convert the String to a msg value, this saves you from having to Html.Attribute.map the result of onInput to convert the Attribute String to a Attribute msg. This would be a really pain if you had to call Html.Attribute.map for every event handler and Attribute on any Html elements.