Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.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

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
List 如何在没有GADT或数据类型上下文的情况下定义列表的Eq实例_List_Haskell_Compiler Flags_Algebraic Data Types_Gadt - Fatal编程技术网

List 如何在没有GADT或数据类型上下文的情况下定义列表的Eq实例

List 如何在没有GADT或数据类型上下文的情况下定义列表的Eq实例,list,haskell,compiler-flags,algebraic-data-types,gadt,List,Haskell,Compiler Flags,Algebraic Data Types,Gadt,我使用的是由GHC 7.6.3版启动的Glasgow Haskell编译器,版本7.8.3,阶段2 我尝试对Haskell中的列表类型使用以下数据定义: data Eq a => List a = Nil | Cons a (List a) 但是,默认情况下,-XDatatypeContexts标志是必需的、无摩擦的,甚至从语言中删除。人们普遍认为这是一种语言的错误。我也不想对列表的定义使用特殊标志,因为我正在尝试复制现有列表类型的功能。 然后我可以使用以下代码段: data List

我使用的是由GHC 7.6.3版启动的
Glasgow Haskell编译器,版本7.8.3,阶段2

我尝试对Haskell中的列表类型使用以下数据定义:

data Eq a => List a = Nil | Cons a (List a)
但是,默认情况下,
-XDatatypeContexts
标志是必需的、无摩擦的,甚至从语言中删除。人们普遍认为这是一种语言的错误。我也不想对列表的定义使用特殊标志,因为我正在尝试复制现有列表类型的功能。 然后我可以使用以下代码段:

data List a where
 Nil :: List a
 Cons :: Eq a => a -> List a -> List a
它运行良好。这个解决方案的一个明显问题是,现在我需要使用
-XGADTs
标志,在这种情况下,我仍然不想依赖它,因为list的内置版本不需要运行。有没有办法将
Cons
中的类型限制为
Eq a
,这样我就可以比较两个列表,而不需要编译器标志,也不需要使用
派生的
关键字? 其余代码如下:

instance Eq (List a) where
 (Cons a b) == (Cons c d) = (a == c) && (b == d)
 Nil == Nil = True
 _ == _ = False
testfunction = Nil :: List Int
main = print (if testfunction == Nil then "printed" else "not printed")
我认为以下解决方案有效:

data List a = Nil | Cons a (List a)
instance Eq a => Eq (List a) where
 (Cons a b) == (Cons c d) = (a == c) && (b == d)
 Nil == Nil = True
 _ == _ = False
testfunction = Nil :: List Int
main = print (if testfunction == Nil then "printed" else "not printed")
但是,由于某些原因,它不适用于Eq的手动定义(此处等于)

我得到以下错误:

No instance for (Equal Int) arising from a use of ‘=+=’
    In the expression: testfunction =+= Nil
    In the first argument of ‘print’, namely
      ‘(if testfunction =+= Nil then "printed" else "not printed")’
    In the expression:
      print (if testfunction =+= Nil then "printed" else "not printed")
然而,通过使用GADT,我可以证明我的Equal类确实起作用。此代码适用于:

class Equal a where  
 (=+=) :: a -> a -> Bool  
 (/+=) :: a -> a -> Bool  
 x =+= y = not (x /+= y)  
 x /+= y = not (x =+= y)
data List a where
 Nil :: List a
 Cons :: Equal a => a -> List a -> List a
instance Equal (List a) where
 (Cons a b) =+= (Cons c d) = (a =+= c) && (b =+= d)
 Nil =+= Nil = True
 _ =+= _ = False
testfunction = Nil :: List Int
main = print (if testfunction =+= Nil then "printed" else "not printed")
但是,我必须使用
instance Equal(List a)where
而不是
instance Equal a=>Equal(List a)where
,否则我会得到错误:

No instance for (Equal Int) arising from a use of ‘=+=’
    In the expression: testfunction =+= Nil
    In the first argument of ‘print’, namely
      ‘(if testfunction =+= Nil then "printed" else "not printed")’
    In the expression:
      print (if testfunction =+= Nil then "printed" else "not printed")

看起来您试图将列表限制为只能保存实现
Eq
的值,如果没有扩展,您就无法做到这一点。但是,当
a
具有实现
Eq
的类型时,您可以告诉编译器如何比较两个
列表a
。有两种简单的方法可以做到这一点。最简单的是使用派生语句:

data List a = Nil | Cons a (List a) deriving (Eq)
或者您可以手动定义它:

instance Eq a => Eq (List a) where
    (Cons a b) == (Const c d) = (a == c) && (b == d)
    Nil == Nil = True
    _ == _ = False
现在,每当您用实现
Eq
的东西填充
列表
类型时,使用
=
,列表也将具有可比性。无需限制
Cons
中的值。您当然可以有一个普通的函数列表,如

