我试着写一个简单的haskell类:
class Funz a where
(+.) :: a -> a -> a
eval :: (Num b, Num c) => a -> b -> c
instance (Num d) => Funz (Constant d) where
Constant a +. Constant b = Constant $ a+b
eval (Constant a) b = a
然而,当我编译它说
app/basics.hs:18:27: error:
• Couldn't match expected type ‘c’ with actual type ‘d’
‘c’ is a rigid type variable bound by
the type signature for:
eval :: forall b c. (Num b, Num c) => Constant d -> b -> c
at app/basics.hs:18:5-8
‘d’ is a rigid type variable bound by
the instance declaration
at app/basics.hs:16:10-37
• In the expression: a
In an equation for ‘eval’: eval (Constant a) b = a
In the instance declaration for ‘Funz (Constant d)’
• Relevant bindings include
a :: d (bound at app/basics.hs:18:20)
eval :: Constant d -> b -> c (bound at app/basics.hs:18:5)
|
18 | eval (Constant a) b = a
| ^
即使a和b都应该是“Num”类型
而我正试图返回
编辑:更改名称是因为人们固执地继续说我认为两个变量是相同的,而这显然不是问题所在。
更新:将所有不明确的Num改为Double,因为似乎没有办法将任何Num转换为Double
1条答案
按热度按时间zsbz8rwp1#
看起来,你是在像OOP程序员一样思考。
让我们看看下面的函数:
它的基本功能呢它有能力将任何类型的任何值“转换”为另一种类型的另一个值(可能相同)。这似乎很荒谬,它不能这样做。你甚至不能像这样实现它:
因为这意味着结果类型必须总是与参数类型相同,但函数类型承诺不同的东西,结果类型可以是任何类型,并且与参数类型不同。
在Haskell中,“使用”函数的上下文很重要。在一个上下文中,参数类型和值类型将是一个,在另一个上下文中,它们可能不同。TypeChecker保证了一个函数可以在某些上下文中“使用”,否则你会得到类型错误。
基本上,
foo :: a -> b
有两个由编译器本身应用的参数。这两个参数是a
的类型和b
的类型。所以,foo
的“完整”类型是foo :: Λa.Λb. a -> b
(它是伪代码),有时也写foo :: forall a b . a -> b
。从上下文中调用提供了这两种类型。此外,在 GHC 中,您有扩展 TypeApplications,它允许您手动提供类型。你可以说,好吧,但是如果我提供类型类约束,像这样:
我能像这样实现
foo x = x
吗?约束说函数参数和它的结果是一个数字,那么为什么我不能直接返回它呢?不可以,因为约束只会限制可能的参数类型和函数值(它们应该是Num类型类的示例),但它们仍然可以不同。
Haskell中没有类型继承。类型类与OOP中的类是不同的。在Haskell中,你可以用记录来代替使用类型类。让我们声明类型为
Num
的替代类。让我们以两种方式定义
square
函数:在编译器中是阶段 desugar,其中类型类声明转换为记录(如
NumCls
),类型类示例声明转换为记录定义(如numClsInt
),像square1
这样的函数转换为像square2
这样的函数,并从函数使用的上下文应用正确的记录。这基本上就是Haskell中类型类的工作方式,没有任何魔法。