2012-07-17 97 views
4

为什么不类型检查?为什么没有这个函数签名类型检查?

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”一般足以捕捉任何类型的?

回答

12

c任何类型的主叫方可能希望函数返回,即一个函数f :: a -> c,将总是需要可以写f x + 1 :: Int以及putStr $ f x以及main = f x。你的功能当然不允许。

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

也许你想要的是简单

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

这就要求在两个“面”的类型是相同的。

+0

谢谢。你们两个的答案都很清楚,解决了我的难题! – 2012-07-17 08:17:45

+0

@ M.Tong:不要忘记将其中的一个标记为接受的答案。 – 2012-07-17 08:28:34

+0

其实这个问题并不是以动态类型为前提的,而只是子类型和upcasts,可以静态检查哪个AFAIK。虽然Haskell没有这些。 – 2012-07-17 17:10:37

11

基本上,你的类型的签名说,你的函数可以返回给定MyEither a b,再次任何类型c的值,为每一位ab。然而,正如编译器指出的那样,这里不可能产生任何这样的c,因为你实际上返回了一个类型为a(即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的回答对另一种可能的重构建议这可能对几乎有用获取您正在查找的类型签名。

8

除了里卡多的回答是:如果你想有一个功能是能够从MyEither值提取一些类型c的值,不管它是否被MyLeftMyRight建,你可以,而不是要求的值已键入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:其用于采取所提取的功能值转换为所需t的值YPE。

这是使用总和类型时非常常见和有用的习惯用法。

+0

我要在我的答案中添加一行以引用您的答案。 +1 :) – 2012-07-17 12:07:17

+0

谢谢。但在这种情况下,我必须指定f和g,对吗?有没有一种方法可以只指定其中的一个,并说出“我不在乎别人是什么”之类的东西 - 在模式匹配中类似'_'。 – 2012-07-17 13:48:50

+0

是的,你可以:'提取未定义的id(MyRight 3.14)',但是最好确定你没有提供用'MyLeft'构建的值。 ;) – 2012-07-17 14:16:48

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,就像blah的作者得到选择的运行时类型结果。但在Haskell中,情况恰恰相反:你的函数的调用者可以选择用于a,bc的类型,并且他们可以选择不兼容的ac。如果我选择任何ac,则没有终止的代码片段可以将我的任意选择a转换为我的任意选择c

相关问题