How can I walk this type with a recursion scheme instead of explicit recursion?
I found a solution that I'm reasonably happy with: an apomorphism.
makeReplacements replacements = apo coalg
where
coalg :: MyStructure -> MyStructureF (Either MyStructure MyStructure)
coalg structure = case lookup structure replacements of
Just replacement -> Left <$> project replacement
Nothing -> Right <$> project structure
Having thought about this a little more, I also saw a symmetry in this that leads to an equivalent paramorphism:
makeReplacements replacements = para alg
where
alg :: MyStructureF (MyStructure, MyStructure) -> MyStructure
alg structure = case lookup (embed $ fst <$> structure) replacements of
Just replacement -> replacement
Nothing -> embed $ snd <$> structure
Following up from the discussion under your question
para
is(Base t (t, a) -> a) -> t -> a
. To me, this looks close but not quite perfect. Wouldn't I actually want((t, Base t a) -> a) -> t -> a
or((t, Base t (t, a)) -> a) -> t -> a
so that I can look at the element I'm on?
That's still a paramorphism. The type of para
looks weird but it is the more precise one. A pair (t, Base t a)
does not encode the invariant that both components are always going to have the "same" constructor.
What you propose still seems like the most natural way of defining makeReplacements
, it's just not defined in the recursion-schemes library.
para' :: Recursive t => (t -> Base t a -> a) -> t -> a
para' alg = go where
go x = alg x (fmap go (project x))