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
(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 -> aor
((t, Base t (t, a)) -> a) -> t -> aso 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))