可以将一个自由monad转换为任何其他monad,但是给定一个类型的值Free f x
,我想打印整个树,而不是将生成的AST的每个节点映射到另一个monad中的某个其他节点.
Gabriel Gonzales 直接使用该值
showProgram :: (Show a, Show r) => Free (Toy a) r -> String showProgram (Free (Output a x)) = "output " ++ show a ++ "\n" ++ showProgram x showProgram (Free (Bell x)) = "bell\n" ++ showProgram x showProgram (Free Done) = "done\n" showProgram (Pure r) = "return " ++ show r ++ "\n"
可以抽象为
showF :: (x -> b) -> ((Free f x -> b) -> f (Free f x) -> b) -> Free f x -> b showF backLiftValue backLiftF = fix (showFU backLiftValue backLiftF) where showFU :: (x -> b) -> ((Free f x -> b) -> f (Free f x) -> b) -> (Free f x -> b) -> Free f x -> b showFU backLiftValue backLiftF next = go . runIdentity . runFreeT where go (FreeF c ) = backLiftF next c go (Pure x) = backLiftValue x
如果我们有像(Choice x = Choice x x
作为仿函数一样)的多态函数,这很容易调用
showChoice :: forall x. (x -> String) -> Choice x -> String showChoice show (Choice a b) = "Choice (" ++ show a ++ "," ++ show b ++ ")"
但是,这似乎很复杂,简单的操作...什么其他方法也有从去f x -> b
到Free f x -> b
?
使用iter
和fmap
:
{-# LANGUAGE DeriveFunctor #-} import Control.Monad.Free data Choice x = Choice x x deriving (Functor) -- iter :: Functor f => (f a -> a) -> Free f a -> a -- iter _ (Pure a) = a -- iter phi (Free m) = phi (iter phi <$> m) showFreeChoice :: Show a => Free Choice a -> String showFreeChoice = iter (\(Choice l r) -> "(Choice " ++ l ++ " " ++ r ++ ")") . fmap (\a -> "(Pure " ++ show a ++ ")")
fmap
转换Free f a
为Free f b
,并iter
完成其余的工作.您可以将其考虑在内,并且可能会获得更好的性能:
iter' :: Functor f => (f b -> b) -> (a -> b) -> Free f a -> b iter' f g = go where go (Pure a) = g a go (Free fa) = f (go <$> fa)