Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 具有嵌套参数的类型的实例_Haskell - Fatal编程技术网

Haskell 具有嵌套参数的类型的实例

Haskell 具有嵌套参数的类型的实例,haskell,Haskell,在这个例子中,我可以推导出Eq: data A f t = A (f t) deriving (Eq) data B f t = B (f (f t)) deriving (Eq) 但在这个例子中: data A f t = A (f t) deriving (Eq) data B f t = B (f (f t)) deriving (Eq) 我得到这个错误: > No instance for (Eq (f (f t))) > arising from the fir

在这个例子中,我可以推导出Eq:

data A f t = A (f t) deriving (Eq)
data B f t = B (f (f t)) deriving (Eq)
但在这个例子中:

data A f t = A (f t) deriving (Eq)
data B f t = B (f (f t)) deriving (Eq)
我得到这个错误:

> No instance for (Eq (f (f t)))
>   arising from the first field of ‘B’ (type ‘f (f t)’)
> Possible fix:
>   use a standalone 'deriving instance' declaration,
>     so you can specify the instance context yourself
这项工作:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
...
data B f t = B (f (f t))
deriving instance (Eq (f (f t)), Eq (f t)) => Eq (B f t)
但是我已经读到使用
不可判定实例
可能是个坏主意,我不确定什么时候可以,什么时候不行

我试过这个,效果很好:

data B f t = B (f (f t))

instance (Eq1 f, Eq t) => (Eq (B f t)) where
  (B x) == (B y) = eq1 x y
但是我还想让
B
成为
NFData
Read
Show
的实例,我不想编写
Read
Show
NFData
实例以及
Eq1
Show1
NFData1
实例和
NFData1
的类

我有3个问题:

  • 为什么第一个示例编译而第二个示例不编译
  • 现在是使用不可判定实例的时候吗
  • 有没有什么方法可以不写所有这些实例就做我想做的事情

  • 这个确切的例子出现在GHC手册的章节中。如文件所述,GHC在推断用于派生实例的上下文时采取保守立场,要求推断上下文中的每个约束必须仅包含类型变量,且不重复。由于
    (f(f t))
    具有重复类型变量
    f
    ,因此它被拒绝。请注意,以下内容将被接受:

    data C f g t = C (f (g t)) deriving (Eq)
    
    手册中给出的解决方案是使用独立的派生子句,正如您所做的那样。正如评论中指出的,以下内容就足够了:

    deriving instance Eq (f (f t)) => Eq (B f t)
    
    但是,由于类型变量
    f
    出现在约束中的频率比出现在“实例头”
    Eq(B f t)
    中的频率更高,因此它违反了(特别是第一个Paterson条件)中记录的规则,这保证了类型检查器不会循环。因为这些规则是足够的,但不是必需的,所以有许多实例声明即使违反了规则也可以正常工作。启用
    不可判定实例
    允许它们

    没有特别的理由不启用
    不可判定实例
    。在最坏的情况下,您将创建一个导致类型检查器循环的实例,编译将失败,并出现一个错误,建议您使用
    -fredectiondepth={n}
    (如果您有一个真正的循环,这将不会有帮助)。请注意,这只是编译时问题。如果代码是使用
    不可判定实例编译的
    ,您就不必担心将来会出现运行时危险

    请注意,我不需要启用
    FlexibleContexts
    。以下工作很好:

    {-# LANGUAGE StandaloneDeriving #-}
    {-# LANGUAGE UndecidableInstances #-}
    
    data B f t = B (f (f t))
    deriving instance Eq (f (f t)) => Eq (B f t)
    deriving instance Read (f (f t)) => Read (B f t)
    deriving instance Show (f (f t)) => Show (B f t)
    
    对于手动派生的实例,以下操作似乎有效,不需要
    NFData1
    或类似操作:

    instance NFData (f (f t)) => NFData (B f t) where
      rnf (B fft) = rnf fft
    

    请注意,实际上并不需要
    Eq(ft)
    。只要
    导出实例Eq(f(f t))=>Eq(B f t)
    就可以了。(它仍然需要
    不可判定的实例
    )@JosephSible如果没有
    Eq(f(f t))
    Eq(f(f t)
    ,我无法编译它,你确定吗?看看我的答案——它对我有用。@JosephSible,@K.A.Buhr,哎呀,我错了。这是一个简化的示例,在原始版本中它是
    data B f t=B(f(f t))(f t)
    。那一个在上下文中需要额外的
    (f t)