ことり、穂乃果と一緒に学ぶHaskell(入門)その4「型クラスの定義と実装」
- 前回 - ことり、穂乃果と一緒に学ぶHaskell(入門)その3「代数的データ型の定義2」
- 記事一覧 - ことり、穂乃果と一緒に学ぶHaskell(入門)
- 更新履歴 - μ'sと一緒に学ぶHaskell
前回
ことり「穂乃果ちゃんとの2人お風呂をしてきた」
穂乃果「言ってくれれば海未ちゃんも呼んで、みんなでお風呂入ったりできたのに」
ことり「えっ……♡♡」
ことり「Cの構造体みたいなデータ型、あれは直積型って言うよ!」
data Member = Member
{ name :: String
, age :: Int
}
ことり「オブジェクト指向のクラスと比べた代数的データ型の最大の利点は、その網羅性って言われたりするくらいだしね」
ことり「その裏付けをするのがパターンマッチだよ」
ことり「列挙型について」
ことり「わたしたちμ'sは、こうやって書けるよ」
data Muse = Hanayo | Rin | Maki | Umi | Kotori | Honoka | Eli | Nico | Nozomi
ことり「直和型は、列挙型と直積型が合わさったようなもので、列挙子がフィールドを持ちます!」
data Student = OtonokiStudent String Int
| UranohoshiStudent String Int Bool
deriving (Show)
型クラス
ことり「こんにちは〜」
穂乃果「日曜おひさま ほのHaskellだよ!」
ことり「今日は日曜日じゃないけどね♪」
ことり「今日は型クラスについてやっていくよ!」
はい
ことり「型クラスはclass
キーワードで定義するよ♪」
穂乃果「class
? Javaにもclass
キーワードがあったけど、Haskellでは型クラスに使うんだー!」
ことり「うん! Javaのclass
はわからないけど、Haskellのclass
は『classify(分類する)』から来てるって説があるね」
class GobiPersons a where
-- 人を受け取って、その人の語尾を返す関数
say :: a -> String
ことり「これは『語尾持ちとしての性質』を表す型クラスだよ!」
穂乃果「うーん?? 凛ちゃんとかにこちゃんとか……希ちゃんもかな?……かな?」
ことり「うん! 希ちゃんの『やん!』とか『やねっ!』が語尾かは微妙だけど……ちょうど希ちゃんとにこちゃんはサブグループやってたよね」
ことり「はいっ」
data NozoNico = Nozomi | Nico
ことり「そうしたら、NozoNico
をGobiPersons
インスタンスで定義するよ」
instance GobiPersons NozoNico where
say Nozomi = "やねっ!"
say Nico = "ニコッ!"
main :: IO ()
main = do
putStrLn (say Nozomi)
putStrLn (say Nico)
-- vvv 出力 vvv
-- やねっ!
-- ニコッ!
ことり「たまに『型クラスはインターフェースに似ている』とか言われるんだ。 ……賛否両論が多数あるけどね」
穂乃果「あー、でも確かに少し似てるかも……? こんな感じかな?」
interface GobiPersons {
public String say();
}
enum NozoNico implements GobiPersons {
Nozomi, Nico;
@Override
public String say() {
if (this == Nozomi) {
return "やねっ!";
} else if (this == Nico) {
return "ニコッ!";
} else {
throw new RuntimeException();
}
}
}
public class Program {
public static void main(String[] args) {
NozoNico nozomi = NozoNico.Nozomi;
System.out.println(nozomi.say());
NozoNico nico = NozoNico.Nico;
System.out.println(nico.say());
}
}
// vvv 出力 vvv
// やねっ!
// ニコッ!
ことり「Congratulation!」
穂乃果「えへへ〜」
型クラスとインターフェースって、何が違うの?
ことり「ここでひとつ、インターフェースと型クラスの拭えない相違点が浮かんできたよ」
穂乃果「ぬぐえないそういてん?」
ことり「うん。 型クラスはね、deriving
を除いて、具体データ型とインスタンス定義が明確に別れてるんだ!」
穂乃果「??? どういうこと?」
ことり「例を見てみよう。 穂乃果ちゃん、ちょっとNozoNico
データ型をもう1回書いてみてもらってもいい?」
穂乃果「うん! やってみるね!」
data NozoNico = Nozomi | Nico
ことり「じゃあ次はGobiPersons
インスタンスももう1回書いてくれるかな」
穂乃果「はーい」
instance GobiPersons NozoNico where
say Nozomi = "やねっ!"
say Nico = "ニコッ!"
ことり「ほら、具体データ型の定義とインスタンス定義。 2つの工程が明確に分割されてるんだ」
ことり「対してJavaのインターフェース実装は、必ずクラス定義と同時に行わなくちゃいけない。 って感じかな」
穂乃果「あ、確かに。 でもそれって、便利なの?」
ことり「うん、とても便利だよ! 例えば……ある性質を持つ型クラスをここに定義しました」
-- aとaを足し合わせることができる性質
class Magma a where
magAppend :: a -> a -> a
ことり「Intって足し算ができるよね?」
穂乃果「うん。 足し算できる」
ことり「でもIntは標準で定義されてるから、後から定義を変更すること……Intにimplements Magma
みたいなのを付け足すことができないんだ」
穂乃果「……あっ!」
ことり「そう、ここで『インスタンス定義が独立していること』が効いてくるよ」
instance Magma Int where -- 任意の時点でIntのインスタンス定義ができる
magAppend x y = x + y
main :: IO ()
main = do
print (magAppend 10 20 :: Int)
-- vvv 出力 vvv
-- 30
穂乃果「ほんとうだ! Haskellすごーい! Haskellすごーい!!」
ことり「でしょー! えへへ」照れり
マグマってだれですか?
穂乃果「そういえば、ことりちゃんがさっき書いてたMagma
って何?」
ことり「ふふっ。 数学者さんでも専門外だと知りすらしない、ちょっと不遇な子かな♪」
参考にしたページ
💎 こんにちは 💎
この続きはTwitterへの共有ツイート、はてなブックマークへのブックマーク登録、Facebookへのいいね及びシェア、Pocketへの登録
そして僕のモチベーションによって書かれます。
シェアして欲しいな〜〜(シェアはこのページの上部ボタンから!)
疑問点があれば、Twitterでリプライくれれば(そのリプライを見逃してなければ)返すよ!
@public_ai000ya - Twitter