为什么没有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'通常不足以捕捉任何类型吗?
答案 0 :(得分:12)
没有。 c
是调用者希望函数返回的任何类型,即对于函数f :: a -> c
,总是需要可以写{ {1}}以及f x + 1 :: Int
以及putStr $ f x
。你的功能当然不允许。
您想要的是返回动态类型。 Haskell故意不像其他语言那样容易这样做,因为当返回的类型是意外的时候,它显然会导致运行时错误。有各种方法可以做到这一点,但哪个是正确的,取决于你真正想要的是什么。你能给出一些背景吗?您的问题的最佳解决方案可能不使用动态类型,但更多的是Haskell-idiomatic。
也许你想要的只是
main = f x
要求两个“边”上的类型相同。
答案 1 :(得分:11)
基本上,您的类型签名表示,对于每个c
和MyEither a b
,您的函数可能会返回a
的任何类型的值b
。但是,正如编译器指出的那样,这里不可能生成任何此类c
,因为您实际上返回的是a
类型的值(p :: a
)。
此外,您的定义仍然无法管理MyEither a b
不是Left a
的情况。它应该怎么做,例如,当你致电extractEither (MyRight 1)
时?如果您尝试运行这样的函数(当然在更正类型签名之后),您可能会发生所谓的非详尽模式异常,这意味着{{1}没有正文定义处理一些可能的输入模式。
如果您正在尝试编写一个同时用于提取extractEither
和MyLeft
值的函数,我担心您应该改变主意。为了提取任何,左或右,你应该在两边都有相同的类型(即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
现在需要另外两个参数f
和g
:用于将提取的值转换为所需类型值的函数。
使用和类型时,这是一种非常常见且有用的习惯用法。
答案 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中反过来说:你的函数的调用者可以选择用于a
,b
和c
的类型,他们可以选择不兼容的a
和c
。如果我选择任何a
≠c
,您就无法编写任何终止代码,以便将我a
的任意选择变为我c
的任意选择。