fs1 :: [Int -> Int]
fs1 = [(+1), (*3), (+2), (*4)]
还是你的情况

fs2 :: List (Int -> Int)
fs2 = Cons (+1) $ Cons (*3) $ Cons (+2) $ Cons (*4) Nil
可以用作

> map ($ 10) fs1
[11, 30, 12, 40]
并给予

map' :: (a -> b) -> List a -> List b
map' f Nil = Nil
map' f (Cons x xs) = Cons (f x) (map' f xs)
然后

虽然要在GHCi中实际查看它,您还应该派生
Show

data List a = Nil | Cons a (List a) deriving (Eq, Show)
在GHC中还可以派生其他几个有用的类型类


要使其与自定义的
Equal
typeclass一起工作,您必须手动编写多个实例:

class Equal a where
    (=+=) :: a -> a -> Bool
    (/+=) :: a -> a -> Bool
    x =+= y = not (x /+= y)
    x /+= y = not (x =+= y)

instance Equal Int where
    x =+= y = x == y

instance Equal a => Equal (List a) where
    (Cons a b) =+= (Cons c d) = (a =+= c) && (b =+= d)
    Nil =+= Nil = True
    _ =+= _ = False
现在,因为您有一个实例
Equal Int
Equal a=>Equal(List a)
,所以您可以比较两个
List Int
s:

> let x = Cons 1 (Cons 2 (Cons 3 Nil)) :: List Int
> let y = Cons 1 (Cons 2 (Cons 3 Nil)) :: List Int
> x =+= y
True
> x =+= Nil
False

对于要存储在
列表中并在上使用
=+=
的任何类型,都必须为该类型实现
Equal

通常的解决方案是使用此结构:

data List a = Nil | Cons a (List a)

instance Eq a => Eq (List a) where
 (Cons a b) == (Cons c d) = (a == c) && (b == d)
 Nil == Nil = True
 _ == _ = False
注意,我添加了
Eq a
作为实例的约束,而不是数据类型的约束。通过这种方式,你可以对所有列表进行平等性比较,就像你试图写它的方式一样,可以对所有列表进行平等性比较,但是你也允许存在无法对平等性进行比较的事物列表。您将遇到的每个Haskell版本都支持这一点,即使是非常旧的版本,也没有扩展

当您想要添加一个
Show
实例、一个
Ord
实例等时,这种方法也可以很好地扩展;要按您的方式执行,您必须继续返回,并通过添加更多约束(可能会破坏运行良好的现有代码,因为它不需要这些实例),使数据结构更具限制性。然而,如果您不限制数据类型,只让实例
Show a=>Show(List a)
Ord a=>Ord(List a)
等,那么您可以显示和排序恰好支持这两种类型的列表,但您仍然可以拥有(和Show)支持
显示但不支持
Ord
的类型列表,反之亦然

另外,对于参数化类型(如
列表a
),有许多有用的类型类要求它们在其类型参数中是完全泛型的。例如
函子
应用程序
单子
;无法为您尝试创建的受约束列表类型正确实现这些


虽然可以像您正在尝试的那样创建受约束的列表(但只能通过使用扩展,正如您所发现的那样),但人们发现,在数据类型的类型参数中保留完全通用的数据类型通常更为有用,如果您的类型的特定用法需要对类型参数施加限制,则可以在该用法站点上施加限制,而不是在每次使用该数据类型时施加限制。这应该是您的“默认设置”;当你有充分的理由时就离开它(你在这里很可能有这样的理由,但你在问题中没有告诉我们,因此没有人能推荐最好的处理方法)。

似乎只要我实际使用内置的Eq类,这个解决方案就行得通。奇怪的是,一个定制的Eq类,如果不这样做的话,就不能使用这个解决方案。我定义了一个:class=a,其中(=+=)::a->a->Bool(+/+=)::a->a->Bool x=+=y=not(x/+=y)x/+=y=not(x=+=y)@TimothySwan,内置的
Eq
类没有什么特别之处,只是它有一堆为内置类型定义的实例。你应该能够完全模仿它,包括这个解决方案(这是你问题的标准解决方案,我已经做了很多次)目前,你的答案似乎只适用于Haskell内置的Eq类。但是,我需要一个适用于任何类的解决方案。我正在相应地更新这个问题。@TimothySwan No.导出的p
> let x = Cons 1 (Cons 2 (Cons 3 Nil)) :: List Int
> let y = Cons 1 (Cons 2 (Cons 3 Nil)) :: List Int
> x =+= y
True
> x =+= Nil
False
data List a = Nil | Cons a (List a)

instance Eq a => Eq (List a) where
 (Cons a b) == (Cons c d) = (a == c) && (b == d)
 Nil == Nil = True
 _ == _ = False