在Haskell中学习参数化类型主题时,我有一个微不足道但又令人筋疲力尽的问题。这是我的问题:
看看这是Maybe
的定义:
data Maybe a = Just a | Nothing
我们使用它:
Just "hello world"
Just 100
但为什么不能采用类型变量?
例如:
Just String
Just Int
我知道这个问题很愚蠢,但我仍然无法弄清楚......
答案 0 :(得分:6)
嗯,首先请注意,String
和Int
不是类型变量,而是类型(类型常量,如果可以的话)。但对于你的问题,这并不重要。
重要的是Haskells 类型语言和值语言之间的命运。这些通常是分开的。 String
和Int
以及Maybe
使用的是类型语言,"hello world"
和100
以及Just
和Nothing
住在价值语言。每个人都对另一方一无所知。只有,编译器知道"值的描述属于该类型",但实际类型仅在编译时存在,并且值仅在运行时存在。
有两件事情有点令人困惑:
允许使用类型和值语言存在的名称。最着名的是()
,仅仅是同义词类型
newtype Endo a = Endo { runEndo :: a -> a }
但实际上这些是两个单独的实体:类型构造函数Endo :: *->*
(参见下面的*
个东西)和值构造函数Endo :: (a->a) -> Endo a
。它们碰巧共享相同的名称,但是在完全不同的范围内 - 就像你同时声明addTwo x = x + 2
和greet x = "Hello "++x
一样,x
符号的两种用法都与每个符号无关其他
data
语法似乎混合了类型和值。在其他任何地方,类型和值必须始终用::
分隔,最常见的是签名
"hello world" :: String
100 :: Int
Just :: Int -> Maybe Int
{-hence-}Just 100 :: Maybe Int
Nothing :: Maybe Int
foo :: (Num a, Ord a) => a -> Maybe a -- this really means `forall a . (Num a, Ord a) => a -> Maybe a
foo n | n <= 0 = Nothing
| otherwise = Just $ n - 1
如果启用data
,确实可以使用该语法以更独特的方式定义-XGADTs
:
data Maybe a where
Just :: a -> Maybe a
Nothing :: Maybe a
现在我们再次::
作为值级别(左)和类型级别之间的明确区别。
你实际上可以再提一个级别:上面的声明也可以写成
data Maybe :: * -> * where
Just :: a -> Maybe a
Nothing :: Maybe a
此处Maybe :: * -> *
表示&#34; Maybe
是类型级别的内容, kind * -> *
&#34 ;,即需要类型*
的类型级参数(例如Int
)并返回另一种类型级别的类*
(此处为Maybe Int
)。种类与类型相同。
答案 1 :(得分:0)
您当然可以声明data Maybe a = Just String | Nothing
,并且您可以声明data Maybe a = Just Int | Nothing
,但一次只能声明其中一个。使用类型变量允许以何种方式声明构造值的内容类型随类型变量的值而变化。所以data Maybe a = Just a | Nothing
告诉我们内容&#34;内部&#34; Just
正好是传递给Maybe
的类型。那样Maybe String
意味着&#34;内部&#34; Just
类型为String
,Maybe Int
表示&#34;内部&#34; Just
的值为Int
。