3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

scalaz - Leibniz

Last updated at Posted at 2015-01-25

Leibniz

ライプニッツの同値関係を表したものだ。scalaの=:=もそうだが、これの強化版が===だ。

使う前にまず確認用の型を定義してみよう。

trait Space
trait Earth extends Space
trait Japan extends Earth
trait Tokyo extends Japan

早速つかってみよう。

implicitly[Space === Space]
// ok
implicitly[Space === Earth]
// err

implicitly[Space =:= Space]
// ok
implicitly[Space =:= Earth]
// err

うん。scalaの=:=と同じだ。

=:=は関数として取得できる。

===はLeibnizのインスタンスを返す。しかし関数にもなれる。

scala> implicitly[Space === Space]
res1: scalaz.Leibniz.===[Space,Space]

scala> implicitly[Space === Space]: (Space => Space)
res2: Space => Space = <function1>

Leibnizの定義をみてみよう。

sealed abstract class Leibniz[-L, +H >: L, A >: L <: H, B >: L <: H]

type ===[A,B] = Leibniz[, , A, B]

⊥はNothingで、⊤はAnyだ。そしてAとBをとるのが===だと。

そしてLeibnizは、ちょっと複雑なので具体化してみてみよう。

Leibniz[
  -Nothing, // -L
  +Any >: Nothing, // +H
  A >: Nothing <: Any,
  B >: Nothing <: Any
]

AはNothing以上で、Any以下と。Bも同様。

つまりScala界での境界を表している。

すべてのクラスはAnyの子だというのは知っていたが、Nothingの親というのは気づかなかった。確かにそうか。

ということは、自分で世界を定義できるのでは。

type =::=[A >: Tokyo <: Space, B >: Tokyo <: Space] = Leibniz[Tokyo, Space, A, B]

implicitly[Space =::= Space]
//ok

implicitly[Any =::= Any]
//err

いや自分で範囲を決めたらそうなるか。

合成してみよう

composeはtransを使ってるのでtransをみてみる。

def trans[L, H >: L, A >: L <: H, B >: L <: H, C >: L <: H](
  f: Leibniz[L, H, B, C],
  g: Leibniz[L, H, A, B]
): Leibniz[L, H, A, C]

推移律だからA=>Cが返るようだ。

しかし===はAとBが同じでないといけないものだ。

つまり、A=>CはAでしかない。

分かりやすいように型aliasをつけてみよう。

type SpaceA = Space
type SpaceB = Space
type SpaceC = Space
trans[Nothing, Any, SpaceA, SpaceB, SpaceC](implicitly[SpaceB === SpaceC], implicitly[SpaceA === SpaceB])
// Leibniz[Nothing,Any,SpaceA,SpaceC]

まぁA=>Cになってるけど、ようはSpaceに変わりない。

一応composeも。

implicitly[SpaceB === SpaceC] compose implicitly[SpaceA === SpaceB]
// Leibniz[⊥,⊤,SpaceA,SpaceC]

うんSpaceに変わりない。

AとBを逆にする関数もあった。symmだ。

Leibniz.symm[Nothing, Any, SpaceA, SpaceB](implicitly[SpaceA === SpaceB])
// Leibniz[Nothing,Any,SpaceB,SpaceA]

逆にしても同じものは同じだ。

ほかのインスタンス関数

関数にして返すonFというのがあった。

implicitly[Space === Space].onF[Int](i => new Space{})
// Int => Space

(onCovとonContraに関しては嘘書きましたxuweiさんにコメントよんでくださいm(_ _)m)↓

onCovというのもあったが、共変のものを受け取るようだがFunctorだったらなんでもいいという定義で、反変も受け取れる。これが間違っているのか意図的なのか分からない。

implicitly[Space === Space].onCov(Option(new Space{}))
// Option[Space]

反変を受け取るものもあった。これは反変しか受け取れないようだ。そういう定義なっていた。

implicitly[Space === Space].onContra({(s:Space) => s})
// Space => Space

implicitly[Space === Space].onContra(Option(new Space{}))
// err

lift

ちゃんとOptionの中身も検査するために必要な型が多いようだ。

Leibniz.lift[Nothing, Nothing, Any, Any, Option, SpaceA, SpaceB](implicitly[Space === Space])
// Leibniz[Nothing,Any,Option[SpaceA],Option[SpaceB]]

liftされた。

型を2つとる型もいけるようだ。しかしlift2はやばい。定義からしてやばい。

def lift2[
    LA, LB, LT,
    HA >: LA, HB >: LB, HT >: LT,
    T[_ >: LA <: HA, _ >: LB <: HB] >: LT <: HT,
    A >: LA <: HA, A2 >: LA <: HA,
    B >: LB <: HB, B2 >: LB <: HB
  ](
    a: Leibniz[LA, HA, A, A2],
    b: Leibniz[LB, HB, B, B2]
  ) : Leibniz[LT, HT, T[A, B], T[A2, B2]]

使うほうもやばい。11個の型パラメータが必要だ。

Leibniz.lift2[Nothing, Nothing, Nothing, Any, Any, Any, Writer, SpaceA, SpaceA, SpaceB, SpaceB](implicitly[SpaceA === SpaceA], implicitly[SpaceB === SpaceB])
// Leibniz[Nothing, Any, Writer[SpaceA,SpaceB], Writer[SpaceA,SpaceB]]

lift3もあるが、もういいだろう。

unlift

unliftする関数もあった。lowerとかいうやつだ。

Leibniz.lower[Nothing, Any, Option, SpaceA, SpaceB](implicitly[Option[SpaceA] === Option[SpaceB]])
// Leibniz[Nothing, Any, SpaceA, SpaceB]

unliftできた。

数学的すぎてついていけない感じだ。

今日はここまで。

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?