Let’s unpack that error message:
You’ve told the compiler that myRec
has type Record r2
(which is what {| r2 }
is sugar for). r2
is a quantification variable, so the compiler doesn’t know what concrete type it stands for. So it makes up a new type that it doesn’t know anything about and calls it r21
(it’s just tacking a 1
onto the name you’ve given the variable).
The compiler knows that, in any x.name
expression, it needs to match the type of x
against Record (name :: a | b)
for some arbitrary a
and b
. So it’s trying to unify those two types: Record r21
and Record (name :: a | b)
will unify if r21
unifies with (name :: a | b)
. But the compiler explicitly doesn’t know anything about r21
, so this unification fails, and you get the above error (with a
and b
having been partially solved by other unifications, in this case).
Now, you’ve told the compiler something about r2
—namely, that it appears in a Union
or Nub
constraint. Unfortunately, while this is enough for a human to work out that there should be a name
entry in that row, the compiler doesn’t work backwards through those constraints to figure this out. This has been requested as a feature in Union constraint to add label to row doesn't work · Issue #3242 · purescript/purescript · GitHub, but I don’t think anyone currently has plans to implement it.
Instead, what you can do is replace r2
with something that has the information the compiler is asking for:
myFn3 :: forall r1 r2. Union (Named r1) (other :: Int) (Named r2) => Proxy r1 -> {|Named r2} -> String
myFn3 _ myRec = myRec.name
Now the compiler is still in the dark about what r2
is inside myFn3
, but as long as the caller of myFn3
provides enough context for r1
and r2
to be determined, and if those types allow the Union
constraint to be solved, everything will check.