I am learning recursion schemes and it has proven very helpful to me to implement them specific to the list type. However, I got stuck on apomorphisms.
Here is an implementation of tails in terms of apo I recently found:
import Data.Functor.Foldable
tailsApo :: [a] -> [[a]]
tailsApo = apo coalgTails
where
coalgTails = \case
[] -> Cons [] (Left [])
li@(_:xs) -> Cons li (Right xs)
Unfortunately, I couldn't import Data.Functor.Foldable with GHCi, because I get a package not found error. Another search revealed this implemenation of apo specific to lists:
apoList :: ([b] -> Maybe (a, Either [b] [a])) -> [b] -> [a]
apoList f b = case f b of
Nothing -> []
Just (x, Left c) -> x : apoL f c
Just (x, Right e) -> x : e
Obviously, apoList's first argument doesn't match with tailsApo. I'd expext the type to be something like apoList :: ([b] -> Either (a, b) [a])) -> [b] -> [a].
There seems to be no more beginner friendly information on this subject. I appreciate any help.
Data.Functor.Foldableis provided by the recursion-schemes package. The type ofapothere is:Here,
tis the structure being generated by the unfold, andBase tis its base functor. Broadly speaking, the base functor represents one level of the recursive structure, the idea being that if we indefinitely nest it within itself we get a type equivalent to that of the whole structure -- in fact, that is precisely whatFixfromData.Functor.Foldabledoes. (On a meta note, there doesn't seem to be a Q&A here specifically aboutBasein recursion-schemes; it could be useful to have one.)Basefor lists is:So
apospecialises to:If we want to write it without using the recursion-scheme infrastructure, we can use the fact that
ListF a bis isomorphic toMaybe (a, b):In terms of
Maybe (a, b), the signature would then become:In the coalgebra (that is, the function argument to
apo),Nothing(orNil, in the recursion-schemes version) signal the generation of the list should be stopped by capping it with an empty tail. That is why you still needMaybe, even if you are also usingEitherto short-circuit the unfold in other ways.The implementation of this
apoListis pretty much like the one in your question, except that this signature doesn't restrict the seed (ofbtype) to a list, and flips the roles ofLeftandRight(so thatLeftsignals short-circuiting):