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

Lui*_*las 5 monads haskell

我一直在试图绕着自由的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)
Run Code Online (Sandbox Code Playgroud)

使用UndecidableInstances困扰我一些; 有没有办法没有它?所有Google收益都是由Edward Kmett撰写的这篇博文,其安慰性Show与我的基本相同.

Edw*_*ETT 11

你实际上可以Show这里消除UndecidableInstance的要求,尽管你不能为Read或做同样的事情Eq.

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

{-# LANGUAGE FlexibleContexts #-}

module Free (Free(..)) where
Run Code Online (Sandbox Code Playgroud)

为我们只能做的事情敲出数据类型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
Run Code Online (Sandbox Code Playgroud)

现在,如果我们从不告诉任何人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)
Run Code Online (Sandbox Code Playgroud)

这里给出的实现并没有消除对它的需要FlexibleContexts,但你可以消除它 - 如果你真的觉得需要Haskell 98兼容性 - 通过编写一些额外的类层.

我在几个包中使用这个技巧 - 包括我的ad包 - 以减少对不可判定实例的需求.