无法将预期类型'b1'与实际类型'b'匹配HASKELL

1cklez4t  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(118)

我试着写一个简单的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

zsbz8rwp

zsbz8rwp1#

看起来,你是在像OOP程序员一样思考。
让我们看看下面的函数:

foo :: a -> b

它的基本功能呢它有能力将任何类型的任何值“转换”为另一种类型的另一个值(可能相同)。这似乎很荒谬,它不能这样做。你甚至不能像这样实现它:

foo x = x

因为这意味着结果类型必须总是与参数类型相同,但函数类型承诺不同的东西,结果类型可以是任何类型,并且与参数类型不同。
在Haskell中,“使用”函数的上下文很重要。在一个上下文中,参数类型和值类型将是一个,在另一个上下文中,它们可能不同。TypeChecker保证了一个函数可以在某些上下文中“使用”,否则你会得到类型错误。
基本上,foo :: a -> b有两个由编译器本身应用的参数。这两个参数是a的类型和b的类型。所以,foo的“完整”类型是foo :: Λa.Λb. a -> b(它是伪代码),有时也写foo :: forall a b . a -> b。从上下文中调用提供了这两种类型。此外,在 GHC 中,您有扩展 TypeApplications,它允许您手动提供类型。
你可以说,好吧,但是如果我提供类型类约束,像这样:

foo :: (Num a, Num b) => a -> b

我能像这样实现foo x = x吗?约束说函数参数和它的结果是一个数字,那么为什么我不能直接返回它呢?
不可以,因为约束只会限制可能的参数类型和函数值(它们应该是Num类型类的示例),但它们仍然可以不同。
Haskell中没有类型继承。类型类与OOP中的类是不同的。在Haskell中,你可以用记录来代替使用类型类。让我们声明类型为Num的替代类。

data NumCls a = NumCls
  { numAdd :: a -> a -> a
  , numMul :: a -> a -> a
  , ...
  }

numClsInt :: NumCls Int
numClsInt = NumCls { ... }

让我们以两种方式定义square函数:

square1 :: Num a => a -> a
square1 x = x * x

square2 :: NumCls a -> a -> a
square2 (NumCls{numMul}) x = x `numMul` x

在编译器中是阶段 desugar,其中类型类声明转换为记录(如NumCls),类型类示例声明转换为记录定义(如numClsInt),像square1这样的函数转换为像square2这样的函数,并从函数使用的上下文应用正确的记录。这基本上就是Haskell中类型类的工作方式,没有任何魔法。

相关问题