为什么函数类型需要 "wrapped" 才能满足类型检查器的要求?
Why is a function type required to be "wrapped" for the type checker to be satisfied?
以下程序类型检查:
{-# LANGUAGE RankNTypes #-}
import Numeric.AD (grad)
newtype Fun = Fun (forall a. Num a => [a] -> a)
test1 [u, v] = (v - (u * u * u))
test2 [u, v] = ((u * u) + (v * v) - 1)
main = print $ fmap (\(Fun f) -> grad f [1,1]) [Fun test1, Fun test2]
但是这个程序失败了:
main = print $ fmap (\f -> grad f [1,1]) [test1, test2]
类型错误:
Grad.hs:13:33: error:
• Couldn't match type ‘Integer’
with ‘Numeric.AD.Internal.Reverse.Reverse s Integer’
Expected type: [Numeric.AD.Internal.Reverse.Reverse s Integer]
-> Numeric.AD.Internal.Reverse.Reverse s Integer
Actual type: [Integer] -> Integer
• In the first argument of ‘grad’, namely ‘f’
In the expression: grad f [1, 1]
In the first argument of ‘fmap’, namely ‘(\ f -> grad f [1, 1])’
直觉上,后一个程序看起来是正确的。毕竟,
以下,看似等效的程序确实有效:
main = print $ [grad test1 [1,1], grad test2 [1,1]]
这看起来像是 GHC 类型系统的限制。我想知道
是什么原因导致失败,为什么存在此限制,以及任何可能的
除了包装函数之外的解决方法(根据上面的 Fun
)。
(注意:这不是单态限制导致的;编译
使用 NoMonomorphismRestriction
没有帮助。)
类型推断算法不会推断更高级别的类型(->
左侧带有 forall
的类型)。如果我没记错的话,它变得不可判定。不管怎样,考虑一下这段代码
foo f = (f True, f 'a')
它的类型应该是什么?我们可以
foo :: (forall a. a -> a) -> (Bool, Char)
但我们也可以
foo :: (forall a. a -> Int) -> (Int, Int)
或者,对于任何类型的构造函数 F :: * -> *
foo :: (forall a. a -> F a) -> (F Bool, F Char)
在这里,据我所知,我们找不到主要类型 -- 一种我们可以分配给 foo
.
的最一般类型
如果主要类型不存在,类型推断机制只能为 foo
选择一个次优类型,这可能会在以后导致类型错误。这是不好的。相反,GHC 依赖于 Hindley-Milner 风格的类型推理引擎,该引擎得到了极大的扩展以涵盖更高级的 Haskell 类型。这种机制与普通的 Hindley-Milner 不同,它将分配 f
多态类型 提供 用户明确要求,例如通过给 foo
一个签名。
使用像 Fun
这样的包装器新类型也以类似的方式指示 GHC,为 f
.
提供多态类型
这是 GHC 类型系统的问题。顺便说一下,它确实是 GHC 的类型系统; Haskell/ML 类语言的原始类型系统不支持更高级别的多态性,更不用说我们在这里使用的谓词多态性了。
问题是为了进行类型检查,我们需要在类型的任何位置支持 forall
s。不仅一直聚集在类型的前面(允许类型推断的正常限制)。一旦离开这个区域,类型推断通常就变得不可判定(对于 n 级多态性和更高级别)。在我们的例子中,[test1, test2]
的类型需要是 [forall a. Num a => a -> a]
,考虑到它不适合上面讨论的方案,这是一个问题。这将要求我们使用谓词多态性,之所以这样称呼是因为 a
范围超过其中包含 forall
的类型,因此 a
可以替换为它正在使用的类型。
因此,因此会出现一些行为不当的情况,仅仅是因为问题无法完全解决。 GHC 确实有一些对 n 阶多态性的支持和一些对命令式多态性的支持,但通常最好只使用新类型包装器来获得可靠的行为。据我所知,GHC 也不鼓励使用这个特性,因为很难准确地弄清楚类型推断算法将处理什么。
总而言之,math 说会有不稳定的情况,newtype
包装器是最好的处理方式,即使有些不满意。
以下程序类型检查:
{-# LANGUAGE RankNTypes #-}
import Numeric.AD (grad)
newtype Fun = Fun (forall a. Num a => [a] -> a)
test1 [u, v] = (v - (u * u * u))
test2 [u, v] = ((u * u) + (v * v) - 1)
main = print $ fmap (\(Fun f) -> grad f [1,1]) [Fun test1, Fun test2]
但是这个程序失败了:
main = print $ fmap (\f -> grad f [1,1]) [test1, test2]
类型错误:
Grad.hs:13:33: error:
• Couldn't match type ‘Integer’
with ‘Numeric.AD.Internal.Reverse.Reverse s Integer’
Expected type: [Numeric.AD.Internal.Reverse.Reverse s Integer]
-> Numeric.AD.Internal.Reverse.Reverse s Integer
Actual type: [Integer] -> Integer
• In the first argument of ‘grad’, namely ‘f’
In the expression: grad f [1, 1]
In the first argument of ‘fmap’, namely ‘(\ f -> grad f [1, 1])’
直觉上,后一个程序看起来是正确的。毕竟, 以下,看似等效的程序确实有效:
main = print $ [grad test1 [1,1], grad test2 [1,1]]
这看起来像是 GHC 类型系统的限制。我想知道
是什么原因导致失败,为什么存在此限制,以及任何可能的
除了包装函数之外的解决方法(根据上面的 Fun
)。
(注意:这不是单态限制导致的;编译
使用 NoMonomorphismRestriction
没有帮助。)
类型推断算法不会推断更高级别的类型(->
左侧带有 forall
的类型)。如果我没记错的话,它变得不可判定。不管怎样,考虑一下这段代码
foo f = (f True, f 'a')
它的类型应该是什么?我们可以
foo :: (forall a. a -> a) -> (Bool, Char)
但我们也可以
foo :: (forall a. a -> Int) -> (Int, Int)
或者,对于任何类型的构造函数 F :: * -> *
foo :: (forall a. a -> F a) -> (F Bool, F Char)
在这里,据我所知,我们找不到主要类型 -- 一种我们可以分配给 foo
.
如果主要类型不存在,类型推断机制只能为 foo
选择一个次优类型,这可能会在以后导致类型错误。这是不好的。相反,GHC 依赖于 Hindley-Milner 风格的类型推理引擎,该引擎得到了极大的扩展以涵盖更高级的 Haskell 类型。这种机制与普通的 Hindley-Milner 不同,它将分配 f
多态类型 提供 用户明确要求,例如通过给 foo
一个签名。
使用像 Fun
这样的包装器新类型也以类似的方式指示 GHC,为 f
.
这是 GHC 类型系统的问题。顺便说一下,它确实是 GHC 的类型系统; Haskell/ML 类语言的原始类型系统不支持更高级别的多态性,更不用说我们在这里使用的谓词多态性了。
问题是为了进行类型检查,我们需要在类型的任何位置支持 forall
s。不仅一直聚集在类型的前面(允许类型推断的正常限制)。一旦离开这个区域,类型推断通常就变得不可判定(对于 n 级多态性和更高级别)。在我们的例子中,[test1, test2]
的类型需要是 [forall a. Num a => a -> a]
,考虑到它不适合上面讨论的方案,这是一个问题。这将要求我们使用谓词多态性,之所以这样称呼是因为 a
范围超过其中包含 forall
的类型,因此 a
可以替换为它正在使用的类型。
因此,因此会出现一些行为不当的情况,仅仅是因为问题无法完全解决。 GHC 确实有一些对 n 阶多态性的支持和一些对命令式多态性的支持,但通常最好只使用新类型包装器来获得可靠的行为。据我所知,GHC 也不鼓励使用这个特性,因为很难准确地弄清楚类型推断算法将处理什么。
总而言之,math 说会有不稳定的情况,newtype
包装器是最好的处理方式,即使有些不满意。