4

为什么不进行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

4 回答 4

12

不。是c调用者希望函数返回的任何类型,即对于一个函数f :: a -> c,它总是需要能够编写f x + 1 :: Int以及putStr $ f xmain = f x您的功能当然不允许。

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

也许你想要的只是

extractEither :: MyEither a a -> a
extractEither (MyLeft p) = p
extractEither (MyRight p) = p

这要求“两侧”的类型相同。

于 2012-07-17T08:08:49.530 回答
11

基本上,您的类型签名表示您的函数可以返回c给定 a的任何类型的值MyEither a b,再次,对于每个aand b。但是,正如编译器指出的那样,这里不可能产生任何这样的c东西,因为您实际上返回的是一个类型的值a(being p :: a)。

此外,您的定义仍然无法管理您MyEither a b不是Left a. 它应该做什么,例如,当你打电话时extractEither (MyRight 1)?如果您尝试运行这样的函数(当然是在更正类型签名之后),您可能会遇到所谓的非穷举模式异常,这意味着没有用于extractEither处理一些可能的输入模式的主体定义。

如果您尝试编写一个同时用于提取MyLeftMyRight值的函数,恐怕您应该改变主意。为了提取左侧或右侧的任何内容,您应该在两侧具有相同的类型(即MyEither a a),这不是那么有用。

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

extractMyLeft :: MyEither a b -> Maybe a
extractMyRight :: MyEither a b -> Maybe b

编辑:另请参阅 dblhelix 的答案,以获取有关另一种可能的重构的建议,该重构可能对几乎获得您正在寻找的类型签名很有用。

于 2012-07-17T08:09:00.260 回答
8

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

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: 用于将提取的值转换为所需类型的值的函数。

在使用 sum 类型时,这是一个非常常见且有用的习惯用法。

于 2012-07-17T10:58:06.223 回答
1
extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p

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

您提出的直觉适用于面向对象的语言或具有子类型的其他语言,但不适用于 Haskell 或其他参数类型系统(例如,Java 泛型)。在 Java 中采用这个方法签名:

public Object blah(Foo whatever);

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

Haskell 类型不是这样工作的。您认为您extractFilter可以返回p,因为您隐含地假设,作者extractEither,可以选择使用哪种类型c,就像 get 的作者blah选择结果的运行时类型一样。但在 Haskell 中,情况正好相反:函数的调用者可以选择用于a, band的类型c,并且他们可以选择不兼容的aand c。如果我选择任何ac,则没有可以编写的终止代码将我的任意选择a转换为任意选择c.

于 2012-07-17T17:08:29.133 回答