当我们在Elm中定义带有类型变量的类型别名时,会发生什么

问题描述 投票:1回答:2

假设我们定义一个类型别名,说Message为:

type alias Message a =
{ code : String
, body : a
}

然后将函数readMessage定义为:

readMessage : Message () -> String
readMessage message =
...

上面的例子来自Elm教程和书中说:

此函数接收带有空体的Message。这与任何值都不一样,只是一个空值。

有人可以详细说明在上面的场景中发生了什么,以及编译器如何处理它。

elm
2个回答
2
投票

除非你真的想看到它的内部编译器表示,否则我认为这里重要的是any valueempty value之间的区别。

Message a是一个带参数的参数化类型。您可以将其作为模板阅读,例如只要小写a出现在Message的定义中,它将被替换为具体类型(String,Int等)。

所以这就是函数应该是什么样子,如果我们想要它与Message身体采取String

readMessage : Message String -> String
readMessage message =

这里发生的是body场的类型不再是a而是StringaString代替):

{ code : String
, body : String
}

在Elm中没有任何东西(aka voidunit)的值被编码为()。这就是为什么具有空体值的Message看起来像这样:

{ code : String
, body : ()
}

但是,当我们根本不关心身体价值时,我们可以采取Messageany值:

readMessage : Message a -> String
readMessage message =

小写的a可以是任何小写字符串,我们可以使它更像这样:

readMessage : Message any -> String
readMessage message =

但后来我们无法真正阅读邮件正文,因为我们不知道它的类型(因此我们不知道如何阅读它)。

希望有所帮助。


2
投票

类型Message ()是以下记录的别名:

{ code : String
, body : ()
}

其中()类型表示没有任何项目的元组(也称为null元组)。这种类型只有一个值,它也写成()

现在,当我们想要省略记录中的某个字段时,我们不能只指定它 - 这会使编译器正确生气,另请参阅The Billion Dollar Mistake。我们需要告诉编译器可以省略该值。

我们可以做的一种方法是使用Maybe类型但是如果我们制作一个消息列表,允许我们将主体包含在某些消息中并在其他消息中省略它。这可能不是我们想要的。

另一种方法是在问题中进行参数化Message类型。这将允许我们在阅读消息时与String身体发出消息,并在我们对身体不感兴趣时​​使用不同的身体类型。

在这种情况下,我们需要考虑身体类型应该是什么。虽然我们可以将空Strings用于带有遗漏体的消息,但它们很容易与具有空体的消息混淆。我们也可以使用Bool但我们需要决定是否要使用TrueFalse作为省略值。最后,我们可以使用null元组;因为它只有一个可能的值,所以它对我们来说是理想的。

实际上还有一种可能性:我们可以创建一个type alias MessageWithoutBody = { code: String }。在某些情况下(特别是当您需要省略更多字段时),它更干净,但由于您需要复制要保留的所有字段,因此可能更加冗长。

© www.soinside.com 2019 - 2024. All rights reserved.