Typing Haskell in Haskellを読んでみる

頭の中の整理も兼ねて、Haskellの型システムについて述べたM.P.Jones氏による論文[Typing Haskell in Haskell](https://web.cecs.pdx.edu/~mpj/thih/thih.pdf)についてまとめていきたいと思います。

※少しずつ加筆しています

# Section 2 : Preliminaries

最初のIntroductionは省略させていただきました（興味があったらぜひ読んでみてください）
ここでは早速Haskellのコードが出てきますね。
まず

```haskell
module TypingHaskellInHaskell
import Data.List (nub,(\\),intersect,union,partition)
import Control.Monad (msum)
```
とありますね。
まあここは説明しなくてもお分かりでしょう。
型推論器を書く上で用いるのは、Preludeの関数と上記のimportした関数のみです。

次にこのようなプログラムが出てきます。

```haskell
type Id = String

enumId :: Int -> Id
enumId n = "v" ++ show n
```

enumIdは整数を受け取って新しい識別子を生成する関数です。
この関数は新しく型変数を生成する際などに用いられます（Section 10で出てきます)
ちなみにIdという型が新しくStringのエイリアスとして定義されていますが、これは識別子を表す型です。
また、型変数の名前は"v"から始まるものとします。

# Section 3 : Kinds

ここではkind（高階型）を表すデータ構造について述べられています。
kindは次のように表現されます

```haskell
data Kind = Star | Kfun Kind Kind deriving Eq
```

Starは具体型のことですね。
ghci等を開いて``` :k Maybe ```などと打ち込んでみるとたぶん```* -> *```と表示されると思います。
この表示された内容の```*```がStarに相当する内容ですね。
ちなみに今、Maybeを例に出してStarの解説をしましたが、Maybeをこのデータ構造を用いて表すと次のようになります。

```haskell
Kfun Star Star -- * -> *
```
Kfunは```* -> *```の```->```に相当するものです。
Maybeのような場合ではStarを２つ用いれば表すことができましたが、例えば```* -> * -> *```などの場合はどうでしょう。Kindのフィールドは２つしかないのでStarが３つあるこの高階型は表せないような感じがしますね。
このような場合は次のように表すことができます。

```haskell
Kfun Star (Kfun Star Star) -- * -> * -> *
```
Starが４つや５つになっても次のように表せます

```haskell
Kfun Star (Kfun Star (Kfun Star Star)) -- * -> * -> * -> *
Kfun Star (Kfun Star (Kfun Star (Kfun Star Star))) -- * -> * -> * -> * -> *
```

簡単ですね、何個でも増やすことができます。

# Section 4 : Types

このセクションでは普通の型についての定義などについて述べられています。
型は次のようなデータ構造によって表されます。

```haskell
data Type 
    = TVar Tyvar 
    | TCon Tycon 
    | TAp Type Type 
    | TGen Int 
deriving Eq

data Tyvar 
    = Tyvar Id Kind 
deriving Eq

data Tycon
    = Tycon Id Kind 
deriving Eq
```

それぞれの値コンストラクタが何を表しているかについて説明します。

まず、TVarは型変数を表しています。
型変数とは未確定の型を表す変数です、とりあえずここでは型を含むことのできる変数程度に認識しておいてください。
また、Tyvarでは型変数の変数名と型変数が持つkindを表しています。

次に、TConは型コンストラクタを表しています。
これも型コンストラクタ名と型コンストラクタのkindを持つTyconをフィールドに持ちます。

TApは型適用です。
例えばkind```* -> *```を持つ型コンストラクタ```list```があるとします。
これに型```Int```を適用して型```list Int```を作る動作のことです。
この動作は、上のデータ構造を用いて

```haskell
(TAp (TCon (Tycon "list" (Kfun Star Star))) (TCon (Tycon "Int" Star)))
```

と表すことができます。

TGenは実体化される前の型とでも考えてください。
どういうことかというと、TGenは例えば次のような型定義があるとします。

```haskell
id :: a -> a
```

例えばこの関数にInt型の値を渡すとします。
すると、この関数idの型は

```haskell
id :: Int -> Int
```

となります。
Int型でaが実体化されてると言えますね。
このようにパラメトリック多相な関数は関数が表れるたびに、実体化されて用いられるわけです（これについては後で詳しく述べます）
このaがTGenなのです。

つぎに組み込みの型についての定義があります。

```haskell
tUnit = TCon (Tycon "()" Star) -- ()
tInt = TCon (Tycon "Int" Star) -- Int
tFloat = TCon (Tycon "Float" Star) -- Float
tChar = TCon (Tycon "Char" Star) -- Char
tInteger = TCon (Tycon "Integer" Star) -- Integer
tDouble = TCon (Tycon "Double" Star) -- Double

tList = TCon (Tycon "[]" (Kfun Star Star)) -- [] (* -> *)
tArrow = TCon (Tycon "->" (Kfun Star (Kfun Star Star))) -- -> (* -> * -> *)
tTuple2 = TCon (Tycon "(,)" (Kfun Star (Kfun Star Star))) -- (,) (* -> * -> *)

tString = list tChar -- [Char]
```

