为什么Either派生出Show但是Maybe不会?

时间:2015-01-07 22:10:35

标签: haskell show maybe either deriving

EitherMaybe的文档都表明它们的实例为Show

Either被定义为派生Show,简单地说:

data  Either a b  =  Left a | Right b
  deriving (Eq, Ord, Read, Show, Typeable)

然而,Maybe没有:

data  Maybe a  =  Nothing | Just a
  deriving (Eq, Ord)

由于它们是base的一部分并且非常相似,为什么不会Maybe直接派生Show

另一个问题也可能是,它在哪里获得Show实例?

2 个答案:

答案 0 :(得分:10)

Maybe的实例在GHC.Show中明确定义,以及一大堆其他常见类型(如元组)的实例。您可以使用:i中的ghci命令找出实例的定义位置:

Prelude> :i Maybe
data Maybe a = Nothing | Just a     -- Defined in ‘Data.Maybe’
instance Eq a => Eq (Maybe a) -- Defined in ‘Data.Maybe’
instance Monad Maybe -- Defined in ‘Data.Maybe’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’

我不知道为什么他们明确定义了实例或将其放在GHC.Show而不是Data.Maybe中 - 据我所知,它可以移到Data.Maybe和/或派生的。我的猜测是他们不希望Data.Maybe依赖GHC.Base之外的任何东西(就像现在一样),大概是因为它在某些其他核心模块中使用过。

答案 1 :(得分:4)

AFAIK元组在任何地方都没有定义,所以为了避免孤立实例[1],必须在GHC.Show [2]中定义元组的Show实例。这些实例的实现碰巧使用foldr1

show_tuple :: [ShowS] -> ShowS
show_tuple ss = showChar '('
              . foldr1 (\s r -> s . showChar ',' . r) ss
              . showChar ')'

所以GHC.Show导入了定义该函数的GHC.List。反过来,GHC.List定义lookup,它位于Maybe monad中(我猜想旧的Haskell 98的单态偏差)。所以GHC.List导入Data.Maybe。为了定义Show实例,Data.Maybe需要导入GHC.Show(直接或间接),这将使整个序列GHC.Show - > GHC.List - > Data.Maybe - > GHC.Show循环依赖。 GHC并不能很好地支持循环依赖(并不是说它们很容易支持!),所以基础很难避免它们。

[1]孤立实例是在与实例中涉及的类和类型不同的模块中定义的实例。形式上,Haskell要求实例搜索在任何模块中完成,直接或由正在编译的模块间接导入;但是对于非孤儿实例,GHC可能会短路,只能看两个地方。对于孤立实例,它必须跟踪模块中的每个孤立实例,然后跟踪每个导入它们的模块重新暴露这些实例,这更昂贵(并且意味着它必须保持具有潜在的许多实例的上下文环境)甚至与当前模块无关,因为它实际上并没有导入这些类或类型)。因此,良好的做法是避免孤立实例。

更哲学上,孤立实例是一种非常好的方法,可以在程序中获得同一类/类型的两个冲突实例,因为它们都是可见的'在Main模块中意味着它们会发生冲突。所以语言功能本身就是一种狡猾的。

[2] IIRC GHC仅提供Show个实例,最多包含一个(相对较小的)固定数量的元组组件,这些组件不符合Haskell 98的要求但是足够好适合任何实际的编程需求。 (说真的,不要使用超过3个元素的元组,你忘记具体组件的含义)。我不知道该标准是否已更新,以便在过去几年内使GHC符合要求。