在数据声明中添加类型约束

时间:2018-10-17 07:00:08

标签: haskell typeclass type-constraints

我一直在阅读有关从all other fields创建我们自己的类型和类型类的信息。在data声明中添加类型约束时,我不太了解Haskell编译器的行为。

例如,我有

{-# LANGUAGE DatatypeContexts #-}

data (Ord a) => OrderedValue a = OrderedValue a

getOrderedValue :: OrderedValue a -> a
getOrderedValue (OrderedValue a) = a

从上面可以看出,我的数据声明有类型约束,说明OrderedValue中包含的任何值都必须具有Ord实例。

我尝试编译此代码,然后编译器吐出

• No instance for (Ord a) arising from a use of ‘OrderedValue’
  Possible fix:
    add (Ord a) to the context of
      the type signature for:
        getOrderedValue :: forall a. OrderedValue a -> a
• In the pattern: OrderedValue a
  In an equation for ‘getOrderedValue’:
      getOrderedValue (OrderedValue a) = a

getOrderedValue的定义更改为

getOrderedValue :: (Ord a) => OrderedValue a -> a
getOrderedValue (OrderedValue a) = a

预期已解决该问题。

我的问题是-为什么编译器在这里抱怨?我以为编译器应该能够推断出a正在模式匹配中

getOrderedValue (OrderedValue a) = a

有一个Ord实例,因为OrderedValue值构造函数用于构造类型参数OrderedValue和实例a的类型Ord的实例。实例。

Ph,那是一口。

谢谢。

编辑-我看了@melpomene建议的替代答案,非常感谢。但是,我正在寻找一个描述为什么的Haskell语言设计师选择以这种方式实现的答案。

1 个答案:

答案 0 :(得分:2)

实际上,编译器可以推断出来:尝试删除函数类型签名并查看。 例如,使用ghci

Prelude> :set -XDatatypeContexts
Prelude> data Ord a => OrderedValue a = OrderedValue a
Prelude> let getOrderedValue (OrderedValue a) = a
Prelude> :t getOrderedValue
getOrderedValue :: Ord t => OrderedValue t -> t

但是,通过显式说明函数的类型,可以禁用任何推断。没有针对术语的显式类型注释时,将进行类型推断。如果您明确给出,则该术语无论如何都将具有这种类型,因此必须正确。

因此,这里的编译器正在对您的代码进行类型检查,并发现诸如OrderedValue a -> a之类的类型是错误的,因为它忽略了Ord a约束。

但是,请注意,不赞成使用DatatypeContexts,它允许您将约束放在数据类型上。对此的共识是最好只对真正需要约束的功能施加约束。如果您从数据类型中删除Ord a约束,那么getOrderedValue函数的编译效果很好,因为将没有Ord a约束要遵守,实际上您可以注意到,它更紧密地匹配您写的原始类型背后的直觉,现在是正确的。

顺便说一句,请注意,编译器自动推断的类型是最通用的类型,用于正确识别该函数的特定正文,但是您可以显式给出一个不太通用的类型

例如,以下功能:

mysum = foldr (+) 0

具有类型(Num b, Foldable t) => t b -> b,但是您可以输入:

mysum :: [Integer] -> Integer
mysum = foldr (+) 0

因为这是该函数的正确类型,尽管它是特定的。当然,将特定类型分配给mysum之后,就不能再使用Foldable的另一个实例或另一个Num类型的名称来调用它:专门化类型时,将失去通用性。 / p>