Syntax, why must the first arg of a record update be a variable?

Why cannot session be inlined in the following sample.

        let session = getSession model in
        let session2 = { session | loginToken = Nothing, accessToken = Nothing } in

but not

         let session2 = { (getSession model) | loginToken = Nothing, accessToken = Nothing } in

This makes code unneccesary verbose, since it should be formatted like this:

        let
            session =
                getSession model
        in
        let
            session2 =
                { session | loginToken = Nothing, accessToken = Nothing }
        in
1 Like

Parallel to your question, but know that you can have many bindings in one let:

        let
            session =
                getSession model

            session2 =
                { session | loginToken = Nothing, accessToken = Nothing }
        in

And you can extract parts to functions (like getSession) if it makes sense:

logout : Session -> Session
logout session =
    { session | loginToken = Nothing, accessToken = Nothing }

getSession model |> logout

That way it also gets less verbose, if it is what you need.

A while ago (ie. before 0.19) I modified the compiler to allow for a generic expression for the record part of a record update { <expression> | update fields }. The change was rejected based on there being no good case made for that improving the language. I had a very brief search around for code that would be improved because of this change, and I had to admit that I couldn’t really find any “in the wild” code that was “obviously improved” by this change.

Thank you for the explanation!

My use case is that I have a SPA with many pages, and the model has a generic part Session shared by all these. I have different definitions of getSession in the pages in and general code in Main. In Main, it has to handle all pages to find the session, and inside each page, it is just the same as “.session”. It is a way to be able to program the same in both places, although the context is different.

It is such random limitation, so I am a bit surprised, and I do not see how it improves readability of code.

When I first encountered this limitation, I am mostly an ocaml / F# programmer, I didn’t even understand that the is “by definition”. So, I think one solution would be to improve the compiler error message and clearly say it must be a single variable name between the { and | . The current message says this

I am partway through parsing a record, but I got stuck here:

309|                     setSession model { getSession model | frms = Session.Requesting "" }
                                                       ^
I just saw a field name, so I was expecting to see an equals sign next. So try
putting an = sign here?

Note: If you are trying to define a record across multiple lines, I recommend
using this format:

    { name = "Alice"
    , age = 42
    , height = 1.75
    }

While I can see the point for not allowing arbitrary expressions (though I don’t necessarily agree), I wish that at least qualified names were allowed. I.e. right know, you have to write

let
    packageRecord = Package.record
in
    { packageRecord | field = newValue }

and I wish we could shorten that to

{ Package.record | field = newValue }

I would argue that in this case the second version is more readable and I think this would be a reasonable tradeoff in syntax complexity vs. usefulness. For example, this would make it a lot more pleasant to provide a record with default options when the user of a package is only expected to change a few of them most of the time.

6 Likes

I second this point by @eike . I regularly hit this when using the Markdown.defaultOptions value from elm-explorations/markdown.

2 Likes

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