Compiler: Cannot use qualified modules as source for record update

Created on 26 Jun 2019  ·  5Comments  ·  Source: elm/compiler

Quick Summary: Cannot update a record referenced in a qualified module, as the module name is capitalised and it is expecting lowercase.

SSCCE

-- Bar.elm
module Bar exposing (..)

record =
    { x = 1, y = 2 }
-- Foo.elm
module Foo exposing (..)

import Bar

newRecord =
    { Bar.record | x = 3 }

Trying to use the qualified module Bar as the source record, results in the following error (due to the capitalisation):

-- PARSE ERROR ---------------------------------------- Foo.elm

Something went wrong while parsing a record in record's definition.

6|     { Bar.record | x = 3 }
         ^
I was expecting:

  - a right curly brace, to end a record
  - a lower-case variable, like `x` or `user`
  • Elm: 0.19.0
  • Operating System: macOS 10.13.2

Additional Details

The workaround is to simply use an open import, so you can access the record using the function/variable name only.

module Foo exposing (..)

import Bar exposing (record)

newRecord =
    { record | x = 3 }
request

Most helpful comment

Maybe I'm just missing something, but this doesn't make sense to me: "... but with { x.y | z = 4 } it seems clear that it should update and return the x record overall, not just the record in the y field."

Like the above commenter, I'm confused why it would act any differently than the f x y case. Why does it "seem clear" that it should be any different? In general, wouldn't you just evaluate whatever is inside the { ... | to a record and then make the changes to the right of the | to that record?

Edit: Oh, I get it now. You are saying that it should actually return the top-level x record, but with the y field changed by substituting 4 for z. Interesting. In that case I still agree with the commenter above, but I can see where Evan is coming from. Being able to make nested changes without doing { x | y = { x.y | z = 4 } } would be very convenient.

All 5 comments

This applies to records inside records as well, wherein the workaround I usually do is naming it in a let...in. But I agree it would be convenient to allow this. Maybe there is a reason they haven't though.

Tracking in https://github.com/elm/compiler/issues/1375

There are a lot of suggestions of how to make record update syntax more convenient. Another suggestion is to allow expressions to the left of the |, but it creates complications. It seems clear that { f x y | z = 4 } should update and return the record produced by f x y, but with { x.y | z = 4 } it seems clear that it should update and return the x record overall, not just the record in the y field.

So while any particular suggestion is quite reasonable, it comes with complex implications for a bunch of other possible situations. It would be best to figure out any record changes all at one time, to minimize the cost to people using the language. That is why we are tracking this suggestion in #1375 until the next time we take a very hard look at the record syntax.

Coming from JS, I’d expect { x.y | z = 4 } to work as { ...x.y, z: 4 } – in other words, no different from the f x y case. If I wanted to update y of x, I’d try { x | y = { x.y | z = 4 } } / { ...x, y: { ...x.y, z: 4 } }.

Maybe I'm just missing something, but this doesn't make sense to me: "... but with { x.y | z = 4 } it seems clear that it should update and return the x record overall, not just the record in the y field."

Like the above commenter, I'm confused why it would act any differently than the f x y case. Why does it "seem clear" that it should be any different? In general, wouldn't you just evaluate whatever is inside the { ... | to a record and then make the changes to the right of the | to that record?

Edit: Oh, I get it now. You are saying that it should actually return the top-level x record, but with the y field changed by substituting 4 for z. Interesting. In that case I still agree with the commenter above, but I can see where Evan is coming from. Being able to make nested changes without doing { x | y = { x.y | z = 4 } } would be very convenient.

If there’s gonna be some shorthand for updating a nested field I think there needs to be some clever syntax to it in order to pass the “can I move this into a variable?” test. I’d be surprised if the following refactor completely changed what the code does:

{ x.y | z = 4 }

⬇️

let
    temp = x.y
in
{ temp | z = 4 }
Was this page helpful?
0 / 5 - 0 ratings