可能使用约束的Haskell类型同义词声明?

可能使用约束的Haskell类型同义词声明?,haskell,types,polymorphism,constraints,Haskell,Types,Polymorphism,Constraints,假设我想用Int为所有列表创建一个类型同义词 我可以做到: type NumberList = [Int] 但如果我想调用所有包含数字的列表NumberList呢?我如何设置一个约束条件,并说只要“numa”,所有[a]都应该被称为相同的呢 在看到答案后,我重新思考。我似乎违背了Haskell背后的一个基本理念,而回报相对较小(只是一个正式问题)。我决定这样做:如果一个类型需要两个彼此相同但只在Int或Float上有所不同的实例,那么它们之间的差异太小,无法保证实现Int和Float的使用所需

假设我想用Int为所有列表创建一个类型同义词

我可以做到:

type NumberList = [Int]
但如果我想调用所有包含数字的列表NumberList呢?我如何设置一个约束条件,并说只要“numa”,所有[a]都应该被称为相同的呢

在看到答案后,我重新思考。我似乎违背了Haskell背后的一个基本理念,而回报相对较小(只是一个正式问题)。我决定这样做:如果一个类型需要两个彼此相同但只在Int或Float上有所不同的实例,那么它们之间的差异太小,无法保证实现Int和Float的使用所需的变通方法,但将它们称为同一事物,这就是为什么我必须限制使用其中一个实例。但是,如果有一个重要的原因说明我应该同时拥有这两个,那么我可能会以实例的名义反映这一重要原因,从而通过以下方式避免问题:

data Thing = Thing_A(String, String Float) | Thing_B(String,String,Int)
---因此,坚持使用Haskell的类型系统,并且仍然接受它们作为数据类型。起初我想做的是

data Thing = Thing(String, String, Float) | Thing(String, String, Int)

这与存在量化相对应。在伪Haskell中

type NumberList = exists a . Num a => [a]
我之所以说“pseudo”,是因为GHC不允许动态引入存在量词——您需要为此创建一个单独的数据类型

现在,您将使用箭头左侧的大多数类型NumberList,其中«exists»有效地将其含义更改为«forall»

也就是说,不是写作

isIncreasing :: NumberList -> Bool
这和

isIncreasing :: (exists a . Num a => [a]) -> Bool
你可以写

isIncreasing :: forall a . Num a => [a] -> Bool
或者干脆

isIncreasing :: Num a => [a] -> Bool
当然,使用类型同义词似乎代码更少,但也有缺点。 顺便说一句,这些缺点对于基于存在主义方法的面向对象编程来说是典型的

例如,您希望连接两个列表。通常你会写信

(++) :: forall a . [a] -> [a] -> [a]
(为了清晰起见,
forall
也没有必要添加)。由于
a
在整个签名中都是相同的,因此可以确保将相同类型的列表串联在一起

我们如何连接两个数字列表?签名

(++) :: NumberList -> NumberList -> NumberList
不起作用,因为一个列表可能包含int,另一个可能包含double。结果NumberList必须包含单一类型的值

或者,比方说,您希望找到列表元素的总和

通常你写

sum :: Num a => [a] -> a
请注意,结果类型与列表元素的类型相同。唉,我们不能为NumberList做同样的事情

sum :: NumberList -> ???
结果类型是什么?我们也可以在那里应用存在量化

sum :: NumberList -> (exists a . Num a => a)
但是现在,原始列表类型和sum类型之间的连接丢失了——至少对于Haskell的类型系统来说是这样。如果你决定写一个函数,比如

multiplySum :: Integer -> [Integer] -> Integer
multiplySum x ys = x * sum ys
然后您将得到一个类型错误,因为
sum ys
可以是任何类型,不一定是Integer类型

如果你把每一件事都推到极端,并将每一种类型都量化,这将是可行的——但你最终会遇到另一种类似面向对象的语言及其所有问题


(当然,也有一些很好的存在量化用例。)

GHC允许使用RankNTypes实现这一点

所以你可以这样做:

type NumList = forall a . (Num a ,Fractional a) => [a]
如果我们有:

numList:: NumList
numList = [1,2,3]

fracList:: NumList
fracList = [1.3,1.7]
进行串联会产生:

fracList ++ numList :: Fractional a => [a]
哪个NumList是的同义词。 总而言之,我真的看不出这种情况有什么意义。

使用数据和参数,而不是存在主义来获取上下文 我想如果你想

data Thing = Good [(Char,Int)] | Bad String | Indifferent Leg
但是有时候

data Thing = Good [(Char,Float)] | Bad String | Indifferent Arm
你可以定义

data Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart
或者,如果您想确保
num
始终是数字,您可以这样做

data Num num => Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart
最后,您可以通过定义自己的类来限制
bodypart
中允许的类型:

class Body a where
   -- some useful function(s) here

instance Body Leg where
   -- define useful function(s) on Legs
instance Body Arm
   -- define useful function(s) on Arms

data (Num num,Body bodypart) => Thing num bodypart = 
                                                             Good [(Char,num)] | Bad String | Indifferent bodypart
我不鼓励您通过forall构造函数或GADTs使用存在类型,因为向数据类型添加
num
参数在实践中更有用,尽管它需要更多的类型

类型同义词的约束? 请注意,当使用约束时,如

data (Num num) => Thing num = T [(Char,num)]
实际上,只将构造函数的类型
T
更改为

T :: (Num num) => [(Char,num)] -> Thing num
而不是
T::[(Char,num)]->Thing num
。这意味着每次使用
T
,都需要有一个上下文
(Num Num)
,但这正是您真正想要的-阻止人们将数据放入非数字的数据类型中

这一事实的结果是你不能写作


因为没有数据构造函数
T
,上下文
(Num Num)
在上是必需的;如果我有一个[('4',False)],它会自动匹配类型
[(Char,num)]
,因为它是同义词。编译器在决定某个对象的类型之前,不能在代码周围运行以查找实例。在
data
案例中,它有一个构造函数
T
,告诉它类型,并且它可以保证有一个
Num
实例,因为它检查了您对函数
T
的使用。没有
T
,没有上下文。

当您无法恢复原始类型时,使用这种类型没有多大意义。如果您只需要特定的
Num
s,您应该简单地将它们包装起来,而不是变戏法:

data NumWrapper = WInt Int 
                | WDouble Double 
                | WFloat Float 
                deriving Show

numList :: [NumWrapper]
numList = [WInt 12, WFloat 1.2, WDouble 3.14]

如果您真的想打开任意
Num
类型,那么列表可能就是您的错误集合。有HLists等,请参见

是否希望类型为
NumberList
的值能够包含任何数字的列表,或包含任何数字的列表,或具有多态性并可用作任何数字类型的列表?a“
a
对于某些
Num a
”类型将是非常无用的,因为Num-typeclass不允许您以任何方式返回数字。你想拿这样的清单做什么?我会调查这些答案。。。我想做什么?我想创建一个数据类型,其中构造函数中的一个值是[(Char,Int)]bu
data NumWrapper = WInt Int 
                | WDouble Double 
                | WFloat Float 
                deriving Show

numList :: [NumWrapper]
numList = [WInt 12, WFloat 1.2, WDouble 3.14]