これはきっと上の説明を読んでいただけた方ならどのような事柄を表しているか、理解できると思います。

次にこれらの型を扱う上で必要な、サポート関数の定義です。

```haskell
infixr 4 `fn`
fn :: Type -> Type -> Type
a `fn` b = TAp (TAp tArrow a) b

list :: Type -> Type
list t = TAp tList t

pair :: Type -> Type -> Type
pair a b = TAp (TAp tTuple2 a) b
```

fnはa型とb型を取って```a -> b```型を作る関数
listはt型を取って```[t]```型を作る関数
pairはa型とb型を取って```(a,b)```型を作る関数です。

次に上記の型についてのデータ構造からkindを抜き出す処理です。

```Haskell
class HasKind t where    
    kind :: t -> Kind

instance HasKind Tyvar where
    kind (Tyvar _ k) = k

instance HasKind Tycon where
    kind (Tycon _ k) = k

instance HasKind Type where
    kind (TCon tc) = kind tc
    kind (TVar u) = kind u
    kind (TAp t _) =
        case kind t of
            (Kfun _ k) -> k
```

これは簡単ですね。
TApの場合の処理について引っかかった方もいるかもしれないので、具体例を上げて説明してみます。

次のような型があるとします。

```Haskell
s = TCon (Tycon "[]" (Kfun Star Star))
-- s :: * -> *
```

この型に型を適用してみましょう。

```Haskell
TAp s tInt
```

これをkind関数に渡してみると

```Haskell
kind (TAp t _)
```

とマッチし次に

```Haskell
case kind t of
```

でsがkindに渡されます。

そして

```Haskell
(Kfun _ k) -> k
```

によりKfunの２つ目のフィールドが取り出されています。
なぜ２つ目のフィールドなのかというと、TApで適用されているからです。
２つ目しか取り出さないので```* -> *```というkindがあるとすると、```*```、つまり具体型になりますね。
ここでは適用した結果のkindを取り出すのです。

# Section 5 : Substituation

Substituationとは、型変数から型への写像です。
Haskellのプログラム中では次のように表されます。

```Haskell
type Subst = [(Tyvar,Type)]
```

型変数とそれに対応する型のTupleのListとして表されていますね。

次に空のSubstituationの定義です。

```Haskell
nullSubst :: Subst
nullSubst = []
```

次に演算子の定義が出てきますね。

```Haskell
(+->) :: Tyvar -> Type -> Subst
u +-> t = [(u,t)]
```

これは型変数uと型tを受け取りSubstituationを作る演算子です。

次にこのようなクラスの定義が出てきます。

```Haskell
class Types t where
    apply :: Subst -> t -> t
    tv :: t -> [Tyvar]
```

apply関数はSubstituationを型変数に適用し、型変数を対応する型で置き換える関数です。
tv関数は受け取った型等に表れる型変数をリストにして返す関数です。
普通の型に対しては次のように定義されます。

```Haskell
instance Types Type where
    apply s (TVar u) =
        case lookup u s of
            Just t -> t
            Nothing -> (TVar u)
    apply s (TAp l r) = TAp (apply s l) (apply s r)
    apply _ t = t

    tv (TVar u) = [u]
    tv (TAp l r) = tv l `union` tv r
    tv t = []
```
TypesのinstanceのListもTypesのinstanceにしてこれらの関数を適用できるようにしておきます。

```Haskell
instance (Types a) => Types [a] where
    apply s = map (apply s)
    tv = nub.concat.map tv
```

簡単ですね。
あと２つこの章ではサポート関数を扱います（少し難しくなってきます）

次は演算子の定義です

```Haskell
infixr 4 @@
(@@) :: Subst -> Subst -> Subst
s1 @@ s2 = [(u,apply s1 t) | (u,t) <- s2] ++ s1
```

```apply (s1 @@ s2) == apply s1 . apply s2```
となるようなSubstituationをこの演算子を用いることで生成することができます。
やってることは簡単で、s2の型をs1で適用したものにs1を結合しているだけです。

もう一つ関数があります

```Haskell
merge :: (Monad m) => Subst -> Subst -> m Subst
merge s1 s2 =
    if agree 
        then return (s1 ++ s2)
        else fail "merge fails"
    where
agree = all (\v -> apply s1 (TVar v) == apply s2 (TVar v)) (map fst s1 `intersect` map fst s2)
```
まず、agreeでは、

```Haskell
(map fst s1 `intersect` map fst s2)
```

によってs1とs2に共通する型変数を抜き出しています。
そして

