Calling HTTP.Post from submodule

I am having the same issue as outlined in this SO question. https://stackoverflow.com/questions/60513194/how-to-avoid-expect-msg-with-http-2-0-0-in-elm

I am trying to make a HTTP Post from an authentication submodule and not sure how to resolve the Msg vs Auth.Msg.
I don’t get the suggestion of Why don't you just pass an a -> msg function to your Api functions?
Can someone provide a more detailed response?

Here is an example for the solution given in StackOverflow

myApi: 
  (a->msg) 
  -> { method : String
     , headers : List Header
     , url : String
     , body : Body
     , expect : Expect a
     , timeout : Maybe Float
     , withCredentials : Maybe String
  }
  -> Cmd msg
myApi msgMapper {method, headers,url,body,expect,timeout,withCredentials} =
  request
    { method = method
    , headers = headers
    , url = url
    , body = body
    , expect = expect
    , timeout = timeout
    , withCredentials = withCredentials
    }
    |> Cmd.map msgMapper

The point is to avoid using Cmd.map, right?

What we want is Api.elm knows how to get a Thing from a server, but doesn’t want to deal with what to do with the response.

And Main.elm, and other clients of Api.elm, want to get a Thing from a remote server so they can do awesome things with it, but they don’t want to deal with how to get it.

So Api.elm can be lazy about doing things with Things:

module Api exposing (getSomething)

{-| I'll get a Thing for you, just tell me what you want done with it. 
For example, I could put it into some kind of msg for you, then I'll give 
you a Cmd you can invoke to get *that kind of msg* eventually.
-}
getThing : (Result Http.Error Thing -> msg) -> Cmd msg  
getThing constructor =
    Http.request 
        { method : "GET"
        , headers : []
        , url : "http://thingServer.someplace.com/"
        , body : Http.emptyBody
        , expect : Http.expectJson constructor thingDecoder
        , timeout : Nothing
        , tracker : Nothing
        }

And Main.elm is lazy about how to get things:

{-| When I init, the Api is going to do some work for me, and then 
I'll get a GotThing message that hopefully has a Thing in it!
-}
main : Program () MyAwesomeModel MyMainMessageType
main = 
    Browser.element 
        { init : () -> ( init, Api.getThing GotThing ) 
        , view : view
        , update : update
        , subscriptions : always Sub.none
        }

type MyMainMessageType
    = GotThing (Result Http.Error Thing)

update : 
    MyMainMessageType 
    -> MyAwesomeModel  
    -> ( MyAwesomeModel, Cmd MyMainMessageType )
update msg model = 
    case msg of
        GotThing (Ok thing) ->
            Thing.doAllTheThings thing

        GotThing (Err error) ->
            dealWith error

When they said suggested passing ( a -> msg), that translated to my example as passing the type constructor GotThing in Api.getThing GotThing. Note that GotThing by itself has the type ( Result Http.Error Thing -> MyMainMessageType), in other words its a constructor for values of MyMainMessageType. And that Api.getThing takes as its first argument a ( Result Http.Error Thing -> msg), or in other words, it just wants a message constructor for some type of msg so that it can cram the Thing it gets somewhere. It returns a Cmd that could produce that type of msg eventually.

3 Likes

Thanks Jeremy. That does clarify things for me and I was able to get my code to work. :call_me_hand:

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