类型注释中断函数

时间:2017-11-30 15:09:41

标签: haskell

考虑函数f'的以下声明。使用带有Frames库(定义UnColumn和AllAre)的单例,以及使用withSing的包装函数。

*******video******** - file1\n
./video.sh
*******video******** - file1\n
*******video******** - file2\n
./video.sh
*******video******** - file2\n
*******video******** - file3\n
./video.sh

这似乎工作正常。但是当我添加一个类型注释时,类型检查失败并显示错误无法推断:{-# LANGUAGE AllowAmbiguousTypes -#} import Frames import Data.Singletons.Prelude f' :: forall rs1 rs2 a. (AllAre a (UnColumn rs1), AllAre a (UnColumn rs2), Num a) => SList rs1 -> SList rs2 -> Frame (Record rs1) -> Frame (Record rs2) -> Int f' = undefined f df1 df2 = withSing (withSing f') df1 df2

(AllAre a0 (UnColumn rs1), AllAre a0 (UnColumn rs2))
事实上,根据GHCi(嗯,Intero),这正是推断的类型签名。根据我的理解,添加与推断签名匹配的显式签名应该对代码语义没有影响,那么为什么会破坏代码?

1 个答案:

答案 0 :(得分:3)

作为一般经验法则,将与推断类型匹配的显式类型签名添加到Haskell程序中不会改变其含义,但在一般情况下实际上并未保证。 (我相信 保证Haskell98中的顶级定义。)

最终,您的问题与Haskell98中本地定义可能发生的类型变量范围问题的类型大不相同:

import Data.List
sortImage :: Ord b => (a -> b) -> [a] -> [a]
sortImage f = sortBy cmp
  where cmp x y = compare (f x) (f y)

此处,cmp的推断类型实际上是(Ord b) => a -> a -> Ordering。但是,您无法明确签名,因为您无法将ab绑定到外部签名(特别是f的类型)除非你使用ScopedTypeVariables,在这种情况下你可以写:

sortImage :: forall a b . Ord b => (a -> b) -> [a] -> [a]
sortImage f = sortBy cmp
  where cmp :: a -> a -> Ordering
        cmp x y = compare (f x) (f y)

正如您所发现的那样,您可以使用顶级定义来实现此类型变量范围问题,至少启用AllowAmbiguousTypes

这是一个更简单的例子,说明我认为是同一个问题,改编自AllowAmbiguousTypes扩展名的GHC文档:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

class D a b
instance D Bool b
instance D Int b

strange :: D a b => a -> a
strange = undefined

-- stranger :: (D a b1, D a b) => a -> a
stranger x = strange (strange x)

我已将stranger的推断类型显示为评论。如果您尝试明确说明,则会收到错误:

  

•由于使用'奇怪'而无法推断(D a b0)     从上下文来看:(D a b2,D a b)

问题在于,GHC可以推断stranger可以在满足aD a b1 strange :: D a b1 => a -> a的任何D a b上调用,并且也可以满足strange :: D a b => a -> a内在b1

但是,如果您尝试明确显示此类型签名,则b的显式签名中的strangerstrange变量之间的链接及其与{的类型的关系{1}}来电失去了,就像假设的a签名中的bcmp与{中​​的ab之间的关系一样{1}}签名在第一个示例中丢失。

单独使用sortImage并不足以解决此问题,因为除了约束之外,ScopedTypeVariables的类型只是strange并且不会直接引用{ {1}}。所以,你可以写:

a -> a

但您无法将bstranger :: forall a b1 b2 . (D a b1, D a b2) => a -> a stranger x = (strange :: a -> a) ((strange :: a -> a) x) b1来电的类型联系起来。您需要b2来执行此操作:

strange

然后键入检查好了,你甚至可以打电话:

TypeApplications

没有任何类型的注释(这有点令人惊讶)。如果你有一个实例:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

class D a b
instance D Bool b

strange :: forall a b . D a b => a -> a
strange = id

stranger :: forall a b1 b2 . (D a b1, D a b2) => a -> a
stranger x = (strange @a @b1) (strange @a @b2 x)

但是,您需要明确在> stranger False False 上使用instance D Int Double

stranger