Search code examples
typescommandmessageelmread-eval-print-loop

Inspecting variables in Elm


I'm trying to understand how Elm type system works. Coming from an imperative programming background, i'm used to take advantage of a debbuger with step and breakpoints to figure out what's happening. But apparently that is not the functionnal way.

Using the REPL to figure out trivial exemples is easy, but it gets way more complicated with something like Http.get for exemple.

Here is what i have tried :

Apparently I have to define a certain custom type for the message to be returned by the runtime when the HTTP request has been done :

> type Message = Done (Result Http.Error String)

Now I'm storing the result of the Http.get in some variable :

> test = Http.get { url = "http://192.168.1.10:3000/site", expect = Http.expectString Done }

Then i'd like to see what's inside test :

> test
<internals> : Cmd Message

If i understood correctly, test seems to have the expected type and is now some command i can somehow send to the Elm runtime.

Now here is what i want to do :

1 - print what's inside the Message payload of my test command.

2 - send the command to the runtime and fetch the result somehow (from the REPL)

3 - print what's inside the Message i'm supposed to get back from the runtime.

Is it possible ? Does it make sense ? If not, how am I supposed to take a look at whats happening behind the apparent magic in order to really understand Elm, especially the rather cryptic type annotations ?

Bonus question : Http.get has the following definition :

get :
    { url : String
    , expect : Expect msg
    }
    -> Cmd msg

Do msg have to be a custom type ?


Solution

  • Do msg have to be a custom type ?

    No. It can be any type, so for instance that could just as well be String for instance.

    Now to the meat of the question: In the REPL, you can evaluate Elm expressions - and Elm as such is a pure language, which means it cannot execute (almost) any effects, such as HTTP requests.

    However, there is a special kind of value in Elm called a Cmd, that if handed over to the Program value will ask the runtime to execute some effect on your behalf. For this to work, you must designate that Program value your application entry point by naming it main.

    This cannot be done in the REPL; hence the Elm REPL cannot execute Cmds.


    So in order to play with Cmd, you'll need to create an Elm application and run it in the browser.

    Something like this:

    import Browser 
    import Html
    
    
    main = Browser.element   
        { init = \() -> ({}, test) -- ask the runtime for your message
        , update = update
        , subscriptions = \_ -> Sub.none 
        , view = \_ -> Html.text "Hello world!"
        }
    
    update msg model =
        case msg of
            Done result ->
                let 
                    _ = Debug.log "HTTP request completed" result
    
                in
                ( model, Cmd.none )
    

    1 - print what's inside the Message payload of my test command.

    Your test command has no Message payload to print. The type parameter Cmd Message doesn't mean that the value somehow "contains" the Message type, it just asserts a certain kind of relationship between the types. In this case a future relationship, which you can think of as a "Command that in the future may produce a Message type of value".

    You could think of the Cmd type implemented something like this (it's not, but could be):

    type Cmd msg
        = NoCmd
        | HTTP Method Url (Response -> msg)
        | Sleep Duration (() -> msg)
        ...