Scalaにおけるカインド(Kind)  - Upper Bound, Lower Boundでのカインドを調べてみる 

# 前置き

カインドの説明に関してはありませんので、「カインドとは何？」ということを知りたい人のお役には立てません。


# この記事で調べたこと


調べたかったことは、「**Upper BoundやLower Boundがある、Scalaの型システムの中で、どのようにカインドが表現されるか**」です。

**値**に関する制約は**型**で表現し、
**型**に関する制約は**カインド**で表現する


だと思うので、Upper BoundとLower Boundという、型に関する制約はカインドに現れるはずだと思いました。



（ここで言う制約というのが意味しているのは、`Int`という型は`1`, `-2`, `18`などの値は受け付けるが、`"hello"`, `'c'`, `true`などの値は受け付けないという、値に対して制約があるという意味で使ってます。カインドで言えば、`*`というカインドは`Int`, `Char`, `List[Double]`という型は受け付けるが、`List`や`Set`のようは`*`でないものを受け付けない型に対する制約がある、という意味です。）

# scalaのバージョン

 Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_65).

# Boundの指定なし

まずは、Boundの指定なしのカインドを見てみます

以下の`A`にはどんな型でのパラメータとし取れるので、`A[Int]`でも`A[String]`でも`A[List[Char]`でもカインドが`*`ならなんでもOKです。

```scala
class A[T1]
```

よって、`:k -v A[_]`してみると、以下のように`* -> *`だということがわかります。

```scala
scala> :k -v A[_]
A's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.


```


# Upper Bound指定

以下のような`B`だと、今さっきのように、`B[Int]`, `B[List[Char]]`は受けません。


```scala
class B[T1 <: CharSequence]
```

`B`は`CharSequence`をUpper Boundに持つので、以下のような指定なら可能です。

* `B[String]`
* `B[StringBuilder]`
* `B[StringBuffer]`

どんな型でも受け付けるわけではないから、`B`のカインドはただの`* -> *`ではだめのはずです。最初の型引数の`*`のには何らかの制約を表す記法を導入しているはずです。

以下が`:k -v B[_]`の結果です。

```scala
scala> :k -v B[_]
B's kind is F[T1 <: CharSequence]
*(CharSequence) -> *
This is a type constructor: a 1st-order-kinded type.
```

これで、`B`のカインドは`*(CharSequence) -> *`と表すということがわかりました。


ここからUpper Boundは、` *(CharSequence)`とScalaの世界では表すだということがわかりました。では、Lower Boundだとどういう記法でカインドを表すのでしょうか？


# Lower Boundの指定

以下ような`C`だと、`Int`より上の型しか受けません。そのため、`C[String]`とか`C[Double]`とかはダメです。

```scala
class C[T1 >: Int]
```

妥当なものは、以下のようものたちです。

* `C[Int]`
* `C[AnyVal]`
* `C[Any]`


以下は`:k -v C[_]`の結果です。

```scala
scala> :k -v C[_]
C's kind is F[T1 >: Int]
*(Int, Any) -> *
This is a type constructor: a 1st-order-kinded type.
```

これで、`C`のカインドは`*(Int, Any) -> *`だということがわかりました。


` * (Int, Any)`という風に`Any`が出てきてしまっているのは、自動的に上は`Any`以上ないので、Upper Boundが出てきてしまっているのでしょう。（じゃあ、なぜ今さっきの`B`が` *(Nothing, CharSequece) -> *`にならなかったのか不思議...）

だいたい想像できてしまっていますが、Lower BoundとUpper Boundを同時に指定したらどうなるでしょうか？


# Lower, Upper両方ともを指定

以下がクラス定義です。

```scala
class D[T1 >: String <: AnyRef]
```

```scala
scala> :k -v D[_]
D's kind is F[T1 >: String <: AnyRef]
*(String, AnyRef) -> *
This is a type constructor: a 1st-order-kinded type.

```

これで、`*(String, AnyRef) -> *`だとわかりました。やはり` *(Lower, Upper)`と表すのがScalaでの表現のようです。


# まとめ

* Upperだけの指定だと`*(Upper)`
* Lowerだけの指定だと`*(Lower, Any)`
* 両方とも指定すると、`* (Lower, Upper)`


# おまけ - Haskellで型クラスで型パラメータに制約をつける

一応Haskell（GHC）なら、Lower, Upper制限ではないですが、制約つきの型パラメータのある型は作ることできます。その型パラメータのカインドを見てみたいと思います。

`ghci`で以下のように実行してみました。
`DatatypeContexts`言語拡張を使って、`A`型パラメータ`a`には`Ord`型クラスのインスタンスではないといけないという制約をつけました。

```haskell
Prelude> :set -XDatatypeContexts
Prelude> data Ord a => A a = AConstructor a
Prelude> :k A
A :: * -> *
```

型クラス制約はカインドでは現れないみたいです。そのため、Ordインスタンスではない`IO ()`型使って以下のようなこともできてしまいました。

```haskell
type X = A (IO ())
```

しかし、`AConstructor (print "hello")`ようにすると以下のエラーがでます

```
 No instance for (Ord (IO ())) arising from a use of ‘AConstructor’
```

なんとなく想像ですが、データ構築子の`AConstructor`に`Ord`の制約ついているだけで、型構築子`A`には`Ord`の制約がついていない気がします。

---

あと、まだScalaでカインドについてやれることで思いつくことは、「共変・反変」だと思います。この記事では非変な型パラメータしかやっていないので、`A[+T]`とか`B[-T]`とかがどのようなカインドになるか？...

# 参考

* [Scalaの「高カインド型」について調べてみました。](http://qiita.com/poly_soft/items/1d7363b85b04e48a6e24)