One option is to use elem
, which checks whether a value is an element of a Foldable
container.
treeLook :: Eq a => a -> TernaryTree a -> Bool
treeLook = elem
Now you just have to write an instance of Foldable
. One option is to enable the DeriveFoldable
extension and just use deriving (Show, Foldable)
to get GHC to write the instance for you. But you won't learn much that way. So let's explore some ways to do it.
-- This import is needed for non-bleeding-edge GHC versions.
import Data.Monoid ((<>))
instance Foldable TernaryTree where
-- Fold over the tree in pre-order
foldMap _ EmptyTree = mempty
foldMap f (Node x ls cs rs) =
f x <> foldMap f ls <> foldMap f cs <> foldMap f rs
But that repeated foldMap
is itself a sort of "pattern" you can pull out by writing the "catamorphism" for trees:
cataTree :: r -> (a -> r -> r -> r -> r) -> TernaryTree a -> r
cataTree e _ EmptyTree = e
cataTree e f (Node x ls cs rs) =
f x (cataTree e f ls) (cataTree e f cs) (cataTree e f rs)
Now you can define foldMap
thus:
foldMap f = cataTree mempty (\a lr cr rr -> f a <> lr <> cr <> rr)
Now foldMap
itself has a more-powerful cousin, traverse
. So another option is to start there:
import Data.Traversable (fmapDefault, foldMapDefault)
instance Traversable TernaryTree where
-- Traverse the tree in pre-order
traverse f =
cataTree (pure EmptyTree)
(\a ls cs rs -> Node <$> f a <*> ls <*> cs <*> rs)
instance Functor TernaryTree where
fmap = fmapDefault
instance Foldable TernaryTree where
foldMap = foldMapDefault
Obviously, you're not at a point where you're going to want to do anything so fancy, but such techniques are actually useful once you're in the position of wanting to produce a bunch of functions quickly.