Haskellは関数型プログラミングであるが故、手続き型プログラミングでよくみられる「変数を複数宣言してそれらを管理する」手法は一般的ではありません。
しかし、Haskell言語の中にも型と変数の仕組み自体は存在し、特に型の考え方は重要になります。 この記事ではHaskellの型について深く紹介します。
Haskellの型システムの特徴
Haskellの型システムには3つの特徴があります。
- 強い型
- 静的な型
- 型推論機能
強い型
強い型システムを持つプログラムというのは、「整数を関数として扱うような式を書けなくなる」という意味です。 これは「エラーを未然に防いでくれる」という意味でもありますし「柔軟性に欠ける」とも見て取れます。
柔軟性に欠けるという点の具体例は以下のコードでわかると思います。
divBy2 :: Int -> Float
divBy2 x = x / 2
main = do
print( divBy2 1)
上記のコードはコンパイルした時点でエラーが発生します。
main.hs:3:12: error: [GHC-83865]
• Couldn't match expected type ‘Float’ with actual type ‘Int’
• In the first argument of ‘(/)’, namely ‘x’
In the expression: x / 2
In an equation for ‘divBy2’: divBy2 x = x / 2
|
3 | divBy2 x = x / 2
| ^
これはデメリットのように見えますが、強い型付の大きな利点はコード中のバグを実行前に確認できるということです。 例えば、整数値が期待されている中で文字列を使うことはありません。
静的型
haskellが静的型システムを持つことは、haskellコンパイラが全ての値の型を知っているということです。
ghci> "1000" + 10
<interactive>:1:8: error: [GHC-39999]
• No instance for ‘Num String’ arising from a use of ‘+’
• In the expression: "1000" + 10
In an equation for ‘it’: it = "1000" + 10
上記のエラーは、+の演算子において文字列を使用しようとした点でエラーが出ています。
しかしそもそも、上記のコードはどのような意図で書かれているのでしょうか?
"1000" + 10を文字列として結合し、100000を結果として表示したい。"1000" + 10を数値として計算し、1010を結果として表示したい。
人間が見ればどちらとも取れるコードですが、Haskellのコンパイラはそのような曖昧さを許しません。 これにより、数字の足し算、もしくは文字列の結合を行った結果で意図しない計算結果が表示にされます。 このような問題は、単縦なのに後からは発見しずらいエラーを見逃さずにすみます。
型推論
Haskellでは明示的に値の型を宣言できますが、 型推論機能もあるため必ずしも型を明示的に表現しなければならないわけではありません。
以下は変数xに二通りの方法で値を入れていますが、どちらともエラーになりません。
x = 123 :: Int x = 123
Haskellの変数と型
Int型
Haskellで整数を扱うためにはInt型を使用します。
次のコードは変数x, yにそれぞれ123と234を代入して、足した結果を画面に出力しています。
x :: Int y :: Int x = 123 y = 234 main = print $ x + y
x :: Intおよびy :: Intはそれぞれ変数xとyの型を宣言しています。ここでは、xとyが整数 (Int) であることを示しています。x = 123およびy = 234はそれぞれ変数xとyの値を定義しています。xには 123、yには 234 が代入されています。main = print $ x + yはmain関数を定義しています。Haskellのプログラムはmain関数から始まります。このmain関数では、print関数を用いてx + yの結果を標準出力に出力しています。$は関数呼び出しの演算子で、f $ xはf xと同じです。この場合、print $ x + yはprint (x + y)と同じ意味です。
変数の宣言と代入は、次のように一度に行うことが出来ます。
x = 123 :: Int y = 234 :: Int main = print $ x + y
リスト型
リストは複数の値をまとめて扱うための型です。
ghci> []
ghci> ["foo", "hoge", "hoge", "star"]
一つのリスト内のすべての要素は同じ型でなければなりません。 違う型だと以下のようなエラーが出力されます。
ghci> ["foo", 10]
<interactive>:1:9: error: [GHC-39999]
• No instance for ‘Num String’ arising from the literal ‘10’
• In the expression: 10
In the expression: ["foo", 10]
In an equation for ‘it’: it = ["foo", 10]
数値のリストも当然作成可能です。
ghci> [10, 11]
また、連続した値であれば、以下のように列挙表記で書くことも可能です。
ghci> [10..20] [10,11,12,13,14,15,16,17,18,19,20]
リストにはステップを指定することもできます。その際には、二つ目の値を指定することで、その差分を推定してくれます。
ghci> [10,12..20] [10,12,14,16,18,20]
二つ目の値を一つ目の値よりも低くすることで、減少するリストを作成することも可能です。
ghci> [20,18..10] [20,18,16,14,12,10]
小数点を指定することも可能です
ghci> [1.0,1.5..10.0] [1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0]
二つのリストを結合するには++演算子を使用します。
ghci> [1,2,3]++[4,5] [1,2,3,4,5]
文字列型
Haskellで文字列型を扱うにはString型を使用します。
ですが、内部的にはString = [Char]であり、文字列のリストと同義です。
そのため、++演算子を使用することが可能です。
次のコードは基本的な文字列型の操作を示しています
x :: String y :: String x = "Hello!." y = "This is Haskell code" main = print $ x ++ y
x :: Stringおよびy :: Stringはそれぞれ変数xとyの型を宣言しています。ここでは、xとyが文字列 (String) であることを示しています。x = "Hello!."およびy = "This is Haskell code"はそれぞれ変数xとyに文字列を代入しています。main = print $ x ++ yはmain関数を定義しています。ここでは、文字列の結合 (++演算子) を行っています。print関数を使って結果を標準出力に出力しています。$演算子は右側の式を評価し、その結果を左側の関数に渡します。この場合、print $ x ++ yはprint (x ++ y)と同じ意味です。
ここで、print関数を実行する際に、Int型の時は+だったのがString型の時は++に代わっている点に注意してください!
+はInt型同士の足し算にしか使用できず、++はString型同士の結合にしか使用できません。
pythonやjavaはこのあたりの制約が緩いのですが、Haskellは型の制約をかなり厳しく指定します。
上記のコードで++を+に変えた場合、以下のようなエラーが出ると思います。
[1 of 1] Compiling Main ( strbar.hs, strbar.o )
strbar.hs:5:16: error:
• No instance for (Num String) arising from a use of ‘+’
• In the second argument of ‘($)’, namely ‘x + y’
In the expression: print $ x + y
In an equation for ‘main’: main = print $ x + y
|
5 | main = print $ x + y
| ^^^^^
そのほかの型
Haskellではそのほかに以下のような型が使用できます。
- Int:
有界整数型。例:
42 - Integer:
任意精度整数型。例:
123456789012345678901234567890 - Float, Double:
浮動小数点数型。
Floatは単精度、Doubleは倍精度。例:3.141592653589793 - Char:
文字型。例:
'A' - Bool:
真偽値型。
TrueまたはFalse。例:True - String:
文字列型。例:
"Hello, Haskell!"
page:https://minegishirei.hatenablog.com/entry/2023/11/22/085647