在榆树,当我想不通type的是适当的关键字对type alias.文档似乎没有对此进行解释,也无法在发行说明中找到.这是在某处记录的吗?
rob*_*oby 133
我怎么想呢:
type 用于定义新的联合类型:
type Thing = Something | SomethingElse
Run Code Online (Sandbox Code Playgroud)
这个定义之前Something,并SomethingElse没有任何意义.现在它们都是Thing我们刚刚定义的类型.
type alias 用于为已存在的其他类型指定名称:
type alias Location = { lat:Int, long:Int }
Run Code Online (Sandbox Code Playgroud)
{ lat = 5, long = 10 }有类型{ lat:Int, long:Int },它已经是一个有效的类型.但现在我们也可以说它有类型,Location因为它是同一类型的别名.
值得注意的是,以下将编译得很好并显示"thing".即使我们指定thing是a String并且aliasedStringIdentity需要a AliasedString,我们也不会得到String/ 之间存在类型不匹配的错误AliasedString:
import Graphics.Element exposing (show)
type alias AliasedString = String
aliasedStringIdentity: AliasedString -> AliasedString
aliasedStringIdentity s = s
thing : String
thing = "thing"
main =
show <| aliasedStringIdentity thing
Run Code Online (Sandbox Code Playgroud)
关键是这个词alias.在编程的过程中,当你想,属于一组的东西,你把它放在一个记录,就像在一个点的情况下,
{ x = 5, y = 4 }
Run Code Online (Sandbox Code Playgroud)
或学生记录.
{ name = "Billy Bob", grade = 10, classof = 1998 }
Run Code Online (Sandbox Code Playgroud)
现在,如果您需要传递这些记录,则必须拼出整个类型,例如:
add : { x:Int, y:Int } -> { x:Int, y:Int } -> { x:Int, y:Int }
add a b =
{ a.x + b.x, a.y + b.y }
Run Code Online (Sandbox Code Playgroud)
如果您可以为一个点添加别名,那么签名将更容易编写!
type alias Point = { x:Int, y:Int }
add : Point -> Point -> Point
add a b =
{ a.x + b.x, a.y + b.y }
Run Code Online (Sandbox Code Playgroud)
所以别名是其他东西的简写.在这里,它是记录类型的简写.您可以将其视为为您将经常使用的记录类型命名.这就是为什么它被称为别名 - 它是裸记录类型的另一个名称{ x:Int, y:Int }
而type解决了一个不同的问题.如果您来自OOP,那么您使用继承,运算符重载等解决问题 - 有时,您希望将数据视为通用事物,有时您希望将其视为特定事物.
这种情况发生的常见情况是传递信息 - 比如邮政系统.当您发送信件时,您希望邮政系统将所有邮件视为同一件事,因此您只需设计一次邮政系统.此外,路由消息的工作应该独立于其中包含的消息.只有在信件到达目的地时,您才关心信息是什么.
以同样的方式,我们可以将a定义type为可能发生的所有不同类型消息的并集.假设我们正在实施大学生与父母之间的信息系统.因此,大学生只能发送两条消息:"我需要啤酒钱"和"我需要内裤".
type MessageHome = NeedBeerMoney | NeedUnderpants
Run Code Online (Sandbox Code Playgroud)
所以现在,当我们设计路由系统时,我们的函数类型可以传递MessageHome,而不是担心它可能是所有不同类型的消息.路由系统不关心.它只需要知道它是一个MessageHome.只有当消息到达其目的地(父母的家)时,您才需要弄清楚它是什么.
case message of
NeedBeerMoney ->
sayNo()
NeedUnderpants ->
sendUnderpants(3)
Run Code Online (Sandbox Code Playgroud)
如果您了解Elm体系结构,则更新函数是一个巨大的case语句,因为它是消息路由的目的地,因此被处理.我们在传递消息时使用union类型来处理一个类型,但是然后可以使用case语句来确切地说明它是什么消息,所以我们可以处理它.
让我通过关注用例并提供有关构造函数和模块的一些上下文来补充之前的答案。
type alias为记录创建别名和构造函数
这是最常见的用例:您可以为特定类型的记录格式定义备用名称和构造函数。
type alias Person =
{ name : String
, age : Int
}
Run Code Online (Sandbox Code Playgroud)
定义类型别名自动意味着以下构造函数(伪代码):
Person : String -> Int -> { name : String, age : Int }
这可以派上用场,例如当您想编写 Json 解码器时。
personDecoder : Json.Decode.Decoder Person
personDecoder =
Json.Decode.map2 Person
(Json.Decode.field "name" Json.Decode.String)
(Json.Decode.field "age" Int)
Run Code Online (Sandbox Code Playgroud)
指定必填字段
他们有时称其为“可扩展记录”,这可能会产生误导。此语法可用于指定您期望某些包含特定字段的记录。如:
type alias NamedThing x =
{ x
| name : String
}
showName : NamedThing x -> Html msg
showName thing =
Html.text thing.name
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用上面的函数(例如在你的视图中):
let
joe = { name = "Joe", age = 34 }
in
showName joe
Run Code Online (Sandbox Code Playgroud)
理查德·费尔德曼 (Richard Feldman) 在 ElmEurope 2017 上的演讲可能会进一步了解这种风格何时值得使用。
重命名内容
您可以这样做,因为新名称可以在您的代码稍后提供额外的含义,就像在这个例子中一样
type alias Person =
{ name : String
, age : Int
}
Run Code Online (Sandbox Code Playgroud)
从不同的模块重新公开类型
如果您正在编写一个包(而不是应用程序),您可能需要在一个模块中实现一个类型,也许在一个内部(未公开)模块中,但您希望从一个不同的(公共)模块。或者,您希望从多个模块公开您的类型。
Taskin core 和Http.Request in Http是第一个示例,而Json.Encode.Value和Json.Decode.Value对是后者的示例。
只有当您想要保持类型不透明时才能这样做:您不公开构造函数。有关详细信息,请参阅type下面的用法。
值得注意的是,在上面的例子中,只有#1 提供了一个构造函数。如果在 #1 中公开类型别名module Data exposing (Person),则将公开类型名称以及构造函数。
type定义一个带标签的联合类型
这是最常见的用例,一个很好的例子是Maybecore 中的类型:
type Maybe a
= Just a
| Nothing
Run Code Online (Sandbox Code Playgroud)
当你定义一个类型时,你也定义了它的构造函数。在可能的情况下,这些是(伪代码):
Just : a -> Maybe a
Nothing : Maybe a
Run Code Online (Sandbox Code Playgroud)
这意味着,如果您声明此值:
mayHaveANumber : Maybe Int
Run Code Online (Sandbox Code Playgroud)
您可以通过以下任一方式创建它
mayHaveANumber = Nothing
Run Code Online (Sandbox Code Playgroud)
或者
mayHaveANumber = Just 5
Run Code Online (Sandbox Code Playgroud)
在Just和Nothing标签不仅可以作为构造函数,它们也可以作为在析构函数或图案case的表达。这意味着使用这些模式,您可以在 a 中看到Maybe:
showValue : Maybe Int -> Html msg
showValue mayHaveANumber =
case mayHaveANumber of
Nothing ->
Html.text "N/A"
Just number ->
Html.text (toString number)
Run Code Online (Sandbox Code Playgroud)
您可以这样做,因为 Maybe 模块定义为
module Maybe exposing
( Maybe(Just,Nothing)
Run Code Online (Sandbox Code Playgroud)
也可以说
module Maybe exposing
( Maybe(..)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,两者是等价的,但在 Elm 中,明确被认为是一种优点,尤其是在编写包时。
隐藏实现细节
正如上面所指出的,它的构造函数Maybe对其他模块可见是一个有意的选择。
然而,还有其他情况,当作者决定隐藏它们时。核心中的一个例子是Dict. 作为包的消费者,你不应该看到后面红/黑树算法的实现细节,Dict直接弄乱节点。隐藏构造函数会强制您的模块/包的使用者仅通过您公开的函数创建您的类型的值(然后转换这些值)。
这就是为什么有时像这样的东西出现在代码中的原因
type Person =
Person { name : String, age : Int }
Run Code Online (Sandbox Code Playgroud)
与本文开头的type alias定义不同,这种语法创建了一个只有一个构造函数的新“联合”类型,但该构造函数可以对其他模块/包隐藏。
如果类型是这样暴露的:
module Data exposing (Person)
Run Code Online (Sandbox Code Playgroud)
只有Data模块中的代码可以创建 Person 值,并且只有该代码可以对其进行模式匹配。