```Haskell
all (\v -> apply s1 (TVar v) == apply s2 (TVar v))
```

によって共通する型変数すべてにs1とs2を適用した結果が等しいことを確かめています。
そして、このチェックを通ると```s1 ++ s2```によりSubstituationを結合していますね。


## なぜこのチェックが必要なのか

仮にこのチェックをせずに、```s1 ++ s2```で結合した場合を考えます。
型変数にs1とs2を適用した結果が等しいことが保証されていません。
すると、s1を適用した場合とs2を適用した場合では違った動作をしてしまう可能性が出てくるわけです。
そのような動作をしてしまうと、```apply (s1 ++ s2)```と```apply (s2 ++ s1)```の結果が違ってしまいます（先に出てきた方を型変数に適用するので）
なのでこのチェックが必要なのです。

# Section 6 : Unification and Matching 

ここでは単一化とmatchingについて説明しています。
まず、単一化とは２つの型、t1とt2に対し
```apply s t1 == apply s t2```
となるようなSubstituation```s```を見つける動作のことです。
この論文では、次のようなコードによって実装されています。

```Haskell
mgu :: (Monad m) => Type -> Type -> m Subst
varBind :: (Monad m) => Tyvar -> Type -> m Subst

mgu (TAp l r) (TAp l' r') =
    do
        s1 <- mgu l l'
        s2 <- mgu (apply s1 r) (apply s1 r')
        return (s2 @@ s1)
mgu (TVar u) t = varBind u t
mgu t (TVar u) = varBind u t
mgu (TCon tc1) (TCon tc2)
    | tc1 == tc2 = return nullSubst
mgu _ _ = fail "types do not unify"

varBind u t
    | t == (TVar u) = return nullSubst
    | u `elem` tv t = fail "occurs check fails"
    | kind u /= kind t = fail "kinds do not match"
    | otherwise = return (u +-> t)
```

やたら長いコードが出てきましたが、実はこれ、やってる事自体はとても簡単です。
型を辿ってどちらかに型変数が出てきた場合、型変数からもう片方の型へのSubstituationを作っているだけです。
と言われてもいまいちピンとこない方もいるかもしれないので簡単に具体例を出して図説してみます。

例えば次のような整数を二つ受けとり加算する関数があるとします

```Haskell
add :: Int -> Int -> Int
add x y = x + y
```
![Untitled Diagram(2).png](https://qiita-image-store.s3.amazonaws.com/0/194988/245a78cd-6e06-762b-9ef5-4a3b8ebde8da.png)

そしてこの関数に型が不明な変数```x```と```2```を適用してみます

```Haskell
add x 2
```

この時、```x```の型は次のように推論されます。

まず、```x```の型がわからないのでxの型を型変数```v0```とします。
すると、単一化をおこなう関数mguに渡される型は次のように表せます

![Untitled Diagram(1).png](https://qiita-image-store.s3.amazonaws.com/0/194988/22e256db-3be0-8160-c960-7eb32ef2f799.png)

mguでは型変数が出てきた場合、もう片方の型へのSubstituationを生成するので。

![Untitled Diagram(2).png](https://qiita-image-store.s3.amazonaws.com/0/194988/1e577740-6ee0-01da-2376-5c26d727bf58.png)

このように```v0```から```Int```へのSubstituationができるわけですね。
このSubstituationを用いてt2を```apply```すると（applyは型変数を対応する型で置き換える関数ですね）
```v0```は```Int```に置き換えられて```x```の型は```Int```というように推論されます。
このSubstituationを生成するのがmgu関数の役割です。

あと、mgu関数でもう一つ解説しておきたい部分があります。

```Haskell
u `elem` tv t = fail "occurs check fails"
```
ここでoccurs checkと書いてありますね。
occurs checkとはSubstituationを作る際、型の中に対応する型変数が現れないかチェックすることです。
なぜこのチェックが必要なのかというと定義が無限にループしてしまうからです。

この章ではもう一つ関数が出てきますね。


```Haskell
match (TAp l r) (TAp l' r') =
    do
        sl <- match l l'
        sr <- match r r'
        merge sl sr
match (TVar u) t
    | kind u == kind t = return (u +-> t)
match (TCon tc1) (TCon tc2)
    | tc1 == tc2 = return nullSubst
match _ _ = fail "types do not match"
```

あれ、この関数、とてもmgu関数に似ていませんか？
何が違うのかというと、mgu関数では```(TVar u) t```と```t (TVar u)```どっちのパターンもありましたが、こちらには```(TVar u) t```のみしかありません。
これはどういうことかというと、第一引数で受け取った型を第二引数で受け取った型に合わせるようなSubstituationを生成するということです。
つまり、この関数は```apply s t1 = t2```となるような```s```を生成する関数ということですね。

# Section 7 : Type Classes, Predicates and Qualified Types

このセクションでは型クラスや修飾された型についての実装について述べてあります。