型を読む

このガイドの言語の基礎の節では、REPLでコードをいろいろ実行しました。 さて、もう一度REPLでコードを試していこうと思いますが、今度は表示される型に注目していきましょう。 ターミナルにelm replと入力してください。 このように表示されます:

---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
>

プリミティブとリスト

いくつかのシンプルな式を入力して、何が起こるかを見てみましょう:

> "hello"
"hello" : String

> not True
False : Bool

> round 3.1415
3 : Int

上の3つの例では、REPLは結果として得られる値と、その値のを教えてくれます。値"hello"Stringです。 値3Intです。何もおかしなことはありません。

様々な型の値を保持するリストで何が起こるか見てみましょう:

> [ "Alice", "Bob" ]
[ "Alice", "Bob" ] : List String

> [ 1.0, 8.6, 42.1 ]
[ 1.0, 8.6, 42.1 ] : List Float

> []
[] : List a

最初のケースでは、ListにはStringの値が入っています。2番目のケースでは、ListにはFloatの値が入っています。3番目のケースでは、リストは空なので実際にどのような値がリストに入っているかはわかりません。つまりList aという型は、「リストがあるのはわかるが、何の型が入るかはわからない」ということを表現しています。小文字のa型変数と呼ばれます。この型変数には特定の型に固定する制約がありません。言い換えると、その型は使用方法に基づいて変化する可能性があるということです。

関数

関数の型を見てみましょう:

> String.length
<function> : String -> Int

String.length関数はString -> Intという型を持っています。必ずString型の引数を1つ受け取り、整数の結果を返すことをこの型は意味しています。

> String.length "Supercalifragilisticexpialidocious"
34 : Int

まずString -> Intの関数にStringを与えてみましょう。結果はIntです。

String以外を与えたら何が起こるでしょうか?

> String.length [1,2,3]
-- error!

> String.length True
-- error!

String -> Intの関数は必ずString型の値を引数にしなくてはなりません!

Note: 複数の引数を取る関数は、より多くの矢印を持つことになります。例えば、2つの引数をとる関数はこうなります:

String.repeat : Int -> String -> String

String.repeat 3 "ha"、このように2つ引数を与えると"hahaha"が生成されます。->を引数の区切り文字として考えるのは奇妙に思えますが、本当の理由はここで説明しています。それはとてもすっきりした説明です!

型注釈(タイプアノテーション)

今のところElmに型を推論させているだけですが、必要ならば、定義の上の行に型注釈を書くこともできます。つまり、次のようにコードを書くことができます:

half : Float -> Float
half n =
  n / 2

-- half 256 == 128
-- half "3" -- error!

hypotenuse : Float -> Float -> Float
hypotenuse a b =
  sqrt (a^2 + b^2)

-- hypotenuse 3 4  == 5
-- hypotenuse 5 12 == 13

checkPower : Int -> String
checkPower powerLevel =
  if powerLevel > 9000 then "It's over 9000!!!" else "Meh"

-- checkPower 9001 == "It's over 9000!!!"
-- checkPower True -- error!

型注釈を書くのは必須ではありませんが、絶対にお勧めします。利点は次のとおりです:

  1. エラーメッセージの質 — 型注釈を書いておけば、あなたがそのコードで何をしようとしているかを型注釈がコンパイラに教えてくれます。あなたの実装は間違っているかもしれません。そして今コンパイラはあなたが型注釈で記述した意図と実装を比較してくれます。コンパイラ「あなたは引数powerLevelIntだと言いましたが、Stringとして使われるようになっています!」
  2. ドキュメントとしての効用 — あとでコードを見直すとき(または同僚が初めて読むとき)、実装を非常に注意深く読む必要なくその関数に何が入って何が出ていくかを正確に理解するのに型注釈は本当に役に立ちます。

型注釈で間違いを犯す可能性がありますが、実装した内容と一致しない型を書いたらどうなるでしょうか?コンパイラはその実装内で使われている全ての型を推論し、型注釈が実際の型と一致するかどうかをチェックします。つまり、コンパイラは追加された型注釈が全て正しいことを常に確認しています。それにより、わかりやすいエラーメッセージを出すだけではなく、型が実装の内容を示す ドキュメントとしても 常に最新になっていることを担保できます!

型変数(タイプバリアブル)

elm/coreの関数を見ると、小文字の型シグネチャがいくつかあることがわかります。 以下のようにelm replで実際に確かめることができます。

訳注: 型シグネチャは関数の引数の型と返り値の型の組み合わせのこと。

> List.length
<function> : List a -> Int

型の中に小文字aがあることに気づきましたか?これは 型変数 と呼ばれるものです。このaが実際にどんな型になるかは、List.lengthがどのように使われるかによって変わります。

> List.length [1,1,2,3,5,8]
6 : Int

> List.length [ "a", "b", "c" ]
3 : Int

> List.length [ True, False ]
2 : Int

単に長さが欲しいだけなのでリストの中に何が入っているかは気にしません。つまり型変数aはどんな型にも使えるということです。もう1つよくある例を見てみましょう:

> List.reverse
<function> : List a -> List a

> List.reverse [ "a", "b", "c" ]
["c","b","a"] : List String

> List.reverse [ True, False ]
[False,True] : List Bool

繰り返しになりますが、型変数aList.reverseがどう使われるかによって変化します。この場合はList.reverseの型をみるとaは引数と結果にあることがわかります。これはつまりList Intを渡せば必ず同じ型List Intが返ってくるということです。一度aが何の型かを決めたらどこであってもそのaはその型になります。

Note: 型変数は小文字から始めなければなりませんが、完全な単語でも構いません。つまり例のように1文字の変数でなくても問題ありません。List.lengthの型をList value -> Intとも書けますし、List.reverseの型はList element -> List elementとも書けます。小文字で始まっていれば大丈夫です。型変数のabといった1文字のものは慣例によりいたるところで使われていますが、より具体的な名前を付けたほうがいい場合もあります。

制約付き型変数

いくつか"制約付き"の型変数があります。最も一般的な例はおそらくnumber型です。negate関数はnumberを使用します:

negate : number -> number

通常、型変数にはどんな型でも当てはめることができますが、numberにはIntFloatしか当てはめられません。制約は型変数の可能性を制限します。

制約付き型変数の完全なリストは次の通りです:

  • numberにはIntFloatを当てはめられます
  • appendableにはStringList aを当てはめられます
  • comparableにはIntFloat,Char,String,そしてcomparableな値で構成されるリストまたはタプルを当てはめられます
  • compappendにはStringList comparableを当てはめられます

これらの制約付き型変数は、(+)(<)のような演算子をより柔軟に使えるようにするために存在しています。

results matching ""

    No results matching ""