我一直在试图绕着自由的monad;作为学习辅助工具,我设法为以下Show
类型编写Free
个实例:
{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
-- Free monad datatype
data Free f a = Return a | Roll (f (Free f a))
instance Functor f => Monad (Free f) where
return = Return
Return a >>= f = f a
Roll ffa >>= f = Roll $ fmap (>>= f) ffa
-- Show instance for Free; requires FlexibleContexts and
-- UndecidableInstances
instance (Show (f (Free f a)), Show a) => Show (Free f a) where
show (Return x) = "Return (" ++ show x ++ ")"
show (Roll ffx) = "Roll (" ++ show ffx ++ ")"
-- Identity functor with Show instance
newtype Identity a = Id a deriving (Eq, Ord)
instance Show a => Show (Identity a) where
show (Id x) = "Id (" ++ show x ++ ")"
instance Functor (Identity) where
fmap f (Id x)= Id (f x)
-- Example computation in the Free monad
example1 :: Free Identity String
example1 = do x <- return "Hello"
y <- return "World"
return (x ++ " " ++ y)
使用UndecidableInstances
让我感到困扰;有没有办法没有它? Google产生的所有内容都是this blog post by Edward Kmett,这与我的Show
类定义基本相同。
答案 0 :(得分:11)
您实际上可以消除此处Show
的UndecidableInstance要求,但您不能对Read
或Eq
执行相同的操作。
诀窍是用你可以更直接展示的东西替换你的仿函数的内容,但是你没有告诉其他人。因此,我们将出口限制为:
{-# LANGUAGE FlexibleContexts #-}
module Free (Free(..)) where
并为我们只能show
的内容敲打数据类型。
newtype Showable = Showable (Int -> ShowS)
showable :: Show a => a -> Showable
showable a = Showable $ \d -> showsPrec d a
instance Show Showable where
showsPrec d (Showable f) = f d
现在,如果我们从不告诉任何人Showable
,Show (f Showable)
的唯一实例将是a
参数中的多态实例,最多约束一个Show实例。只要最终用户没有主动尝试使用其他扩展来破坏您的代码,这是合理的推理。添加功能依赖项和/或重叠/不可判定的实例,但只有破坏意图的事情,没有任何可能导致您崩溃的事情,可能会产生一些隐蔽。
通过这种方式,我们可以构建一个可判定的Show
实例。
data Free f a = Pure a | Free (f (Free f a))
instance (Functor f, Show (f Showable), Show a) => Show (Free f a) where
showsPrec d (Pure a) = showParen (d > 10) $ showString "Pure " . showsPrec 10 a
showsPrec d (Free as) = showParen (d > 10) $ showString "Free " . showsPrec 10 (fmap showable as)
这里给出的实现并没有消除对FlexibleContexts
的需要,但你也可以消除它 - 如果你真的觉得需要Haskell 98的兼容性 - 通过编写一些额外的类层。 / p>
我在几个包中使用这个技巧 - 包括我的ad
包 - 以减少对不可判定的实例的需求。