[Haskell-cafe] Trouble understanding records and existential types

Brian Hulley brianh at metamilk.com
Fri Jan 26 00:35:06 EST 2007

Chris Kuklewicz wrote:
> This is how I would write getLeaves, based on your GADT:
>> data IsLeaf
>> data IsBranch
>> newtype Node = Node { getNode :: (forall c. ANode c) }
>> data ANode :: * -> * where
>>     Branch :: String -> String -> (ANode a,ANode b) -> [Node] ->
>> ANode IsBranch
>>     Leaf :: String -> String -> ANode IsLeaf
>> getLeaves :: ANode a -> [ANode IsLeaf]
>> getLeaves (Branch _ _ (l1,l2) rest) = getLeaves l1 ++ getLeaves l2
>>         ++ concatMap (getLeaves.getNode) rest
>> getLeaves x@(Leaf {}) = [x]

Thanks Chris - that's really neat!
I see it's the explicit wrapping and unwrapping of the existential that 
solves the typechecking problem, and the use of newtype ensures there's no 
run-time penalty for this.
Also the wrapping of the existential allowed higher order functions to be 
used making the code much neater.
Regarding the question of why in the original example the typechecker was 
trying to match (forall b.ANode b) against (ANode a) and not (ANode IsLeaf), 
I think the answer is probably that the typechecker first finds the MGU of 
the types occupying the same position in all the left hand sides first, then 
it tries to match this against the declared type at that position, whereas 
for the original example to have typechecked it would have to treat each 
equation separately. Anyway it's now an irrelevant point given the clarity 
of your solution which compiles fine,

Best regards, Brian.

More information about the Haskell-Cafe mailing list