Haskell: type と typeclass を作る (3)
前回の続き.
Learn You a Haskell for Great Good! の Chapter 8, Making Our Own Types and Typeclasses を元にしたものです.基本的には翻訳のつもりですが,省略や加筆が勝手に入ることもあります.原文は CC-BY-NC-SA でライセンスされており,この文書もそれに従います.
今回は Type parameters から.
Type parameters
Value constructor がパラメータをとって value を作るように,types -> types が type constructors. ちょっとメタすぎるように感じるかもしれないがそんなに複雑じゃない. C++ の template と似てるらしいけど, C++ 知らないのでアレですね,アレ.
じつはすでに知り合いだったりするのだ
data Maybe a = Nothing | Just aここで a に入るのは type parameter1.そこで Maybe を type constructor
といい,結局型としては Maybe Int とかそういう感じになる.
それ自身では型ではないから,Maybe 型を持つ値などといったものは存在しない.
Maybe なんていうと上段に構えた感じだけど実は list もそうで,型として存在するのは [Int] だったり [Char] だったりするけど,
[] 型の値,というものは存在しないというわけだ.
Maybe についてしばらく遊んでみよう.:t Just "hi" は Maybe [Char],
こういうのはいいけど,Nothing も型が Maybe a なのに注意しよう.
:t head [Nothing, Just 'a'] とかやってみるとわかりやすいかもしれない.
これは polymorphic で,Maybe Int がほしい時にも Maybe Char がほしい時にも
与えられるってわけだ.[1,2,3] ++ [] とか "hi" + [] とかが
できるのも同じ理屈.
さて,カッコいいからと言って何でもこれ使ったらいいかというと勿論そういうわけではなく,
普通は折角 type parameter 使ってもまあ結局 String が入ってないと役に立たん,みたいなことになるので,
そういう場合に使ってもあまり意味はない.効力を発揮するのはまさに「何の型でもいいからこれとこれ」みたいな奴の時で,
list とか Maybe とかはまさにそういう場合にあたる.
もうひとつのそういう例は Data.Map の Map k v. k が keys で v が value というやつですね.
こういうかんじでつかう
Prelude Data.Map> :t fromList [("foo",3), ("bar",6)]
fromList [("foo",3), ("bar",6)] :: Num a => Map [Char] aこれは (k は Ord じゃないとだめだけど) k, v がそれぞれ何でもよくて,まさに type parameters の出番という感じだ.
ところで,この Map を作りたいと思ったらこういう感じで constraint をつけたりしたくなる2かもしれない
data (Ord k) => Map k v ...ところが,決して data 宣言に type constraints を加えない というのが非常に強い慣習となっている.
なぜか.それは,結局対して得をしないばかりか,必要のないところにまで type constraints を書きまくる羽目になるから.
例えば,さっき出てきた Data.Map の toList の型はこんな感じになっている.
toList :: Map k a -> [(k,a)]ところが data (Ord k) => Map k v と書いているとここにも Ord じゃないと行けなさがにじみでてくるから,こう書かないといけなくなる.
toList :: (Ord k) -> Map k a -> [(k,a)]べつにこの函数に Ord さは必要ないのに,これはちょっと嫌だなーってことで3,そういうのは書かないようにしましょうということだ.
というわけで,練習問題的に三次元ベクトルを書いてみよう.Num のなかの色々に対応できるように書いてみる.
data Vector a = Vector a a a deriving (Show)
vplus :: (Num t) => Vector t -> Vector t -> Vector t
(Vector x0 y0 z0) `vplus` (Vector x1 y1 z1) = Vector (x0+x1) (y0+y1) (z0+z1)
vectmult :: (Num t) => Vector t -> t -> Vector t
vectmult (Vector x y z) a = Vector (a*x) (a*y) (a*z)
innerProd :: (Num t) => Vector t -> Vector t -> t
(Vector x0 y0 z0) `innerProd` (Vector x1 y1 z1) = x0*x1 + y0*y1 + z0*z1例によって Vector a が型,Vector 3 2.3 4 は値なので型に Vector a a a などと書かないように気をつけましょう.
というわけで一旦ここまで.
-
:t Just 't'とかを思い出してみよう. ↩ -
そもそもこういう書き方出来るんですね? …とおもったら最近のは無理?Haskell-cafe : Why were datatype contexts removed instead of “fixing them”? というのがあり,それっぽい書き方は ghcmod で
Illegal datatype context (use -XDatatypeContexts): Ord k =>といわれたりする. ↩ -
ちょっとふーむという感じだが. ↩