Haskell 调用一个实例";方法“;在哈斯克尔

Haskell 调用一个实例";方法“;在哈斯克尔,haskell,Haskell,我正在阅读《Haskell从第一原理编程》一书,在其中一个建议的练习中,作者要求我们编写一个类TooMany for(Num a,TooMany a)=>(a,a)的实例,如果两个数字之和大于42,则应进行比较 这是我的密码: class TooMany a where tooMany :: a -> Bool instance TooMany Int where tooMany n = n > 42 instance (Num a, TooMany a) =>

我正在阅读《Haskell从第一原理编程》一书,在其中一个建议的练习中,作者要求我们编写一个类TooMany for(Num a,TooMany a)=>(a,a)的实例,如果两个数字之和大于42,则应进行比较

这是我的密码:

class TooMany a where
    tooMany :: a -> Bool
instance TooMany Int where
    tooMany n = n > 42
instance (Num a, TooMany a) => TooMany(a, a) where
    tooMany (a, b) = tooMany (a + b)
我认为我的代码是正确的,但我不知道如何编写表达式来测试它。对于TooMany Int,我只是在REPL中编写了TooMany(10::Int),就是这样,我得到了一个布尔答案


有人能给我一个提示吗?

根据错误消息,您似乎不仅在使用
FlexibleInstances
,甚至在使用
OverlappingInstances
。正如duplode所指出的,为了使用元组调用方法,您必须显式地修复精确的参数类型。在选择实例之前,GHC必须能够看到元组既不是
(Int,String)
也不是
(Int,Int)
,并且组件具有相同的类型


通常不鼓励使用这种特殊类,而更强烈地反对使用这种完全疯狂的实例。一般来说,类实例应该是相当统一的。如果元组需要多个实例,那么可能是做错了什么。重叠实例本质上是用于类型定向元编程的。我个人像躲避瘟疫一样躲避它们;如果您要使用它们,您需要非常非常小心您正在做的事情,并认识到在某些情况下,产生的API可能会以意外的方式运行。有一些技术可以使用类型族来避免重叠,这些类型族使复杂的实例结构不那么杂乱,但即使是那些类型族也需要非常小心地进行设计,并且会限制您以后的工作。

根据错误消息,似乎您不仅使用了
FlexibleInstances
,甚至还使用了
OverlappingInstances
。正如duplode所指出的,为了使用元组调用方法,您必须显式地修复精确的参数类型。在选择实例之前,GHC必须能够看到元组既不是
(Int,String)
也不是
(Int,Int)
,并且组件具有相同的类型


通常不鼓励使用这种特殊类,而更强烈地反对使用这种完全疯狂的实例。一般来说,类实例应该是相当统一的。如果元组需要多个实例,那么可能是做错了什么。重叠实例本质上是用于类型定向元编程的。我个人像躲避瘟疫一样躲避它们;如果您要使用它们,您需要非常非常小心您正在做的事情,并认识到在某些情况下,产生的API可能会以意外的方式运行。有一些技术可以使用类型族避免重叠,这些类型族可以使复杂的实例结构不那么杂乱,但即使是那些类型族也需要非常小心地进行设计,并且会限制您以后的工作。

首先,正如@dfeuer恰当地指出的,在“现实生活”中Haskell programming你不想用TypeClass来处理这类事情。编译器不会检查这些规则(尽管程序员通常会为它们编写代码)

有了这些,我假设这本书教你这些纯粹是为了让你熟悉类型类的机制(它们可以有引用类型类本身的约束),而不是支持这个类型类是一个特别好的类型

@duplode为您提供了在这种情况下要做什么的正确答案:注释您的类型

Main> tooMany (10 :: Int, 10 :: Int)
False
那么,为什么GHC不能自动找到
(10,10)
的正确实例,而需要类型注释呢?基本上,这是因为Haskell typeclass是开放的,这意味着任何人都可以在任何时候为你在另一个你不知道的模块中创建的typeclass创建一个新实例,例如,如果你把这个typeclass放在一个库中,该库的下游用户可以创建自己的这个typeclass实例

在本例中,正如您已经注意到的,数值文本在Haskell中实际上没有单态类型
10
属于
Num a=>a
类型,不是
Int
Integer
Double
或其他类似类型。由于类型类是开放的,即使您只为
TooMany
(即
Int
)定义了一个
Num
类型的单个实例,Haskell编译器也不能依靠它来推断
TooMany
实际上只有一个
Num
类型的实例,因此
(10,10)
必须具有类型
(Int,Int)
。使用您的代码的任何其他人都可以定义自己的
TooMany
实例,例如,
Double
s

特别是,模块的使用者可以为
TooMany
Num
创建一个新类型,其中包含退化类型实例

-- Imagine you published `TooMany` as a library on Hackage
-- and now a downstream consumer writes the following

-- This example happens to break the laws for Num, 
-- but the compiler doesn't know about typeclass laws
data DegenerateType = DegenerateType

instance Num DegenerateType where
    _ + _ = DegenerateType
    _ * _ = DegenerateType
    negate _ = DegenerateType
    abs _ = DegenerateType
    signum _ = 0
    -- The next line dictates what a numeric literal 
    -- actually means for a DegenerateType
    fromInteger _ = DegenerateType

instance TooMany DegenerateType where
    tooMany _ = True
现在,
tooMany(10,10)
根据单态类型的不同有不同的行为

Main> tooMany (10 :: DegenerateType, 10 :: DegenerateType)
True

因此,
tooMany(10,10)
不足以指定
tooMany
的行为,您必须使用类型注释。

首先,正如@dfeuer恰当地指出的,在“现实生活”的Haskell编程中,您不希望将类型类用于此类内容。编译器不会检查这些规则(尽管程序员通常会为它们编写代码)

有了这些,我假设这本书教你这些纯粹是为了让你熟悉类型类的机制(它们可以有引用类型类本身的约束),而不是支持这个类型类是一个特别好的类型

@duplode为您提供了在这种情况下要做什么的正确答案:注释您的类型

Main> tooMany (10 :: Int, 10 :: Int)
False
那么为什么GHC不能自动生成f