関数の基本

この章では、Elmに慣れるために重要な基本的な構文、関数、関数シグネチャ、部分適用、およびパイプ演算子について説明します。

機能

Elmは2種類の関数をサポートしています:

  • 無名関数
  • 名前付き関数

無名関数

無名関数は、その名前が示すように、名前なしで作成する関数です。

\x -> x + 1

\x y -> x + y

バックスラッシュと矢印の間には関数の引数を列挙し、矢印の右側にはそれらの引数で何をすべきかを示します。

名前付き関数

Elmの名前付き関数は次のようになります。

add1 : Int -> Int
add1 x =
  x + 1
  • この例の最初の行は関数のシグネチャです。シグネチャはElmではオプションですが、関数の意図を明確にするために記述することが推奨されています。
  • 残りは関数の実装です。実装は、その直上で定義したシグネチャに従わなければなりません。

この場合、シグネチャは「引数として整数(Int)をとり、別の整数(Int)を返す」ということを言っています。

この関数は、以下のように呼び出すことができます:

add1 3

Elmでは、関数適用を表すために(カッコの代わりに)空白を使用します。

以下は別の名前付き関数です:

add : Int -> Int -> Int
add x y =
  x + y

この関数は両方ともIntである2つの引数をとり、別のIntを返します。この関数は次のように呼び出すことができます。

add 2 3

引数なし

Elmでは定数は引数をとらない関数です。

name =
  "Sam"

どのように関数適用がなされるか

前述のように、2つの引数をとる関数は次のようになります。

divide : Float -> Float -> Float
divide x y =
    x / y

このシグネチャを、2つの浮動小数点数をとり、別の浮動小数点数を返す関数と考えることもできます。

divide 5 2 == 2.5

しかし、これはまったく真実ではありません。Elmでは、すべての関数はただ1つの引数をとり、1つの結果を返します。ここではその結果は別の関数になります。 上記の関数を使って説明しましょう。

-- 次のようにすると:

divide 5 2

-- 次のように評価されます:

((divide 5) 2)

-- 最初の「divide 5」が評価されます。
-- 引数 '5'は `divide`に適用され、中間的な関数になります。

divide 5 -- ->中間的な関数

-- この中間的な関数を `divide5`と呼びます。
-- この中間的な関数のシグネチャと本体を見ることができたなら、次のようになるでしょう:

divide5 : Float -> Float
divide5 y =
  5 / y

-- したがって、すでに `5`が適用された関数があります。

-- 次の引数、すなわち `2`が適用されます

divide5 2

-- これは最終結果を返します

括弧を書かないで済むのは、関数適用が左結合だからです。

カッコでグループ化する

別の関数呼び出しの結果で関数を呼び出す場合は、呼び出しをグループ化するために括弧を使用する必要があります。

add 1 (divide 12 3)

ここでは、「add」の第2引数として「divide 12 3」の結果が与えられます。

対照的に、他の多くの言語では、次のように書かれます。

add(1, divide(12, 3))

部分適用

上で説明したように、すべての関数は1つの引数しか取らず、別の関数または結果を返します。 これが意味するのは、引数が1つだけでも上記の addのような関数を呼び出すことができるということです。たとえばadd 2とすると、部分的に適用された関数が返ります。 この結果の関数のシグネチャは、Int -> Intです。

add 2は最初引数が値'2'に束縛された別の関数を返します。返ってきた関数に2番目の引数を与えて呼び出すと、その結果は「2 + 2番目の引数」になります。

add2 = add 2
add2 3 -- 結果は5

部分適用は、コードを読みやすくしたり、アプリケーション内の関数間で状態を渡したりすることができ、Elmプログラミングにおいて非常に便利です。

パイプ演算子

上記のように、次のような関数を入れ子にすることができます:

add 1 (multiply 2 3)

これは簡単な例ですが、もっと複雑な例を考えてみましょう:

sum (filter (isOver 100) (map getCost records))

このコードが読み難いのは、内から外に解決していくからです。パイプ演算子を使用すると、そのような式をより読みやすく書くことができます。

3
    |> multiply 2
    |> add 1

このようにできるのは前述の部分適用のおかげです。この例では、値「3」が部分適用された関数multiply 2に渡されます。その結果は、部分適用されたまた別の関数 add 1に渡されます。

パイプ演算子を使用すると、上記の複雑な例は次のようになります。

records
    |> map getCost
    |> filter (isOver 100)
    |> sum

results matching ""

    No results matching ""