ale*_*pov 13 haskell types type-systems
抓住Haskell类型系统的表面,运行:
Prelude> e = []
Prelude> ec = tail "a"
Prelude> en = tail [1]
Prelude> :t e
e :: [a]
Prelude> :t ec
ec :: [Char]
Prelude> :t en
en :: Num a => [a]
Prelude> en == e
True
Prelude> ec == e
True
Run Code Online (Sandbox Code Playgroud)
不知何故,尽管en和ec有不同的类型,但它们都在== e上测试True .我说"某种程度上"不是因为我感到惊讶(我不是),而是因为我不知道允许这个的规则/机制的名称是什么.就好像表达式"[] == en"中的类型变量 "a"被允许采用值"Num"进行评估.同样,当用"[] == ec"测试时,它被允许变成"Char".
我不确定我的解释是否正确的原因是:
Prelude> (en == e) && (ec == e)
True
Run Code Online (Sandbox Code Playgroud)
,因为直觉上这意味着在同一个表达式中,e"同时"假设Num和Char的值(至少我用来解释&&的语义).除非Char的"假设"仅在(ec == e)的评估期间起作用,并且(en == e)是独立评估的,在单独的......减少中?(我在这里猜测一个术语).
然后来了:
Prelude> en == es
<interactive>:80:1: error:
• No instance for (Num Char) arising from a use of ‘en’
• In the first argument of ‘(==)’, namely ‘en’
In the expression: en == es
In an equation for ‘it’: it = en == es
Prelude> es == en
<interactive>:81:7: error:
• No instance for (Num Char) arising from a use of ‘en’
• In the second argument of ‘(==)’, namely ‘en’
In the expression: es == en
In an equation for ‘it’: it = es == en
Run Code Online (Sandbox Code Playgroud)
异常并不令人惊讶,但令人惊讶的是,在两个测试中,错误消息都抱怨"使用'en'" - 并且如果它是第一个或第二个操作数并不重要.
关于Haskell类型系统可能需要学习一个重要的教训.感谢您的时间!
Fyo*_*kin 13
当我们这样说时e :: [a]
,它意味着它e
是任何类型的元素列表.哪种类型?随便哪种!无论你目前碰巧需要哪种类型.
如果你来自非ML语言,首先通过查看函数(而不是值)可能会更容易理解.考虑一下:
f x = [x]
Run Code Online (Sandbox Code Playgroud)
这个功能的类型是f :: a -> [a]
.这大致意味着此功能适用于任何类型a
.你给它一个这种类型的值,它会返回一个包含该类型元素的列表.哪种类型?随便哪种!无论你碰巧需要什么.
当我调用此函数时,我有效地选择了我想要的类型.如果我称它为f 'x'
,我选择a = Char
,如果我称之为f True
,我选择a = Bool
.因此,重要的一点是调用函数的人选择类型参数.
但我不必一次性地选择它,而且永远不会选择它.相反,我每次调用函数时都会选择类型参数.考虑一下:
pair = (f 'x', f True)
Run Code Online (Sandbox Code Playgroud)
我在这里打电话f
两次,每次都选择不同的类型参数 - 我第一次选择a = Char
,第二次选择a = Bool
.
好了,现在进行下一步:当我选择类型参数时,我可以通过多种方式完成.在上面的例子中,我通过传递我想要的类型的值参数来选择它.但另一种方法是指定我想要的结果类型.考虑一下:
g x = []
a :: [Int]
a = g 0
b :: [Char]
b = g 42
Run Code Online (Sandbox Code Playgroud)
这里,函数g
忽略它的参数,因此它的类型和结果之间没有关系g
.但我仍然能够通过周围环境约束来选择结果的类型.
而现在,精神上的飞跃:没有任何参数的函数(又称"值")与具有参数的函数没有什么不同.它只有零参数,就是这样.
如果一个值有类型参数(例如你的值e
),我可以在每次"调用"该值时选择该类型参数,就像它是一个函数一样容易.因此,在表达式中,e == ec && e == en
您只需"调用" e
两次值,在每次调用时选择不同的类型参数 - 就像我在pair
上面的示例中所做的那样.
混乱Num
是一个完全不同的问题.
你看,Num
不是一种类型.这是一个类型类.类型类有点像Java或C#中的接口,除了您可以稍后声明它们,不一定与实现它们的类型一起.
因此签名en :: Num a => [a]
意味着它en
是一个包含任何类型元素的列表,只要该类型实现("有一个实例")类型类Num
.
Haskell的类型推断方式是,编译器将首先确定它可以使用的最具体类型,然后尝试查找这些类型所需类型类的实现("实例").
在你的情况下,编译器看到en :: [a]
正在进行比较ec :: [Char]
,并且它表示:"哦,我知道:a
必须是Char
!" 然后它去寻找类实例,并注意到a
必须有一个实例Num
,而且由于a
是Char
,它遵循的是Char
必须具备的一个实例Num
.但它没有,所以编译器抱怨:"找不到(Num Char)"
至于"因使用而产生en
" - 嗯,那是因为是需要实例en
的原因Num
.en
是Num
其类型签名中的一个,因此它的存在是导致需求的原因Num
归档时间: |
|
查看次数: |
301 次 |
最近记录: |