我在函数参数的类型中放置了一个约束,而不是在函数的类型中。
我认为这会导致语法错误或向函数类型添加更多信息。
但看起来约束被完全忽略了。
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
test :: a -> String
test (n :: (Num a, Ord a) => a) =
if n > 10 then "Hello"
else "World"
main = print "Hello World"
Run Code Online (Sandbox Code Playgroud)
这给出了以下类型错误:
Test3.hs:6:8: error:
• No instance for (Num a) arising from a use of ‘n’
Possible fix:
add (Num a) to the context of
the type signature for:
test :: forall a. a -> String
• In the first argument of ‘(>)’, namely ‘n’
In the expression: n > 10
In the expression: if n > 10 then "Hello" else "World"
|
6 | if n > 10 then "Hello"
| ^
Test3.hs:6:8: error:
• No instance for (Ord a) arising from a use of ‘>’
Possible fix:
add (Ord a) to the context of
the type signature for:
test :: forall a. a -> String
• In the expression: n > 10
In the expression: if n > 10 then "Hello" else "World"
In an equation for ‘test’:
test (n :: (Num a, Ord a) => a)
= if n > 10 then "Hello" else "World"
|
6 | if n > 10 then "Hello"
|
Run Code Online (Sandbox Code Playgroud)
在参数的类型中添加约束实际上有什么作用?
编辑:
为什么这需要RankNTypes
扩展?
如果我删除(Num a, Ord a) =>
约束,则不需要它。
这是多态包含的一个相当奇特的实例,如此处所述,与约束包含交互。
如果类型a
包含b
,则在表面语言中exp :: a
暗示exp :: b
。包含的一个特殊例子是f :: forall a. a -> a
暗示f :: Int -> Int
。此外,我们已经n :: Int
暗示n :: c => Int
了任何c
约束。
然而,在核心语言中根本没有包含。表面语言中的每个包含情况都必须转换为显式的 lambda 表达式和应用程序。此外,c => a
简单地变成c -> a
,并且约束函数的使用被转换为f :: c => a
对某些 的简单函数应用inst :: c
。因此,f :: forall a. a -> a
成为f @Int :: Int -> Int
, 并
n :: Int
成为\_ -> n :: c -> Int
。
一个很少使用的情况是函数的逆变包含规则。以下是有效代码:
f :: (Int -> Int) -> Bool
f _ = True
g :: (forall a. a -> a) -> Bool
g = f
Run Code Online (Sandbox Code Playgroud)
这被翻译成
f :: (Int -> Int) -> Bool
f = \_ -> True
g :: (forall a. a -> a) -> Bool
g = \x -> f (x @Int)
Run Code Online (Sandbox Code Playgroud)
它与约束包含类似地工作:
f :: forall a. (Eq a => a) -> Bool
f _ = True
g :: forall a . a -> Bool
g = f
Run Code Online (Sandbox Code Playgroud)
翻译成
f :: forall a. (Eq a -> a) -> Bool
f = \_ -> True
g :: forall a . a -> Bool
g = \x -> f (\_ -> x)
Run Code Online (Sandbox Code Playgroud)
更接近原始问题,如果我们有
f (x :: Eq a => a) = True
Run Code Online (Sandbox Code Playgroud)
作为顶级定义,它的推断类型是forall a. (Eq a => a) -> Bool
. 然而,我们可以有任何类型的注释f
被包含在推断类型中!所以我们可能有:
f :: forall a. a -> Bool
f (x :: Eq a => a) = True
Run Code Online (Sandbox Code Playgroud)
GHC 仍然很高兴。原始代码
test :: a -> String
test (n :: (Num a, Ord a) => a) =
if n > 10 then "Hello"
else "World"
Run Code Online (Sandbox Code Playgroud)
相当于下面稍微清晰一点的版本:
test :: forall a. a -> String
test (n :: (Num a, Ord a) => a) =
if n > 10 then "Hello"
else "World"
Run Code Online (Sandbox Code Playgroud)
该类型的错误,你得到的是仅仅是因为n
实在是有两个参数的函数,一个具有类型Num a
和其他Ord a
,而这两个参数是包含记录Num
和Ord
方法。但是,由于定义中的范围内没有此类实例,因此您不能将其n
用作数字。翻译会转换n > 10
为(>) inst (n inst) (10 inst)
, where inst :: Num a
,但没有这样的inst
,所以我们无法翻译。
因此,在test
代码体中仍然使用n :: (Num a, Ord a) => a)
. 但是,如果我们只返回“Hello”而不使用n
,那么与前f
一种情况类似,我们会得到一个包含forall a. a -> String
注释类型的推断类型。然后在翻译输出中通过用 替换n
的主体中的每个出现来实现test
包含\_ -> n
。但是由于n
没有出现在正文中,因此翻译在这里没有任何作用。
归档时间: |
|
查看次数: |
166 次 |
最近记录: |