注意:在 Elm 中,自定义类型通常被称为“并集类型”。其他社群中的名称包括 标记并集ADT

自定义类型

到目前为止,我们已经看到了很多像 BoolIntString 之类的类型。但是我们如何定义自己的类型呢?

假设我们正在制作一个聊天室。每个人都需要一个名字,但是可能有些用户没有永久帐户。他们每次出现时只需提供一个名字就可以了。

我们可以通过定义一个 UserStatus 类型来描述这种情况,列出所有可能的变化

type UserStatus = Regular | Visitor

UserStatus 类型有两个变体。某人可以是 RegularVisitor。因此,我们可以将用户表示为这样的记录

type UserStatus
  = Regular
  | Visitor

type alias User =
  { status : UserStatus
  , name : String
  }

thomas = { status = Regular, name = "Thomas" }
kate95 = { status = Visitor, name = "kate95" }

现在我们就可以跟踪某人是带有帐户的 Regular,还是只是路过的 Visitor 了。这不难,但我们可以让它更简单!

我们可以仅用一个自定义类型来表示所有这些内容,而无需创建自定义类型和类型别名。RegularVisitor 变体各有一个关联数据。在我们的例子中,关联数据是一个 String

type User
  = Regular String
  | Visitor String

thomas = Regular "Thomas"
kate95 = Visitor "kate95"

数据直接附加到变体,因此不再需要记录了。

这种方法的另一个好处是每个变体都可以有不同的关联数据。假设 Regular 用户在注册时提供了他们的年龄。使用记录无法很好地捕获这一点,但是当您定义自己的自定义类型时,这不成问题。让我们在交互式示例中向 Regular 变体添加一些关联数据

[{“add-type”: “访客”, “input”: “访客 字符串\n | 非访客 字符串 Int\n”},{“input”: “非访客”, “value”: "\u001b[36m<function>\u001b[0m", “type_”: "String -> Int -> 访客"},{“input”: “访客”, “value”: "\u001b[36m<function>\u001b[0m", “type_”: "String -> 访客"},{“input”: “非访客 “Thomas” 44”, “value”: "\u001b[96m非访客\u001b[0m \u001b[93m“Thomas”\u001b[0m \u001b[95m44\u001b[0m", “type_”: “访客”},{“input”: “访客 “kate95””, “value”: "\u001b[96m访客\u001b[0m \u001b[93m“kate95”\u001b[0m", “type_”: “访客”}]

尝试使用名称和年龄定义一个非访客访客 ⬆️

我们只是添加了一个年龄,但类型的变量可能会大大不同。例如,我们可能想为非访客用户添加位置,以便我们可以建议区域聊天室。添加更多相关数据!或者我们可能想拥有匿名用户。添加一个名为匿名的第三个变量。我们可能会最终得到

type User
  = Regular String Int Location
  | Visitor String
  | Anonymous

没问题!现在让我们看一些其他示例。

消息

在架构部分,我们看到了几个定义消息类型的示例。这种类型的类型在 Elm 中非常常见。在我们的聊天室中,我们可以像这样定义一个消息类型

type Msg
  = PressedEnter
  | ChangedDraft String
  | ReceivedMessage { user : User, message : String }
  | ClickedExit

我们有四个变量。一些变量没有关联数据,而另一些变量有一堆。请注意,接收消息实际上有一个记录作为关联数据。这完全没问题。任何类型都可以成为关联数据!这允许您非常精确地描述应用程序中的交互。

建模

当你开始非常精确地建模情况时,自定义类型将变得非常强大。例如,如果您正在等待某些数据加载,您可能希望使用自定义类型对其进行建模,如下所示

type Profile
  = Failure
  | Loading
  | Success { name : String, description : String }

因此,你可以从加载状态开始,然后根据发生的情况转换为失败成功。这使得编写一个在数据加载时始终显示合理内容的视图函数变得非常简单。

现在我们知道了如何创建自定义类型,下一节将展示如何使用它们!

注意:自定义类型是 Elm 中最重要的特性。它们有很大的深度,特别是当你习惯试着更精确地建模场景的时候。我试图在附录中的类型作为集合类型作为比特位中分享一些这种深度。我希望你发现它们有帮助!

结果匹配“

    没有结果匹配“