Reputation: 311
I'm trying to recursively add to a binary tree in Haskell. I'm following Learn You A Haskell on this, only with a few changes, but I'm getting errors, which I do not understand:
data Male = Male { maleName :: String
, maleDOB :: Int
} deriving (Show, Read, Eq, Ord)
data Female = Female { femaleName :: String
, femaleDOB :: Int
} deriving (Show, Read, Eq, Ord)
data FamilyTree a = EmptyTree
| Node a (FamilyTree Female) (FamilyTree Male)
deriving (Show, Read, Eq, Ord)
singleton :: a -> FamilyTree a
singleton x = Node x EmptyTree EmptyTree
treeInsert :: (Ord a) => a -> FamilyTree a -> FamilyTree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
Here is the error message I'm getting:
Couldn't match type `Female' with `Male'
Expected type: Male
Actual type: a
In the first argument of `treeInsert', namely `x'
In the third argument of `Node', namely `(treeInsert x right)'
In the expression: Node a left (treeInsert x right)
I'm quite new to Haskell and can't wrap my head around what is happening here. Any pointer in the right direction would be welcome!
Upvotes: 3
Views: 243
Reputation: 12715
When you write
treeInsert :: Ord a => a -> FamilyTree a -> FamilyTree a
the type system ensures that the type of the first argument equals the index of the second. It means that you can insert a Male
only in a tree that starts from a Male
. I guess, this is not what you want.
However it's a nice question and I'll answer it. The problem in
treeInsert :: Ord a => a -> FamilyTree a -> FamilyTree a
is that a
is far to general. What would
treeInsert :: Int -> FamilyTree Int -> FamilyTree Int
mean? You need to restrict a
to be either Female
or Male
. That's a job for GADTs
:
{-# LANGUAGE GADTs #-}
data Person a where
PFemale :: Female -> Person Female
PMale :: Male -> Person Male
Person
contains either Female
or Male
and carries at the type level information about which one. Having this we can define
runPerson :: Person a -> a
runPerson (PFemale x) = x
runPerson (PMale x) = x
treeInsert :: Person a -> FamilyTree a -> FamilyTree a
treeInsert p EmptyTree = singleton (runPerson p)
treeInsert p@(PFemale x) (Node a left right)
| x == a = Node x left right
| otherwise = treeInsert p left
treeInsert p@(PMale x) (Node a left right)
| x == a = Node x left right
| otherwise = treeInsert p right
The trick is that when you pattern match on Person a
a
gets instantiated either to Female
or Male
and never to something else. When a
is Female
, you continue to insert into the "Female
" subtree, otherwise — into the "Male
".
Upvotes: 2