Merge record with Extensible record

row-typing
records
#1

Hi everyone!

Couldn’t find a question around this so hope this isn’ta duplicate.

I’d like to be able to:

mergeRecord :: forall r. {a :: String | r} ->  {a :: String, b :: String | r}
mergeRecord x = Record.merge { b: "hi" } x

Is this possible? I keep getting:

No type class instance was found for

Prim.Row.Nub ( a :: String
             , b :: String
             | r4
             )
             ( a :: String
             , b :: String
             | r4
             )

I thought maybe adding

Prim.Row.Nub () r =>

would help but obviously didn’t. Was this at all in the right direction?

Thanks for the help and pardon the noobness :grimacing:

  • Charles
#2

merge maintains the invariant that there are no duplicate labels in the row via Nub. With the signature as-is, there’s no way for it to know that r does not also contain b itself. As an example, lets consider what would happen if there was no Nubing (which you can get by replacing merge with union):

mergeRecord { a: "hello", b: 42 } -- b is Int

This would yield the result type of

{ a :: String, b :: String, b :: Int }

Since the input record already contains a b. However, what you actually want is for it to remove the existing b, and replace it with yours. Nub is a constraint that will do that, it only keeps the first occurrence of a label. But since r is a forall quantified variable, you will need to propagate the Nub constraint so it can compute the nubbed row.

Considering your type signature has an a label in it, I assume this is a reduction and your real code actually needs to assume the presence of that. You probably want something like:

mergeRecord ::
  forall rin rout.
  Nub (a :: String, b :: String | rin) rout =>
  { a :: String | rin } ->
  { | rout }
mergeRecord x = Record.merge { b: "hi" <> x.a } x

This separates the input and output arguments, otherwise the constraint will restrict you to passing in only records that do not contain b. If that is what you want, I suggest using disjointUnion instead.

7 Likes
#3

Nate! Dude, you rock. Thanks!

1 Like
#4

You’re right that I was looking for disjointUnion.

That’s great to get some clarification around that; appreciate the help. Cheers mate