Elm 中的自定义类型表示一组固定数量的具名情况。每个值都恰好对应其中一种具名情况。
自定义类型使用type关键字定义,需要的语法非常少。它是 Elm 编程中最重要的技术之一,用来让代码中可能出现的值与现实世界中合法的值完全吻合,从而不给无效数据留下任何空间,也让不可能的状态无法在代码中表示。
type Season
= Spring
| Summer
| Autumn
| Winter
自定义类型的每个情况都可以关联数据,也可以不关联;不同的情况可以关联不同类型的数据。如果所有情况都没有关联数据,这种可辨识联合就类似于其他语言通常所说的_枚举_(或 enum)。
type FlexibleNumber
= Integer Int
| Float Float
| Invalid
为特定的情况创建值,可以直接引用它的名字(Spring),前提是该类型定义在同一个模块中,或者已通过import SeasonModule exposing (Season(..))这种语法导入;也可以通过它的完全限定名(Season.Spring)来引用,前提是已通过import SeasonModule exposing (Season)这种语法导入。
integerTwo = Integer 2
invalid = FlexibleNumber.Invalid
自定义类型和 Elm 中的其他一切一样,都具有_结构相等性_,也就是说,同一个情况、且(可选的)数据也相同的两个值是等价的。
处理自定义类型的首选方式是模式匹配:
let describe flexibleNumber =
case flexibleNumber of
Integer i ->
"Integer: " ++ fromInt(i)
Float f ->
"Float: " ++ fromFloat(f)
Invalid ->
"Invalid"
在这个练习中,今天是情人节,你和你的伴侣打算一起做点开心的事。你的伴侣有很多想法,现在请你给这些想法打分,好挑出要参加的活动。
你的伴侣提出了以下想法:
你有 6 个任务,帮助你选出情人节的活动。
对于伴侣提出的每个想法,你用 3 个选项之一来回应:是、否或也许。
定义Approval自定义类型,用以下 3 种情况表示这些选项:Yes、No 和 Maybe。
你的伴侣挑出了 2 家可能的餐厅:一家做韩国料理,另一家做土耳其料理。
定义Cuisine自定义类型,用以下 2 种情况表示这些菜系:Korean 和 Turkish。
可供选择的电影非常多,为了缩小范围,你的伴侣还列出了电影的题材。
定义Genre自定义类型,用以下情况表示这些题材:Crime、Horror、Romance 和 Thriller。
如前所述,你的伴侣想出了 5 种不同的活动:玩桌游、放松一下、看场电影和去餐厅吃饭。
定义Activity自定义类型来表示这些活动类型:
BoardGame:没有关联数据。Chill:没有关联数据。Movie:带有Genre作为关联数据。Restaurant:带有Cuisine作为关联数据。最后,你可以给伴侣的想法打分了。你对伴侣想法的感觉是这样的:
实现一个名为rateActivity的函数,它接收一个Activity值,并根据上面的想法返回对应的Approval:
rateActivity (Restaurant Turkish)
-- => Maybe