我目前正在学习Haskell,所以这里有一个初学者的问题:
下面的文字中单一类型是什么意思?
单一类型是一个特殊的Haskell术语吗?这是否意味着原子类型?
或者这是否意味着我永远不能在Haskell中创建一个列表,我可以将1
和'c'
放在一起?
我在想一个类型是一组值。
所以我无法定义包含Char
和Int
s的类型?
代数数据类型怎么样?
类似于:data IntOrChar = In Int | Ch Char
? (我想这应该有用,但我很困惑作者对那句话的意思。)
顺便说一句,这是在Haskell中制作列表的唯一方法,我可以将Ints
和Chars
放在一起吗?还是有更棘手的方法?
Scala类比:在Scala中,可以将隐式转换写入表示Int
和Char
s(如IntOrChar
)的类型,然后就可以了将Int
和Char
放入List[IntOrChar]
中,是不是Haskell无法做到的?如果我想将它们放入Int
列表中,我是否始终必须将每个Char
或IntOrChar
明确包装到IntOrChar
?
Haskell还包含多态类型---类型 通过某种方式对所有类型进行普遍量化。多态型 表达式实质上描述了类型的族。例如, (forall a)[a]是由每种类型a组成的类型的家族, a的列表类型。整数列表(例如[1,2,3]),列表 字符(['a','b','c']),甚至是整数列表等等 这个家庭的所有成员。 (但请注意,[2,'b']不是a 有效的例子,因为没有包含2和2的单一类型 'B')
答案 0 :(得分:5)
在Haskell中没有隐式转换。也没有联合类型 - 只有不相交的联合(代数数据类型)。所以你只能写:
someList :: [IntOrChar]
someList = [In 1, Ch 'c']
注意:这是一种很少使用的技术。如果您需要它,可能会使您的API过于复杂。
但存在各种类型。
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
class IntOrChar a where
intOrChar :: a -> Either Int Char
instance IntOrChar Int where
intOrChar = Left
instance IntOrChar Char where
intOrChar = Right
data List = Nil
| forall a. (IntOrChar a) => Cons a List
someList :: List
someList = (1 :: Int) `Cons` ('c' `Cons` Nil)
在这里,我创建了一个只有函数IntOrChar
的类型intOrChar
。这样,您就可以将forall a. (IntOrChar a) => a
类型的任何内容转换为Either Int Char
。
还有一种在其第二个构造函数中使用存在类型的特殊列表。
这里类型变量a
在构造函数范围内绑定(带forall
)。所以每一次
使用Cons
,您可以传递forall a. (IntOrChar a) => a
类型的任何内容作为第一个参数。因此,在破坏(即模式匹配)期间,第一个参数将会出现
仍然是forall a. (IntOrChar a) => a
。您唯一能做的就是将其传递或致电intOrChar
并将其转换为Either Int Char
。
withHead :: (forall a. (IntOrChar a) => a -> b) -> List -> Maybe b
withHead f Nil = Nothing
withHead f (Cons x _) = Just (f x)
intOrCharToString :: (IntOrChar a) => a -> String
intOrCharToString x =
case intOrChar of
Left i -> show i
Right c -> show c
someListHeadString :: Maybe String
someListHeadString = withHead intOrCharToString someList
再次注意,你不能写
{- Wont compile
safeHead :: IntOrChar a => List -> Maybe a
safeHead Nil = Nothing
safeHead (Cons x _) = Just x
-}
-- This will
safeHead2 :: List -> Maybe (Either Int Char)
safeHead2 Nil = Nothing
safeHead2 (Cons x _) = Just (intOrChar x)
safeHead
无效,因为您希望IntOrChar a => Maybe a
的{{1}}类型绑定在a
范围,而safeHead
的类型为{{1} } Just x
绑定在IntOrChar a1 => Maybe a1
范围内。
答案 1 :(得分:3)
在Scala中,有些类型包括Int
和Char
,例如AnyVal
和Any
,它们都是Char
和{{1}的超类型}}。在Haskell中没有这样的层次结构,所有基本类型都是不相交的。
您可以创建自己的联合类型来描述'Int
或Int
的概念(或者您可以使用内置的Char
类型),但有Haskell中没有隐式转换可以将Either
透明地转换为Int
。
您可以使用existential types模仿“任意”的概念:
IntOrChar
请注意,此模式为discouraged。
答案 2 :(得分:2)
我认为这里的区别在于你的代数数据类型IntOrChar
是一个带标记的联合" - 也就是说,当您的值为IntOrChar
时,您会知道它是Int
还是Char
。
通过比较考虑这个匿名联合定义(在C中):
typedef union { char c; int i; } intorchar;
如果您获得intorchar
类型的值,则您不知道(先验)哪个选择器有效。这就是为什么大多数情况下union
构造函数与struct
一起使用以形成标记联合构造的原因:
typedef struct {
int tag;
union { char c; int i; } intorchar_u
} IntOrChar;
此处tag
字段编码联合的哪个选择器有效。
union
构造函数的另一个主要用途是覆盖两个结构,以获得子结构之间的有效映射。例如,这种联合是有效访问int的各个字节的一种方法(假设8位字符和32位整数):
union { char b[4]; int i }
现在,来说明"标记的工会与#34;之间的主要区别。和#34;匿名工会"考虑如何在这些类型上定义函数。
要在IntOrChar
值(标记的联合)上定义一个函数,我声称你需要提供两个函数 - 一个取Int
的函数(如果值为{{1} }}和一个带Int
的值(如果值为Char
)。由于该值是用其类型标记的,因此它知道它应该使用哪两个函数。
如果我们让Char
表示从F(a,b)
类型到a
类型的函数集,我们就有:
b
其中F(IntOrChar,b) = F(Int,b) \times F(Char,b)
表示叉积。
对于匿名联合\times
,由于值不对其类型的任何内容进行编码,因此可以应用的唯一函数是对intorchar
和{{1}都有效的函数。值,即:
Int
其中Char
表示交集。
在Haskell中,只有一个函数(据我所知)可以应用于整数和字符,即身份函数。因此,在Haskell中使用F(intorchar,b) = F(Int,b) \cap F(Char,b)
之类的列表并不多。在其他语言中,这个交集可能不是空的,然后像这样的结构更有意义。
总而言之,如果您创建了tagged-union,则可以在同一列表中包含整数和字符,在这种情况下,您必须标记每个值,这些值将使您的列表看起来像:
\cap
如果你没有标记你的值,那么你正在创建一个类似于匿名联合的东西,但是由于没有任何(有用的)函数可以应用于整数和字符。对于那种联盟,你真的不能做任何事情。