traitとtype class
あくまでど素人の考えですが、RustとHaskellは似ているのかなと思うところがありました。それはRustのtraitとHaskellのtype classです。
Rustにはi32
とかchar
とかがあり、HaskellにはInt
とかChar
とか、それぞれ基本的な型がありますが、それらに様々な機能を持たせるものとしてtraitとtype classがあって、例えばRustのi32
はAdd
やEq
やOrd
などの多くのtraitが実装されています。これらが実装されているおかげで、四則演算や比較などができるのですが、Haskellも似たような感じになっていました。
たとえばHaskellのghciで以下のようにやってみると、
Prelude> :t 1
1 :: Num p => p
と表示されます。
このNum
というものがtype classと呼ばれるもので、これが実装されていることで、加減乗の計算・絶対値などを扱うことが可能になっているようです。どのような機能を持っているか調べるにはHoogleというHaskell専用の検索エンジンみたいなものがあり、そこから「Prelude」で検索し、調べることができました。コードを見てRustと比較してみると、
class Num a where
(+), (-), (*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
.
.
.
.
とあり、どうやらこの部分がRustのtraitと同じ感じで、
instance Num Int where
.
.
.
.
の部分がRustのimpl Int for Num {}
と同じような感じに見えました。
Fizzbuzzプログラムを例にtype classを見てみます。まず前回のFizzbuzzプログラムをちょっと違った感じで書いてみます。
fizzbuzz n
| mod n 15 == 0 = "Fizzbuzz"
| mod n 5 == 0 = "Buzz"
| mod n 3 == 0 = "Fizz"
| otherwise = show n
この関数の型をghciで調べてみます。
*Main> :t fizzbuzz
fizzbuzz :: (Integral a, Show a) => a -> [Char]
となりました。
ここで出てくる(Integral a, Show a) => a
というところにtype classが出てきています。これは引数の型に制約を設けています、ということを表しているようです。型a
はIntegral
とShow
の両方を満たすものに限られる、ということになります。Integral
はmod
ができるものという制約をつけ、Show
は文字列として表せるものという制約をつける、といった感じのようです。したがって、前回の記事でのFizzbuzzで書いたfizzbuzz :: Int -> String
は、ユルユルの大雑把すぎる型アノテーション?であることが発覚しました。(ただただ勉強不足)
しかし、なんとも記号が多いこと。これは中々大変な言語なような気が致します。Rustの方が分かりやすいのじゃないか……なんて思うのですが、何事も継続、勉強です。ゆるゆる慣れていきます。