Haskellerへの道 #1 - 関数、型、バインディング

Last Edited: 6/12/2024

このブログ記事では、Haskellの重要な概念である関数、型、およびバインディングについて紹介します。

Haskell & Functions

関数

さあ、早速コーディングを始めましょう。関数はHaskellの基本であり、次のように定義することができます

name arg1 arg2 ... argn = <expr>

ここで、nameは関数の名前、arg1 arg2 ... argnは関数の引数/入力、<expr>は関数の式です。ここで重要な点は、 HaskellにおけるreturnがPythonなど他のプログラミング言語で見るreturnと同じではないことです。定義した関数を使用するときは、 以下のようにします。

name arg1 arg2 ... argn

関数がどのようにHaskellで動作するかを体験するために、例を試してみましょう。以下の関数 doubleinRange は、入力を倍にし、 与えられた範囲内に値があるかどうかを見ます。

double x = 2 * x
 
double 2 --- => 4
double 100 --- => 200
 
inRange min max x = min <= x && x <= max
 
inRange 10 15 11 --- => True
inRange 1 2 3 --- => False

これらをあなたのhsファイルに定義し、GHCiで関数を実行してみてください。

もしも上記の関数をVSCodeでHaskell拡張機能を使って定義した場合(もしまだしていない場合は、 Road to Haskeller #0 - Setting Up Environment を読んで同じ環境をセットアップすることをお勧めします)、関数の上に小さな文字列が表示されるはずです。それらは関数の推論された型ですが、 今はクリックしないでください。Haskellではすべての値(関数も値です)に型があり、次のように型を付けることができます。

x :: <type>

型がどのようにHaskellで付けられるかを理解するために、いくつかの例を見てみましょう。

x :: Integer
x = 1
 
y :: Bool
y = True

上記は変数に型を割り当てる方法を示しています。では、doubleinRange にどのように型を割り当てるか見てみましょう。

double :: Integer -> Integer
double 1 --- => 2
double 1.2 --- => Type Error
 
inRange :: Float -> Float -> Float -> Bool
inRange 1.1 1.3 1.2 --- => True
inRange 1 3 2 --- => Type Error

まず double 関数を見てみましょう。型定義の最初の部分は、入力がInteger型(1、2、3など)であることを指定します。 その後、出力もInteger型であることを定義しています。ここで注意すべき点は、型定義では入力と出力を区別しないことです。 これはHaskellの非常に重要な概念(カリー化)に関連していますが、これについては後の記事で詳しく説明します。

inRange 関数についても同様です。最初の3つの引数の型はFloatに設定され、出力はBoolTrueまたはFalse)に設定されています。 ただし、同じVSCodeのセットアップを使用している場合、上記と推論された型との違いについて疑問に思うかもしれません。これは推論された型が型クラス (NumやOrdなど)の概念を使用しているためですが、これについてはこの記事の範囲外です。今のところ、型クラスではなく具体的な型を使用することをお勧めします。

Bindings

時には、コードを整理するために何らかの中間変数を使用したいと思うことがあります。しかし、以下のような方法はHaskellでは許されていません。

inRange min max x =
  lower_bound = x >= min
  upper_bound = x <= max
  return lower_bound && upper_bound

では、どうすればHaskellに叱られることなく同じことができるのでしょうか?ここでバインディングの出番です。

Letバインディング

letバインディングは次のように機能します。

inRange min max x =
  let lower_bound = x >= min
      upper_bound = x <= max
  in
  lower_bound && upper_bound

ここで、letを使って中間変数を定義し、inでそれらの変数を操作します。

Whereバインディング

関数によっては、コードの可読性を向上させるために whereバインディングを使用したい場合があります。

inRange min max x = lower_bound && upper_bound
  where lower_bound = x >= min
        upper_bound = x <= max

ここでは、まず中間変数を操作し、その後にwhereを使ってそれらの変数の意味を定義します。

If文

他のプログラミング言語でコーディングの経験がある場合、if 文をどのように書くかが気になるかもしれません。Haskellでは以下のように書くことができます。

inRange min max x = 
  if lower_bound then upper_bound else False
  where lower_bound = x >= min
        upper_bound = x <= max

これは非常に直感的で書きやすく、読みやすい方法です。今日は、Haskellにおける関数、型、バインディング、およびif文という重要な要素をカバーしました。

クイズ

この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。

リソース