为什么在Haskell中不进行这种类型检查?

时间:2019-05-07 05:55:58

标签: haskell generics types

这不会键入检查:

module DoesntTypeCheck where {
import Prelude(Either(..));
defaultEither :: a -> Either b c -> Either a c;
defaultEither a (Left _) = Left a;
defaultEither _ b = b;
}

但是这样做:

module DoesTypeCheck where {
import Prelude(Either(..));
defaultEither :: a -> Either b c -> Either a c;
defaultEither a (Left _) = Left a;
defaultEither _ (Right b) = Right b;
}

编译器可能有错误,Either a c类型只能是Left (x::a)Right (y::c),如果不是Left,则是Right,我们知道Right :: b -> Either a b也是Right (y::c) :: Either a c

4 个答案:

答案 0 :(得分:9)

问题是,当你说

defaultEither _ b = b

您是说输出值b与第二个输入值相同。只有这些值具有相同的类型,才有可能。但是您已经告诉编译器,输入的类型为Either b c,而输出的类型为Either a c。这些是不同的类型,所以难怪编译器会抱怨。

我了解您正在尝试做的事情-但是即使一个值Right x(类型为x的{​​{1}}可以是c类型的任何Either d c的类型签名都会将输入和输出值限制为具有不同d的版本。这意味着您不能使用相同的变量来引用两个值。

答案 1 :(得分:5)

让我们尝试一个更简单的示例:

data Foo x = Bar

foobar :: Foo a -> Foo b
foobar f = f

看看Foo的定义。左侧有一个类型变量(x),它实际上从未出现在右侧的任何地方。这是所谓的“幻像类型变量”的示例。类型签名中有一个类型实际上与实际值中的任何类型都不对应。 (顺便说一下,这是完全合法的。)

现在,如果您有表达式Just True,则从True :: Bool开始,然后是Just True :: Maybe True。但是,表达式Nothing绝对是Maybe 某物。但是,在没有实际价值存在的情况下,没有什么可以强迫它成为任何特定的可能类型。在这种情况下,类型变量是幻影。

我们这里有类似的事情; Bar :: Foo x,用于任何x。因此,您会认为我们对foobar的定义是合法的。

然后您会错误

即使期望值Foo a的类型为Foo b,也不能传递Foo Int的值,即使它们具有完全相同的运行时结构。因为类型检查器不关心运行时结构;它只关心类型。就类型检查器而言,Foo Boolfoobar :: Foo a -> Foo b foobar Bar = Bar 不同,即使在运行时没有明显的区别。

是您的代码被拒绝的原因。

事实上,你必须写

Bar

让类型检查器知道您输出的Bar是一个新的 Foo Int,而不是您收到的输入(因此它可以具有另一种类型。

信不信由你,这实际上是一个功能,而不是一个错误。您可以编写使之生效的代码,以使Foo Char的行为不同于Bar。即使在运行时它们都只是b

您已经发现,解决方案是从Right中取出ValueError: Error when checking target: expected dropout_5 to have shape (33,) but got array with shape (1,). 的值,然后立即重新输入。看起来毫无意义而且很愚蠢,但是它是要明确通知类型检查器类型可能已更改。这也许很烦人,但这只是该语言的那些角落之一。这不是一个错误,它是专门设计用于这种方式的。

答案 2 :(得分:3)

  

一个c类型只能是...

确实,但是在您的第一个示例中,值fatal: bad config line 30 in file [path]/.gitconfig 的类型不是b!正如您的类型签名所证明的,它具有类型Either a c。当然,您不能在期望Either b c的地方返回Either b c。相反,您必须对值进行解构并使用正确的类型对其进行重构。

答案 3 :(得分:0)

  

一个ac类型只能是Left(x :: a)或Right(y :: c),如果不是Left则它是Right,我们知道Right :: b-> ab都是右(y :: c)::要么是c。

我认为您正在将type构造函数与data构造函数混淆。 Eitherghc-base中的定义如下:

data  Either a b  =  Left a | Right b

Either是具有两个抽象变量的type构造函数。即采用两种类型(IntString等)。并构造一个像Either Int String这样的具体类型。

另一方面,

LeftRightdata的构造函数。它们采用1"hi"之类的实际值,并构造Left 1Right "hi"之类的值。

Prelude> :t Left
Left :: a -> Either a b
Prelude> :t Right
Right :: b -> Either a b

Haskell类型推断不能valuesLeftRight)一起使用。它仅适用于typesEither)。因此,类型检查器仅了解Either b cEither a c-因此,在第一种情况下,变量不匹配。