ことり、穂乃果と一緒に学ぶHaskell(入門)その2「関数とデータ型」

前回

ことり「次は、Memberデータ型をmainで表示してみるよ」
ことり「これはこう書けるよ」

data Member = Member String Int
  deriving (Show)

ことり「deriving (Show)って行を追加したよ」
ことり「こういう単純なパターンを実装したい時には、derivingっていうのを使うよ!

穂乃果「でもことりちゃん、このShowってなに?」
ことり「それはね穂乃果ちゃん、いわゆる型クラスって言って……」

Showってなあに?

ことり「実はね穂乃果ちゃん、型クラスは型に振る舞いを与えるんじゃなくて、ある関数の実装を与えるんだよ
穂乃果「『ある関数の実装』?」
ことり「うん、例えばShowは、Showを実装したデータ型であるMemberに対してshow :: Show a => a -> Stringなる関数の実装を与えるよ♪」
ことり「Show(頭文字大文字)が型クラス名、show(頭文字小文字)は関数名だよ!」

穂乃果「ちょちょっとまってことりちゃん、何か今、すごい勢いで知らない言葉が出てきたよ!? Show a => a -> Stringって何!?」
ことり「そこ気になっちゃう? 気になっちゃうよね! ここすっごく大事なところだから、やっていこうか♪」

関数

ことり「穂乃果ちゃん、関数の定義方法は授業では習った?」
穂乃果「い…いちおう……。 えーと、こういうのだっけ」

add1 :: Int -> Int
add1 x = x + 1

穂乃果「このadd1の後に書いてあるxIntの引数…」

穂乃果「でも::とか->とか、よくわかんないな……」
ことり「説明していくね」

ことり「まず:: Int -> Intっていうのは、add1っていう関数にInt -> Intっていう型を付けているんだ」
ことり「::っていうのを使うと、ある名前(変数)に型を付けてあげられるよ」
ことり「『add1Int -> Intという型を持つ名前』ってことを書いているの」

ことり「別の例を挙げると……」

ten :: Int
ten = 10

ことり「Javaに直すと、こうかな」

int ten = 10;

穂乃果「なるほど、でもさっきの例と違って、これは数じゃなくて、変数じゃない?」
ことり「ふふふ、そうなんです。 Haskellでは関数と変数もあまり区別がなくってね……こほん。 ちょっとこれについてはまたあとでね」

ことり「Haskellでは->というのが、関数の型を作ります。 例えば」
ことり「さっきのInt -> Intは『引数にIntを1つ受け取って、戻り値にIntを返す』ということを表す型だよ。 Javaに直すとこうかな!」

int add1(int x) { return x + 1; }

穂乃果「なるほど! Javaの関数定義だと戻り値の型が前に来てるけど」

int add1(int x) { return x + 1; }

穂乃果「Haskellだと戻り値の型が後に来るってことかな?」

add1 :: Int -> Int
add1 x = x + 1

ことり「こんぐらっちゅれーしょんだよ〜 穂乃果ちゃん♡」ナデナデヨシヨシ
穂乃果「えへへ!」エヘヘ

ことり「例えばこんな多引数の関数も」

int constTen(char x, boolean y) { return 10; }

ことり「こういうふうに変換されます!」

constTen :: Char -> Bool -> Int
constTen x y = 10

穂乃果「へー、引数の数が増えても、戻り値の型は最後に来るんだ」
ことり「そういうことだね!」

型制約

穂乃果「Showについてだけどことりちゃん、さっきことりちゃんはこう言ってたよね」


ことり「うん、例えばShowは、Showを実装したデータ型であるMemberに対してshow :: Show a => a -> Stringなる関数の実装を与えるよ♪」

data Member = Member String Int
  deriving (Show)

穂乃果「show :: Show a => a -> Stringこれ……何かを受け取ってStringを返してるshow関数に見えるけど……Show a => aってなんだろう……」
ことり「そのとおりだよ穂乃果ちゃん!」
穂乃果「ほへっ!?!?」

