假设我们将类型别名Message
定义为:
type alias Message a =
{ code : String
, body : a
}
然后将函数readMessage
定义为:
readMessage : Message () -> String
readMessage message =
...
以上示例摘自Elm教程,书中写道:
此功能将带空正文的消息带入。这不一样 就像任何值一样,只是一个空值。
有人可以详细说明在上述情况下到底发生了什么以及编译器如何处理它。
答案 0 :(得分:2)
除非您真的希望看到内部的编译器表示形式,否则我认为这里重要的是any value
和empty value
之间的区别。
Message a
是带有1个参数的参数化类型。您可以将其作为模板阅读,例如在a
的定义中出现小写的Message
的任何地方,它将被具体类型(字符串,整数等)替换。
因此,如果我们希望函数采用Message
主体为String
的函数,则函数应该是这样:
readMessage : Message String -> String
readMessage message =
这里发生的是body
字段的类型不再是a
而是String
(a
被String
取代):< / p>
{ code : String
, body : String
}
榆木中的空值(又名void
或unit
)被编码为()
。这就是为什么Message
的主体值为空的原因:
{ code : String
, body : ()
}
但是,当我们根本不关心主体值时,可以将Message
的值设为any
:
readMessage : Message a -> String
readMessage message =
小写的a
可以是任何小写的字符串,我们可以像这样使它更具可读性:
readMessage : Message any -> String
readMessage message =
但是我们不能真正读取消息正文,因为我们不知道消息的类型(因此我们不知道如何读取消息)。
希望有帮助。
答案 1 :(得分:2)
类型Message ()
是以下记录的别名:
{ code : String
, body : ()
}
其中()
类型表示不包含任何项目的元组(也称为空元组)。这种类型只有一个值,并且也写为()
。
现在,当我们想省略记录中的某些字段时,我们不能只是不指定它-这会使编译器很不正确,请参见The Billion Dollar Mistake。我们需要告诉编译器该值可以省略。
一种可行的方法是使用Maybe
类型,但是如果我们列出了一条消息列表,该列表允许我们将主体包含在某些消息中而将其省略。这可能不是我们想要的。
另一种方法是像在问题中一样对Message
类型进行参数化。这样一来,当我们阅读邮件时,我们将收到String
条正文,而对正文不感兴趣的邮件将具有不同的正文类型。
在这种情况下,我们需要考虑应该是什么身体类型。尽管我们可以为正文省略的邮件使用空的String
,但它们很容易与正文为空的邮件混淆。我们也可以使用Bool
,但是接下来我们需要确定是否要对省略的值使用True
或False
。最后,我们可以使用 null元组;因为它只有一个可能的值,所以对我们来说是理想的。
实际上还有一种可能性:我们可以创建一个type alias MessageWithoutBody = { code: String }
。在某些情况下(尤其是当您需要省略更多字段时),这种方法比较干净。但是在您需要复制所有要保留的字段时,它可能更加冗长。