Why does this use of a function work when declared separately, but not as part of other function?


Why does implementation A work fine, but implementation B gives me “types do not unify”?

-- a function which operates on records with a field 'common'
sd :: forall a. { common :: Common | a} -> { common :: Common | a} 
sd r = set _dispSelf Nothing r

-- this takes a function for altering records with 'Common' and an Obj.
clearObjDisplay :: forall a. ({ common :: Common | a} -> 
                   { common :: Common | a }) -> Obj -> Obj

-- Obj has several constructors. 
-- Every one has a record type as data which 
-- includes a field of type Common, so they should all match
-- 'forall a. { common :: Common }'. The exact record type is different
-- for each object, however.

-- implementation A. Calling the function 'sd' defined
-- above to alter the record.
clearObjDisplay g (ObjText r) = ObjText $ sd r 
clearObjDisplay g (ObjImg  r) = ObjImg  $ sd r

-- implementation B. Using the function passed as argument 'g'
clearObjDisplay g (ObjText r) = ObjText $ g r 
clearObjDisplay g (ObjImg  r) = ObjImg  $ g r



In implementation B, the a defined in clearObjDisplay's type signature is specialized to the first Obj it encounters, so when you pass it a different Obj it won’t match. Moving your forall to inside the parens for your sd fn should get you what you want. Try changing your type signature to:

clearObjDisplay :: (forall a. { common :: Common | a } -> { common :: Common | a }) -> Obj -> Obj

Here’s a great video explaining this: https://www.youtube.com/watch?v=k0cZe0LVFI4


Thanks, and great video link! I’ve been wondering how I could learn to understand polymorphism more precisely, rather than “winging it” and hacking type signatures until they work. I will watch this!