ことり「a -> Stringっていうのは『何か型aを受け取って、Stringを返す』っていう型なんだ。  だからさっき穂乃果ちゃんが言った『何かを受け取ってStringを返してる』っていうのは大正解なの♪」
穂乃果「エーッ!? えーと、自分が言ったことなんだけど、よくわからないや……型aって、『何か』って何? どういうこと?」

ことり「型aっていうのは、何の型でもいいの。 任意の型a。 具体例を示すと」

id :: a -> a
id x = x

ことり「こんな、xを受け取ってxをそのまま返す関数とか。 これって別に、aIntでもCharでもMemberでも、同じことができるよね!  Haskellではそれをこうやって、簡単に書くことができるんだ」
穂乃果「そっか、aの内容に言及する必要がないんだね。 だから……もしかしてこれって、ジェネリクス?」
ことり「そのとおり! Javaではこれをジェネリクスとして表現してるね」

public class Program {
	public static <T> T id(T x) {
		return x;
	}

	public static void main(String[] args) {
		System.out.println(id(10));
		System.out.println(id('x'));
		System.out.println(id("kotohono"));
	}
}

ことり「次にShow a => aっていう表現だね。 これは」
ことり「さっきのaが『任意の型a(何でも)』だったのに比べて、『Showを実装する任意の型a(Showを実装するなら何でも)』って言えるかな」

穂乃果「じゃあshow :: Show a => a -> Stringっていうのは……Showを実装する型aを受け取って、文字列(String)を返す関数show?」
ことり「そのとおりです!」

ことり「実はIntもCharもBoolもShowを実装するので、こんなことができるよ」

main :: IO ()
main = do
  putStrLn (show 10)

(文字列を受け取って、その文字列を標準出力に表示する関数putStrLn)

putStrLn :: String -> IO ()

ことり「『型XがShowを実装する』ってことを、『型XはShowインスタンスである』って言ったりもするかな!  『IntはShowインスタンスである』みたいにね。」
ことり「Haskellでの『インスタンス』は、他のオブジェクト指向言語でのインスタンスとは趣が違うから注意だね。 値じゃなくて型に対する言葉なんだ」

穂乃果「なるほど……こんな感じかな?」

class Member implements Show {
	public String name;
	public int age;
	public Member(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public String show() {
		return "Member " + this.name + " " + this.age;
	}
}

interface Show {
	public String show();
}

public class Test {
	public static <T> T id(T x) { return x; }
	public static void main(String[] args) {
		Member kotori = new Member("南ことり", 16);
		System.out.println(kotori.show());
	}
}

ことり「いいね穂乃果ちゃん!  型クラスはよく、interfaceと似てるって言われたりもするんだぁ  今回の、derivingを利用したShowインスタンスの実装は、ちょうどそうなるね!」
穂乃果「やったぁ♪ ……『derivingを利用したShowインスタンスの実装は』……?」

ことり「うん、derivingって実は全ての型クラスに対してできるわけじゃなくってね、Showや基本的な型クラスのみができるんだ」
ことり「じゃあderivingできない型クラスはどうやって使うのかって言うと……instanceってキーワードを使います。  これを使って、Showインスタンスを自分で実装することもできるんだ」
ことり「derivingはあくまで、自分で実装するところをコンパイラが機械的にやってくれてるだけ……ほらここ」

@Override
public String show() {
	return "Member " + this.name + " " + this.age;
}

ことり「returnする文字列を自分で構築してるよね。 これをHaskellはコンパイラが勝手にコードを書いてくれるんだ♪」

穂乃果「ほえー……機械が勝手に書いてくれるんだぁ……」

…………

(次回に続く)


 疑問点があれば、Twitterでリプライくれれば(そのリプライを見逃してなければ)返すよ!
@public_ai000ya - Twitter

筆者プロフィール

my-latest-logo

aiya000(あいや)

せつラボ 〜圏論の基本〜」 「せつラボ2~雲と天使と関手圏~」 「矢澤にこ先輩といっしょに代数!」を書いています!

強い静的型付けとテストを用いて、バグを防ぐのが好き。Haskell・TypeScript。