我一直在阅读有关从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语言设计师选择以这种方式实现的答案。
答案 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>