我可以在此Show实例中为免费Monad消除UndecidableInstances的使用吗?

时间:2012-06-04 00:57:20

标签: haskell monads

我一直在试图绕着自由的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类定义基本相同。

1 个答案:

答案 0 :(得分:11)

您实际上可以消除此处Show的UndecidableInstance要求,但您不能对ReadEq执行相同的操作。

诀窍是用你可以更直接展示的东西替换你的仿函数的内容,但是你没有告诉其他人。因此,我们将出口限制为:

{-# 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

现在,如果我们从不告诉任何人ShowableShow (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包 - 以减少对不可判定的实例的需求。