为什么这个功能没有签名类型检查?

时间:2012-07-17 08:03:21

标签: haskell types typechecking

为什么没有extractEither类型检查?

data MyEither a b = MyLeft a | MyRight b
                    deriving (Read, Show)

extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p

编译器显示:

Couldn't match type `a' with `c'
  `a' is a rigid type variable bound by
      the type signature for extractEither :: MyEither a b -> c
      at /Users/tongmuchenxuan/playground/test.hs:5:1
  `c' is a rigid type variable bound by
      the type signature for extractEither :: MyEither a b -> c
      at /Users/tongmuchenxuan/playground/test.hs:5:1
In the expression: p
In an equation for `extractEither': extractEither (MyLeft p) = p

不是'c'通常不足以捕捉任何类型吗?

4 个答案:

答案 0 :(得分:12)

没有。 c调用者希望函数返回的任何类型,即对于函数f :: a -> c总是需要可以写{ {1}}以及f x + 1 :: Int以及putStr $ f x。你的功能当然不允许。

您想要的是返回动态类型。 Haskell故意不像其他语言那样容易这样做,因为当返回的类型是意外的时候,它显然会导致运行时错误。有各种方法可以做到这一点,但哪个是正确的,取决于你真正想要的是什么。你能给出一些背景吗?您的问题的最佳解决方案可能使用动态类型,但更多的是Haskell-idiomatic。

也许你想要的只是

main = f x

要求两个“边”上的类型相同。

答案 1 :(得分:11)

基本上,您的类型签名表示,对于每个cMyEither a b,您的函数可能会返回a的任何类型的值b。但是,正如编译器指出的那样,这里不可能生成任何此类c,因为您实际上返回的是a类型的值(p :: a)。

此外,您的定义仍然无法管理MyEither a b不是Left a的情况。它应该怎么做,例如,当你致电extractEither (MyRight 1)时?如果您尝试运行这样的函数(当然在更正类型签名之后),您可能会发生所谓的非详尽模式异常,这意味着{{1}没有正文定义处理一些可能的输入模式。

如果您正在尝试编写一个同时用于提取extractEitherMyLeft值的函数,我担心您应该改变主意。为了提取任何,左或右,你应该在两边都有相同的类型(即MyRight),这是没有用的。

您应该考虑使用这些函数来提取值。在这里,我返回MyEither a a以避免管理对Maybe的调用的负担,例如当您尝试从正确的值中提取左侧时:

error

编辑另请参阅dblhelix的回答,了解有关其他可能重构的建议,这些重构可能对几乎获取您正在寻找的类型签名非常有用。

答案 2 :(得分:8)

除了Riccardo的回答:如果你想要一个能够从c值中提取某种类型MyEither的值的函数,无论它是由MyLeft构建的还是MyRight,您可以,而不是要求值具有类型MyEither c c,“指示”关于如何从任一方获取c类型值的函数:

extractEither :: (a -> c) -> (b -> c) -> MyEither a b -> c
extractEither f g (MyLeft x)  = f x
extractEither f g (MyRight y) = g y

也就是说,extractEither现在需要另外两个参数fg:用于将提取的值转换为所需类型值的函数。

使用和类型时,这是一种非常常见且有用的习惯用法。

答案 3 :(得分:1)

extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p
  

Isn' t'一般到足以捕捉任何类型?

您带到桌面的直觉在面向对象的语言或其他语言中使用子类型,但在Haskell或其他参数化打字系统(例如,Java泛型)中则不行。在Java中使用此方法签名:

public Object blah(Foo whatever);

此方法返回Object,这是层次结构中的最高类型。这意味着此方法的实现可以选择返回它想要的任何类型,并且可以简单地向上转换为Object

Haskell类型不会那样工作。您认为您的extractFilter可以返回p,因为您隐含地假设extractEither的作者,选择要用于c的类型,就像blah的作者选择结果的运行时类型一样。但是在Haskell中反过来说:你的函数的调用者可以选择用于abc的类型,他们可以选择不兼容的ac。如果我选择任何ac,您就无法编写任何终止代码,以便将我a的任意选择变为我c的任意选择。