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

M. *_*ong 4 haskell types typechecking

为什么不进行类型检查extractEither

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

extractEither :: MyEither a b -> c
extractEither (MyLeft p) = p
Run Code Online (Sandbox Code Playgroud)

编译器显示:

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
Run Code Online (Sandbox Code Playgroud)

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

lef*_*out 12

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

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

也许你想要的只是简单

extractEither :: MyEither a a -> a
extractEither (MyLeft p) = p
extractEither (MyRight p) = p
Run Code Online (Sandbox Code Playgroud)

这要求两个"边"上的类型是相同的.


Ric*_* T. 11

基本上,你的类型的签名说,你的函数可以返回任何类型的值c给出MyEither a b,再次,每一个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
Run Code Online (Sandbox Code Playgroud)

编辑:又见dblhelix的回答对另一种可能的重构来,可能是有用的建议几乎得到你要找的类型签名.


Ste*_*ans 8

除了Riccardo的答案之外:如果你想要一个能够c从一个MyEither值中提取某种类型值的函数,无论它是由MyLeftor 构建还是MyRight,你可以,而不是要求该值具有类型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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 值得注意的是,它的类型对应于逻辑析取的消除规则:"如果是的话,如果是`a`然后是`c`,那么如果是的话,如果`b`然后是`c`,那么如果` a`或`b`然后`c`." 具有终止功能的通用类型对应于逻辑重言式.与问题中的原始类型相比:"如果`a`或`b`,那么`c`"(例如,"如果乔治华盛顿是美国的第一任总统或斯大林格勒战役始于1942年,那么拿破仑波拿巴拍摄John F. Kennedy"). (2认同)