繁体   English   中英

类型注释中断功能

[英]Type annotation breaks function

考虑以下使用框架框架(定义了UnColumn和AllAre)的单例函数f'的声明以及使用withSing的包装函数。

{-# 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))

f :: (SingI rs1, SingI rs2, AllAre a (UnColumn rs2), AllAre a (UnColumn rs1), Num a)
=> Frame (Record rs1) -> Frame (Record rs2) -> Int
f df1 df2 = withSing (withSing f') df1 df2

事实是,根据GHCi(好吧,Intero),这正是推断的类型签名。 据我了解,添加一个与推断出的签名相匹配的显式签名应该不会对代码语义产生影响,那么为什么要破坏代码呢?

作为一般经验法则 ,将与推断的类型匹配的显式类型签名添加到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 但是,您不能使该签名明确,因为除非使用ScopedTypeVariables ,否则您不能将ab绑定到外部签名(特别是f的类型),在这种情况下,您可以编写:

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 ab)

问题是,GHC可以推断, stranger可以叫上任何a即满足D a b1的外strange :: D a b1 => a -> a和也满足D ab为内strange :: D ab => a -> a

但是,如果您尝试进行这种类型的签名明确的之间的联系, b1b为显性特征变量stranger和他们的类型的关系strange来电丢失,多为关系ab在假设的cmp签名以及sortImage签名中的ab在第一个示例中丢失了。

仅使用ScopedTypeVariables不足以解决这里的问题,因为除约束之外, strange的类型只是ScopedTypeVariables a -> a而没有直接引用b 因此,您可以编写:

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

但是您不能将b1b2strange呼叫的类型相关联。 您需要使用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

但是,那么您需要明确地在Int上使用stranger

> stranger @_ @Double @Double (1 :: Int)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM