エルムでは、type
が適切なキーワードとtype alias
。ドキュメントにはこれについての説明がないようです。また、リリースノートで見つけることもできません。これはどこかに文書化されていますか?
私の考え:
type
は、新しい共用体タイプの定義に使用されます。
type Thing = Something | SomethingElse
この定義の前は、Something
とSomethingElse
は何の意味もありませんでした。現在、これらは両方ともタイプThing
であり、先ほど定義しました。
type alias
は、既に存在する他のタイプに名前を付けるために使用されます。
type alias Location = { lat:Int, long:Int }
{ lat = 5, long = 10 }
のタイプは{ lat:Int, long:Int }
、これは既に有効なタイプでした。しかし今では、Location
型を持っていると言うこともできます。これは、同じ型のエイリアスだからです。
次のコードが正常にコンパイルされ、"thing"
。 thing
がString
であり、aliasedStringIdentity
が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
キーはWord alias
です。プログラミングの過程で、一緒に属するものをグループ化する場合、ポイントの場合のように、それをレコードに入れます
{ x = 5, y = 4 }
または学生記録。
{ name = "Billy Bob", grade = 10, classof = 1998 }
ここで、これらのレコードを渡す必要がある場合は、次のようにタイプ全体を綴る必要があります。
add : { x:Int, y:Int } -> { x:Int, y:Int } -> { x:Int, y:Int }
add a b =
{ a.x + b.x, a.y + b.y }
ポイントをエイリアスできる場合、署名は非常に簡単に記述できます。
type alias Point = { x:Int, y:Int }
add : Point -> Point -> Point
add a b =
{ a.x + b.x, a.y + b.y }
したがって、エイリアスは他の何かの省略形です。ここでは、レコードタイプの省略形です。頻繁に使用するレコードタイプに名前を付けると考えることができます。それが別名と呼ばれる理由です。それは{ x:Int, y:Int }
で表されるネイキッドレコードタイプの別の名前です
一方、type
は別の問題を解決します。あなたがOOPから来ている場合、それは継承、演算子のオーバーロードなどで解決する問題です-時には、データを一般的なものとして扱いたい、時には特定のもののように扱いたい場合があります。
これが発生する一般的な場所は、郵便システムなどのメッセージをやり取りする場合です。手紙を送るとき、郵便システムですべてのメッセージを同じものとして扱うようにしたいので、郵便システムを一度設計するだけで済みます。さらに、メッセージをルーティングするジョブは、その中に含まれるメッセージから独立している必要があります。手紙が目的地に到着したときだけ、あなたはメッセージが何であるかを気にします。
同様に、発生する可能性のあるすべての種類のメッセージの結合としてtype
を定義できます。大学生とその親の間のメッセージングシステムを実装しているとします。そのため、大学生が送信できるメッセージは「ビールのお金が必要」と「パンツが必要」の2つだけです。
type MessageHome = NeedBeerMoney | NeedUnderpants
そのため、ルーティングシステムを設計するとき、関数の型は、可能性のあるすべての種類のメッセージを心配するのではなく、MessageHome
を渡すだけで済みます。ルーティングシステムは気にしません。 MessageHome
であることを知る必要があるだけです。メッセージが宛先である親の家に到着したときに初めて、メッセージの内容を把握する必要があります。
case message of
NeedBeerMoney ->
sayNo()
NeedUnderpants ->
sendUnderpants(3)
Elmアーキテクチャを知っている場合、更新関数は巨大なcaseステートメントです。これは、メッセージがルーティングされて処理される宛先であるためです。また、ユニオン型を使用して、メッセージを渡す際に単一の型を処理しますが、caseステートメントを使用して、メッセージが何であったかを正確に引き出すことができるため、対処できます。
ユースケースに焦点を当て、コンストラクター関数とモジュールに関する小さなコンテキストを提供することにより、以前の回答を補完させてください。
type alias
_の使用法レコードのエイリアスとコンストラクター関数を作成します
これは最も一般的な使用例です。特定の種類のレコード形式に対して代替名とコンストラクター関数を定義できます。
_type alias Person =
{ name : String
, age : Int
}
_
型エイリアスを自動的に定義すると、次のコンストラクター関数(擬似コード)が暗黙的に含まれます。
_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)
_
必須フィールドを指定します
「拡張可能レコード」と呼ばれることもあり、誤解を招く可能性があります。この構文を使用して、特定のフィールドが存在するレコードがあることを指定できます。といった:
_type alias NamedThing x =
{ x
| name : String
}
showName : NamedThing x -> Html msg
showName thing =
Html.text thing.name
_
次に、上記の関数を次のように使用できます(たとえば、ビューで)。
_let
joe = { name = "Joe", age = 34 }
in
showName joe
_
ElmEurope 2017でのリチャードフェルドマンの講演 は、このスタイルがいつ使用する価値があるかについてのさらなる洞察を提供するかもしれません。
ものの名前を変更する
これは、この例のように、コードの後半で新しい名前が追加の意味を提供する可能性があるためです。
_type alias Id = String
type alias ElapsedTime = Time
type SessionStatus
= NotStarted
| Active Id ElapsedTime
| Finished Id
_
おそらく コアでのこの種の使用法はTime
のより良い例でしょう。
異なるモジュールから型を再公開する
パッケージ(アプリケーションではない)を作成する場合、1つのモジュール、おそらく内部(公開されていない)モジュールに型を実装する必要があるかもしれませんが、別の(公開されている) )モジュール。または、複数のモジュールからタイプを公開することもできます。Task
coreおよび Http.Request in Http は最初の例ですが、 Json.Encode.Value および Json.Decode.Value のペアは、後者の例です。
これは、タイプを不透明にしたい場合にのみ実行できます。コンストラクター関数を公開しません。詳細については、以下のtype
の使用法を参照してください。
上記の例では#1のみがコンストラクター関数を提供することに注意してください。 module Data exposing (Person)
のような#1で型エイリアスを公開すると、型名とコンストラクター関数が公開されます。
type
の使用法タグ付きユニオン型を定義する
これは最も一般的な使用例です。その良い例は、 Maybe
コアのタイプ :です。
_type Maybe a
= Just a
| Nothing
_
タイプを定義するとき、そのコンストラクター関数も定義します。たぶん、これらは(擬似コード)です:
_Just : a -> Maybe a
Nothing : Maybe a
_
つまり、この値を宣言すると:
_mayHaveANumber : Maybe Int
_
次のいずれかで作成できます
_mayHaveANumber = Nothing
_
または
_mayHaveANumber = Just 5
_
Just
およびNothing
タグは、コンストラクター関数として機能するだけでなく、case
式のデストラクタまたはパターンとしても機能します。つまり、これらのパターンを使用すると、Maybe
内で確認できます。
_showValue : Maybe Int -> Html msg
showValue mayHaveANumber =
case mayHaveANumber of
Nothing ->
Html.text "N/A"
Just number ->
Html.text (toString number)
_
Maybeモジュールは次のように定義されているため、これを行うことができます
_module Maybe exposing
( Maybe(Just,Nothing)
_
それはまた言うことができます
_module Maybe exposing
( Maybe(..)
_
この場合、この2つは同等ですが、特にパッケージを作成している場合、Elmでは明示的であることは美徳と見なされます。
実装の詳細を隠す
上で指摘したように、Maybe
のコンストラクター関数が他のモジュールから見えるようにするのは意図的な選択です。
ただし、作成者がそれらを非表示にすることを決定する場合、他のケースがあります。 コアのこの例の1つはDict
です。パッケージのコンシューマとして、Dict
の背後にあるRed/Blackツリーアルゴリズムの実装の詳細を確認して、ノードを直接混乱させることはできません。コンストラクター関数を非表示にすると、モジュール/パッケージのコンシューマーは、公開する関数を介して型の値のみを作成します(そして、それらの値を変換します)。
これがコードに時々このようなものが現れる理由です
_type Person =
Person { name : String, age : Int }
_
この投稿の冒頭の_type alias
_定義とは異なり、この構文はコンストラクタ関数を1つだけ持つ新しい「ユニオン」型を作成しますが、そのコンストラクタ関数は他のモジュール/パッケージから隠すことができます。
タイプが次のように公開されている場合:
_module Data exposing (Person)
_
Data
モジュールのコードのみがPerson値を作成でき、そのコードのみがパターンマッチできます。
私が見るように、主な違いは、「シノニカル」タイプを使用する場合、タイプチェッカーがあなたに怒鳴るかどうかです。
次のファイルを作成し、どこかに配置してElm-reactor
を実行し、http://localhost:8000
に移動して違いを確認します。
-- Boilerplate code
module Main exposing (main)
import Html exposing (..)
main =
Html.beginnerProgram
{
model = identity,
view = view,
update = identity
}
-- Our type system
type alias IntRecordAlias = {x : Int}
type IntRecordType =
IntRecordType {x : Int}
inc : {x : Int} -> {x : Int}
inc r = {r | x = .x r + 1}
view model =
let
-- 1. This will work
r : IntRecordAlias
r = {x = 1}
-- 2. However, this won't work
-- r : IntRecordType
-- r = IntRecordType {x = 1}
in
Html.text <| toString <| inc r
2.
のコメントを外して1.
をコメントすると、以下が表示されます。
The argument to function `inc` is causing a mismatch.
34| inc r
^
Function `inc` is expecting the argument to be:
{ x : Int }
But it is:
IntRecordType
alias
は、OOPのclass
に似た他のタイプの単なる短い名前です。経験:
type alias Point =
{ x : Int
, y : Int
}
type
(エイリアスなし)を使用すると、独自の型を定義できるため、アプリ用にInt
、String
、...などの型を定義できます。例として、一般的な場合、アプリの状態の説明に使用できます。
type AppState =
Loading --loading state
|Loaded --load successful
|Error String --Loading error
したがって、view
Elmで簡単に処理できます。
-- VIEW
...
case appState of
Loading -> showSpinner
Loaded -> showSuccessData
Error error -> showError
...
type
とtype alias
の違いを知っていると思います。
しかし、type
とtype alias
を使用する理由と方法はElm
アプリで重要です。皆さんは Josh Claytonの記事