如何为Mu递归类型编写Show实例

时间:2018-06-20 17:22:22

标签: haskell show functor

由于previous question,我知道如何为特定的Mu递归类型(如Mu NatFMu (ListF a))编写专门的实例。现在,我想编写一个通用实例,该实例适用于应用于Mu的所有函子。

基本上我想做的是填补以下Haskell摘录中名为<???>的空白:

newtype Mu f = Mu (forall a. (f a -> a) -> a)

instance (Functor f, Show1 f) => Show (Mu f) where
    showsPrec d (Mu f) = <???>

这怎么实现?

编辑

想象一下我想使用Mu定义自然数。像这样:

data NatF r = Zero | Succ r

instance Functor NatF where
  fmap f Zero = Zero
  fmap f (Succ r) = Succ (f r)

type NatMu = Mu NatF

现在,我想显示类型为NatMu的值,所以我这样做:

instance Show (Mu NatF) where
    show (Mu f) = f alg where
        alg Zero = "Zero"
        alg (Succ n) = "(Succ " ++ n ++ ")"

一切正常,每个人都很高兴,但是...如果我想定义data NatNu = Nu NatFdata ListMu a = Mu (ListF a)怎么办?我将需要为每个变体创建一个几乎相同的实例。为什么不对其进行抽象处理,以使我的定义仅随着函子和递归方案的数量线性增长,而不是平方增长?

我不知道该怎么做。我只是从大量的汇编+ C背景(嵌入式编程和数字代码)中学习Haskell的。我的大脑捷径包括这些抽象,类型,提升...,我试图从头开始理解它实现了大多数基本概念,但是我可以解决这个特殊问题。

EDIT2

我设法写了一些有用的东西,但对我来说看起来很奇怪。而且,它需要一些丑陋的扩展名,例如UndecidableInstances

instance Show (NatF String) where
    show Zero = "Zero"
    show (Succ n) = "(Succ " ++ n ++ ")"

instance (Functor f, Show (f String)) => Show (Mu f) where
    show (Mu f) = f $ show

是否有更好的方法(不需要花哨的Haskell扩展)?

1 个答案:

答案 0 :(得分:2)

首先,让我重写您的Mu NatF实例以定义showsPrec而不是show,这通常是首选的方式(稍后会变得很重要)。

instance Show (Mu NatF) where
  showsPrec p (Mu f) = showParen (p>9) $ f alg where
     alg :: NatF ShowS -> ShowS
     alg Zero = ("Zero"++)
     alg (Succ n) = ("(Succ "++) . n . (")"++)

这里什么都没改变;我基本上只是将++串联运算符替换为差异列表的组合-串联。 (之所以会优先使用这些选项,主要是因为其左关联列表级联的烦人的 O n 2 )复杂性;这有点历史性尴尬。)

现在,如果要切换到其他函子,则唯一需要更改的就是alg函数。您需要通用的

alg :: Show1 f => f ShowS -> ShowS

好吧,这几乎是 the function which the Show1 class is about

showsPrec1 :: (Show1 f, Show a) => Int -> f a -> ShowS

(这基本上与show1 :: (Show1 f, Show a) => f a -> String等效,尽管库中没有提供。)

您只需要代替{em> showing a参数,而只需逐字粘贴到ShowS中即可。我会这样做:

newtype UnquotedString = UnquotedString {getUnquotedString :: ShowS}

instance Show UnquotedString where
  showsPrec _ (UnquotedString s) = s

我们总是想要括号;这可以通过始终为Priority参数设置10来实现。

alg = showsPrec1 10 . fmap UnquotedString

IMO,使用Show1的整个方法相当尴尬。这是一个令人讨厌的实例化类,与您可以简单派生的Show有很大不同。因此,实际上,我发现使用(Functor f, Show (f String))更整洁的方法。不必太在意-XUndecidableInstances –这是必需的,因为您以某种方式“重新调用”该类,这样编译器在进行类型检查时不会最终陷入循环,但这并不是一个大问题

尽管如此,您仍然需要避免使用多余的引号。