For a library I’m working on, I’m trying to create two operators on records. Given two records, r1 .|. r2 should return a record with the union of all fields, and r1 .&. r2 should return a record with the intersection of them (left biased). For example:
Using disjointUnion from the record library, creating <&> is doable. However, I’m struggling with the intersection. Does anybody know if such a thing is even possible?
Below the code I have till now:
module Rows where
import Prim.Row (class Nub, class Union)
import Record (disjointUnion)
-- Union --
class (Union a b c, Nub c c) <= DisjointUnion a b c
instance disjointUnion :: (Union a b c, Nub c c) => DisjointUnion a b c
infixl 5 or as .|.
or :: forall a b c. DisjointUnion a b c => Record a -> Record b -> Record c
or r1 r2 = disjointUnion r1 r2
-- Intersection --
class Intersection (a :: # Type) (b :: # Type) (c :: # Type)
infixl 4 and as .&.
and :: forall a b c. Intersection a b c => Record a -> Record b -> Record c
and r1 r2 = ?how_to_do_this
It’s probably not too hard to implement, but I’d prefer not to. I think we ought to start getting more conservative about adding new compiler-solved classes, especially if what they achieve is already possible in userland. I think all the same reasons for not having Prelude built in to the language apply: in particular, after a new compiler-solved class has been added, we can’t change it without it being a breaking change to the language.
The ones that are built in right now exhibit much better inference and performance. RowList is a compile-time performance drain, while Union and friends are implemented in Haskell. It’s essentially the difference of the type-checker interpreting a program vs it being native code.
The answer is to implement it in PureScript if it’s possible, and if after using it for a while – and there’s demand – propose a new compiler solved class. For example, we originally implemented Lacks in terms of several Union constraints, and it was pretty terrible in practice. Lacks is frequently needed and the original implementation led to enough confusion that we added compiler support.