Scala Advent Calendar 2015、15 日目です。
Existential Types について書くかなーと思ってたんですがそんな誰も使わない機能について書いても仕方ないのでやめました。同程度に使われそうにないことについて書きます。
#Context Bound による制約
今更なんやかんやと説明する必要もないでしょう。
trait Show[T] { def show(t: T): String }
implicit val stringShow = new Show[String] {
def show(s: String) = s
}
def show[T : Show](t: T) = implicitly[Show[T]].show(t)
show("Hello") // => Hello
よくあるやつです。
#インスタンスが存在しないことを保障する
割と書ける。
trait !:[T, F[_]]
object !: {
implicit def notFound[F[_], T]: !:[T, F] = new !:[T, F] { }
implicit def found[F[_], T](implicit ev: F[T]): !:[T, F] = ???
}
def cannotShow[T](t: T)(implicit ev: T !: Show) = ()
cannotShow(new {})
cannotShow("Hello") // => !!!Error!!!
ContextBound とは制約として逆なので "!:" にしてみました。型注釈のための ":" が近くにあるおかげで最高に読みづらいですね…
#何に使うの
Show のような、機能を提供するための物に対して使うことはまずないでしょう。あったらびびる。
Implicits は型に対するある種の制約、述語として利用することができます。そのような性質を持つものとして、Predef では "=:=" や "<:<" 等が定義されていますね。
type IsInt[T] = T =:= Int
implicitly[IsInt[Int]]
implicitly[IsInt[Char]] // => !!!Error!!!
trait Base
trait Inherited extends Base
type IsInheritedBase[T] = T <:< Base
implicitly[IsInheritedBase[Inherited]]
implicitly[IsInheritedBase[Nothing]]
implicitly[IsInheritedBase[Any]] // => !!!Error!!!!
これにさっき定義した "!:" を使うと、こんな具合に。
implicitly[Char !: IsInt]
implicitly[Any !: IsInheritedBase]
本当は下のように書けるといいんですが、Type Projection 使わないと無理です。使うとあんまり綺麗じゃなくなるので今回はしませんでした。
implicitly[Not[IsInheritedBase][Any]] // ill-formed
#実践編
真面目に書こうと思ってたんですが面倒になってきた。
Phantom Types とかと組み合わせて色々できるんじゃないでしょうか。And は複数並べればいいから必要ないけれど、Or は欲しいなあ。
trait Or[F[_], G[_], T]
implicitly[Or[IsInheritedBase, IsInt, Int]]
というわけで Or のインスタンスを返す implicit な定義を書くのは各自の宿題ということで。どちらも満たす場合を考慮すると、ちょっと面倒になるかな。
#まとめとか言い訳とか
最初に書いたとおり、登録したときは Existential Types について書くつもりで、実際ちょっと書いたのですが、あまりにもつまらない。
それで何書こうかなあとぼーっとしてたら、そういえばこの前ドワンゴ秘蔵のトランザクションが云々で Read と ReadWrite の権限を継承関係による制約で扱ってたけど、権限周りのルールがもっと複雑な場合これじゃ無理だよなー、でも普通に書いても面白くない…
などと思ってたら「インスタンスが存在しない制約」とかいう、わけのわからないものが思いついたので、とりあえず書いてみました。誰か頑張って役に立ててみてください。
おしまいです。明日は takezoux2 さんですが、明日って Scala 勉強会 in 東京とその忘年会のはず…大丈夫なのだろうか…!