独習 Scalaz — 独習 Scalaz 気がついたらある程度読めるようになっていた
scalaz/exampleってディレクトリあったの知らなかった
例えばStateTUsage.scalaではフィボナッチ数列生成をStateモナド使ってやってる例がある
型クラスとかデータ構造とか
- Semigroup : 二項演算で結合則 (scalazなら|+|が二項演算)
- Monoid: Semigroup + 単位元(zero)
- 単位元の左右どちらにappendしても同じ値
- Functor -> fmap (map)
- F map id == F で元に戻る
- 関数f,gについて、F.map(f.compose(g)) == F.map(g).map(f)
- ScalazではFunctorLaw+scalacheck-bindingでテスト出来る
- Applicative -> pure, ap
<*>
- ..
- Foldable[F[_]] -> foldMap/foldRight
- Bind -> bind
>>=
(flatMap) - (Scalazでは)
Monad[F[_]] extends Applicative[F] with Bind[F]
MonadPlus[F[_]] exnteds Monad[F] with ApplicativePlus[F]
-> filter(単位元と畳み込み)- Scalazでは
Plus[F[_]]
(≒Semigroup[A]
)とPlusEmpty[F[_]]
(≒Monoid[A]
)みたいのがある - Writer: run, written, value (3.set(Vector("log")), Vector("log").tell etc...)
- Writer[+W, +A] は、WriterT[Id, W, A] の型エイリアス
- 値を計算しながらログを残すとか
- Readerモナド <=> 関数モナド (全ての関数が共通の情報を読む)
- Stateモナドは関数のラッピングに特化
f: S => (S, A)
type State[S, +A] = StateT[Id, S, A]
- forで個々の演算をStateを引き回さずに連鎖出来る
- TODO ねこはるさんのScalazでTetrisみたいのがこんなんだった
- Eitherモナド
\/
: 失敗の文脈を与えるモナド - Validation <- validation/disjuction ->
\/
- Validationはモナドではなく、Applicative functor
- 最初のイベントの結果を次へと連鎖するのではなく、全イベントを検証する(Formとかetc)
- NonEmptyList
NEL
: ValidationNELにおけるFailure中身はNEL - Kleisli: A => M[B]という型の関数に対する特殊なラッパー
>=>
: alias forandThen
<=<
: alias forcompose
case class Kleisli[M[_], A, B](run: A => M[B])
- ReaderはKleisliの特殊系 -> Mが
Id
となる
- Zipper: Stream向けのものを提供
あるデータ構造の注目点、および周辺の情報を含んでいるデータ構造は Zipper と呼ばれます
- Id: Haskellの
Control.Monad.Identity
- 全てのデータ型はその型のIdとなれる
(0: Id[Int])
- 下の方にあるReaderとKleisliの例とか
|>
に関数を渡したり、visit
にPartialFunction渡したり
- 全てのデータ型はその型のIdとなれる
- Length, Index, Each等の無法者の型クラス
- 型クラスっちゃ型クラスだけど、そんなに有用でないとかそんなん
- モナド変換子: ref Read World Haskell
- Lens: 入れ子になったデータ構造を不変なまま更新する需要
- case classのcopy...copyとかのやつ
- LensはStore(setter/geterのラッパー)を使う
Lens.lensu[A, B]
: Aがオブジェクトで、BがAに含まれる更新されるやつの型- 確かにStateモナドかっていわれたら近い
- LensLawのdoubleSet (2度setしてgetすると2度目にsetしたものが得られる)、なんで最新のものが得られる的な感じじゃないんだろう
- DList: difference list (名前HListっぽいな? )
- 定数時間での追加をサポートするデータ構造
- Idiomatic traversal:
Traverse
- List[A], f: A => Option[A] から Option[List[A]]みたいなの作れるやつ、最高
trait Traverse[F[_]] extends Functor[F] with Foldable[F]
final def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[F[B]] = G.traverse(self)(f)
- Applicativeなインスタンスを持つ型を生成するやつに利用出来るって感じ
- Traverseの
sequence
?haskell sequence :: Monad m => [m a] -> m [a]
<- なるほど〜List(1.some, 2.some).sequence
=>Some(List(1,2))
List(1.some, none).sequence
=>None
- ValidationとTreeの例とか便利過ぎる
- Arrow <=>
Function1[A, B]
,PartialFunction[A, B]
,Kleisli[F[_], A, B]
,CoKleisli[F[_], A, B]
- ScalaにはFunctionNとかあるけど、ScalazにはArrowが別にある
trait Arrow[=>:[_, _]] extends Split[=>:] with Profunctor[=>:] with Category[=>:] { self => ...
trait Category[=>:[_, _]] extends Compose[=>:] { self =>...
A scalaz.Category supporting all ordinary functions, as well as combining arrows product-wise.
- うーん、ArrowがCategoryをmixinする?射に圏がmixinされてるって読んでいいのかな
- 射=Functorではなく...?Arrowはそのへんの抽象化らしい
id
が恒等射、arr
は関数から射を作り、first
は既存の射からペアを取ってペアを返す射を作る***
は2つの射を両方共同じ値に対して実行する事で新しい射を提供する&&&
は同じ値を2つの射に実行してペアを返すような射を作る
- ScalaにはFunctionNとかあるけど、ScalazにはArrowが別にある
Compose[=>:]
: 2つの射を合成するcompose
や>>>
、<<<
などを提供- Composeは型引数1つで、Categoryは型引数を2つ取る型引数を1つ取る高カインド型で、取った1つの型引数(
=>:
)をそのままComposeに渡してるって感じ- 幸いなことに自然と
=>:
が単なる1つの型引数ってのが読めるようになってた(って説明も後ろにあった) trait Foo[F[_,_]] { def foo[A]: A F A }
- 幸いなことに自然と
- Composeは型引数1つで、Categoryは型引数を2つ取る型引数を1つ取る高カインド型で、取った1つの型引数(
Unapply
: 異なるカインド付けされた型の間での型推論のためのものApplicative[M[_]]
<=>Unapply[Applicative, X]
Memo
: メモ化ST
: Stateっぽい?Purely functional mutable state threads.
"Lazy Functional State Threads"
って論文- 状態が可変で上書きされるけど、外から観測出来ないらしい
- STRefはSTモナドの中でしか使えない可変変数を表すデータ構造
- STArrayとかもあって、可変配列に対する処理を外から見えない形で処理出来る
IO
Iteratee
Input
,Step
,Enumerator
,Enumeratee
とかあるしPlayのと用途は同じっぽい- データが連なって来るのを順に処理するって感じ
Iteratee[E,A]
はE
がInput[E]
になってA
が計算結果の方IterateeT[E, F[_], A]
ってのもあって、結果をF
で囲む事もできるし、Id
を使えばそのままの値が得られる- IterateeTを使えば入力処理等も行える:
Iteratee.enumReader[F[_]](r: => java.io.Reader)
Free
: mapしか定義されてないモナド(下の方でもFreeのbindについて見てる)
sealed abstract class Free[S[_], A] { final def flatMap[B](f: A => Free[S, B]): Free[S, B] = this match { case a @ Gosub() => gosub(a.a)(x => gosub(() => a.f(x))(f)) case a => gosub(() => a)(f) }
sealed abstract class FreeInstances extends FreeInstances0 with TrampolineInstances with SinkInstances with SourceInstances { implicit def freeMonad[S[_]:Functor]: Monad[({type f[x] = Free[S, x]})#f] = new Monad[({type f[x] = Free[S, x]})#f] { def point[A](a: => A) = Return(a) override def map[A, B](fa: Free[S, A])(f: A => B) = fa map f def bind[A, B](a: Free[S, A])(f: A => Free[S, B]) = a flatMap f } }
Trampoline
: Function0のFreeモナド- 実装簡単で分かりやすい
- Stackless nanntara <- これもねこはるさんかだれかの何かあった気がする
なんか
- scala repl
:k -v Option
- 型コンストラクタ = 1階カインド型 = プロパーな型になるのに他の型を受け取る型
- 高カインド型 = X[F[A]] = eg ( -> ) -> * = 型コンストラクタを受け取る型コンストラクタ
- FunctorはX[F[A]]なのに、List(F[+A])がfunctorなのはなんで?
- Haskellの
newtype
がTaggedType(@@
)- wrapした値クラスとかに
.value
みたいの呼ばなくて済むのが便利みたいな使い方 A @@ Kilogram
はKilogramってTag付けされたtypeを持つA型のクラス -> A with Tag[Kilogram]Double with Tag[Kilogram]
とかにすると上で挙げた使い方になる- 直接値を渡したりするとコンパイルエラー -> ただのtypeとは違う
- wrapした値クラスとかに
- Disjunction/Conjunction <-> 論理和/論理積
- Tagもある
- Option[A]は、AがSemigroupである場合にMonoidになれる
for { x <- 1 |-> 40 if x...} yield x
って書ける- モナディックな関数群
join[B](implicit ev: A <~< F[B]): F[B]
(flatten)filterM[M[_] : Monad](p: A => M[Boolean]): M[List[A]]
foldLeftM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit M: Monad[G]): G[B]
foldl
のモナド版がfoldM
(foldLeftM
)
- ReaderはKleisliの特殊系とは?多分こんな感じ
val addStufR: Reader[Int, Int] = for { a <- Reader{(_: Int)*2} b <- Reader{(_: Int)+10} } yield a + b addStafR(3) // -> 19 // Idを使った場合と等価 val addStufK: Kleisli[Id, Int, Int] = for { a <- Kleisli{(x: Int) => (x*2: Id[Int])} b <- Kleisli{(x: Int) => (x+10: Id[Int])} } yield a+ b addStafR(3) // -> 19
- Tree: Scalazでは多分木
- TreeのためのZipperはScalazのTreeLoc
- Zipperが何を表現するのか
scala> Stream(1, 2, 3, 4).toZipper >>= {_.next} res25: Option[scalaz.Zipper[Int]] = Some(Zipper(<lefts>, 2, <rights>))
- 10日目モナド変換子...このへんから難しいとこ
- Readerのモナド変換子版であるReaderTをOptionの上に積む
- ReaderTOption:(そもそも
type ReaderT[F[_], E, A] = Kleisli[F, E, A]
)
- ReaderTOption:(そもそも
type ReaderTOption[A, B] = ReaderT[Option, A, B] object ReaderTOption extends KleisliInstances with KleisliFunctions { def apply[A, B](f: A => Option[B]): ReaderTOption[A, B] = kleisli(f) }
- この例何のためにKleisli使うのかとか分かりやすくて良いな (エラー情報消えるからValidationの方が良いけど)
scala> def configure(key: String) = ReaderTOption[Map[String, String], String] {_.get(key)} configure: (key: String)ReaderTOption[Map[String,String],String] scala> def setupConnection = for { host <- configure("host") user <- configure("user") password <- configure("password") } yield (host, user, password) setupConnection: scalaz.Kleisli[Option,Map[String,String],(String, String, String)]
type StateTReaderTOption[C, S, A] = StateT[({type l[+X] = ReaderTOption[C, X]})#l, S, A]
StateT[l, S, A]
whereAnyVal { type l[+X] = ReaderTOption[C, X]}
こんな感じ- Stateだから
S => (S, A)
みたいのを取り扱って、Readerでl
にラップするって感じか StateTReaderTOption[C, S, A]
な時、StateTReaderTOption.flatMap{ s: S => ..}
で、S=List[A]
にすると、こいつの状態を変化させる(言い方おかしいか)事でStackとかを実装出来る
- applicative functorは最初idiomと名付けられた idiomatic <=> applicative
- ScalazはMonoid[m].applicativeを実装して、どんなMonoidでもapplicativeに変換出来る:
- monoidal applicative functor
- ↓ Monoid.applicative
final def applicative: Applicative[({type λ[α]=F})#λ] = new Applicative[({type λ[α]=F})#λ] with SemigroupApply { def point[A](a: => A) = zero }
- Applicative同士(F,G)の積(product) =
[x](F[x], G[x]])
はApplicativeApplicative[List].product[Option].point(1)
=(List(1),Some(1)): (List[Int], Option[Int])
- 合成した
[x]F[G[x]]
もApplicativeApplicative[List].compose[Option].point(1)
=List(Some(1)): List[Option[Int]]
Int => Int を Int => A として扱うことで applicative として扱えることが知られている:
scala> Applicative[Function1[Int, Int]] <console>:14: error: Int => Int takes no type parameters, expected: one Applicative[Function1[Int, Int]] ^ scala> Applicative[({type l[A]=Function1[Int, A]})#l] res14: scalaz.Applicative[[A]Int => A] = scalaz.std.FunctionInstances$$anon$2@56ae78ac})]
- HaskellでのFree
data Free f r = Free (f (Free f r)) | Pure r
instance (Functor f) => Monad (Free f) where return = Pure (Free x) >>= f = Free (fmap (>>= f) x) (Pure r) >>= f = f r
- Freeのbindを読み解く: Scalazの中でFreeは
Return
とSuspend
とGosub
の直和でbindの結果は常にGoSub
になる
object Free extends FreeInstances with FreeFunctions { /** Return from the computation with the given value. */ private[scalaz] case class Return[S[_], A](a: A) extends Free[S, A] /** Suspend the computation with the given suspension. */ private[scalaz] case class Suspend[S[_], A](a: S[Free[S, A]]) extends Free[S, A] /** Call a subroutine and continue with the given function. */ private sealed abstract case class Gosub[S[_], B]() extends Free[S, B] { type C val a: () => Free[S, C] val f: C => Free[S, B] } def gosub[S[_], A, B](a0: () => Free[S, A])(f0: A => Free[S, B]): Free[S, B] = new Gosub[S, B] { type C = A val a = a0 val f = f0 } ...
- でもって
flatMap
は以下のような処理が行われる- thisが
Gosub
の時: Gosubはa
が値で、a
の中身を変換するf
が本ってて感じ- 値
a
はそのまま、それと"次の計算"内容を渡したGosub
を返す - "次の計算"とは、何かを引数に自身が持つ関数
a.f
に適用したのと、新しい計算処理f
を渡したGosub
となる
- 値
- thisが
Suspend
orReturn
の時- 自身をaに渡し、Freeを生成する計算fをまとめて
gosub
に渡し、Gosub
を作る
- 自身をaに渡し、Freeを生成する計算fをまとめて
- thisが
final def flatMap[B](f: A => Free[S, B]): Free[S, B] = this match { case a @ Gosub() => gosub(a.a)(x => gosub(() => a.f(x))(f)) case a => gosub(() => a)(f) }
- ↑所感
- なんかあれだな、
Gosub
に処理を渡すけどそいつもFreeだから、Freeなままって、まぁ...まぁなるほどって感じ - Scalazのデータ構造の作り方の問題から見え方が変わってるかもしれない
- なんかあれだな、
- Freeモナドのまとめ?
ほー
- 13日目(importガイド)
Scala では import は 2つの目的で使われる: 1. 値や型の名前をスコープに取り込むため。 2. implicit をスコープに取り込むため。 implicit には僕が考えられる限り 4つの使い方がある: 1. 型クラスインスタンスを提供するため。 2. メソッドや演算子を注入するため。(静的モンキーパッチ) 3. 型制約を宣言するため。 4. 型の情報をコンパイラから取得するため。 implicit は以下の優先順位で選択される: 1. プレフィックス無しでアクセスできる暗黙の値や変換子。ローカル宣言、import、外のスコープ、継承、および現在のパッケージオブジェクトから取り込まれる。同名の暗黙の値があった場合は内側のスコープのものが外側のものを shadow する。 2. 暗黙のスコープ。型、その部分、および親型のコンパニオンオブジェクトおよびパッケージオブジェクト内で宣言された暗黙の値や変換子。
scalaz.Scalaz._
以下のtraitをmixinして、importすることで色んなimplicitや関数・演算子をグローバルに用いれるようにしている
- StateFunctions
- get/putとかその辺の関数をグローバル関数として扱えるようにする
- std.AllFunctions
- ListFunctionsとかなんか色々スコープに取り込む系
- IdInstances
type Id[+X] = X
とかIdのための型クラスのインスタンス定義がある
- std.AllInstances
- 標準データ構造に対する型クラスのインスタンスのミックスイン
- AnyValInstances, OptionInstances, ListInstances,...
- syntax.ToTypeClassOps
- syntax.ToDataOps
- syntax.std.ToAllStdOps
圏論
前に圏論勉強会 @ ワークスアプリケーションズ見たし適当に流す
(参加出来るとこでこの勉強会もっかいして欲しい)
- 圏、有限集合、射、対象
- ここでは射mapを射arrowとして用いる
- 射fに対して、fのdomain A -f-> B fのcodomain
- domain = codomainの射を自己順同型射(endomorphism)
- 集合Aの全てのaにおいてf(a)=aであるものを恒等射(identity arrow) 1_A
- scala.Predef.identity
- 圏の変化を全て担ってるのが射の合成(composition of maps)
- scala.Function1のandThenかcompose
- A -g-> A -f-> B <=> A -f∘g-> B <=> fとgの合成射
- 圏の構成要素: 単位元律(The identity laws)と結合律(The associative law)を満たす
- 対象 objects: A,B,C
- 射 arrows: f: A => B
- 恒等射 identity arrows: 1_A: A => A
- 射の合成
- 単集合 singleton: 唯一の要素elementのみを持つ集合 <=> 1
- 点pointは1 => Xという射
- Scalaでは(): Unit <=> 値とはUnit => Xである
関数型プログラミングをサポートする言語における第一級関数は、関数を値として扱うことで高階関数を可能とする。圏論は逆方向に統一して値を関数として扱っている。 - なるほどたしかに
- 同型射(isomorphism): f: A => Bに対して g∘f=1_A とf∘g=1_B の両方を満たすg: B => Aが存在する時のf
- 1つでも同型射f: A => Bが存在する時、2つの対象AとBは同型(ispmorphic)
- ScalazではIsomorphismsの
Iso
や集合の時はtype IsoSet[A, B] = Iso[Function1, A, B]
- ScalazではIsomorphismsの
- 決定問題(determination problem / extension)
- f: A => B, h: A => C、h=g∘fであるときのgを全て挙げよ
- 選択問題(choice problem / lifting)
- g: B => C, h A => C, h=g∘fであるときのfを挙げよ???(何か違う気がするから後で見る)
- レトラクション retraction
- f: A => B、r∘f=1_Aが成り立つ射r: B => Aのこと
- セクション section
- f: A => B, f∘s=1_Bが成り立つ射s: B => Aのこと
- 全射 surjective
- f: A => Bに関して「y: T => Bに対してf∘x=yが成り立つ射x: T => Aが存在する」時、「fはTからの射に関して全射」
- 単射 injective
- 「任意の射のペアx1: T => Aとx2: T => Aに対してf∘x1 = f∘x2ならばx1 = x2である」時、「fはTからの射に関して単射」
- モノ射 monomorphism
- エピ射 epimorphism: 全射の一般形
- 射fが「任意の射のペアt1: T => Aとt2: T => Aに対してt1∘f = t2∘fならばt1 = t2 である」という条件が全てのTに成り立つ時、エピ射であるという
- 自己同型射 endmorphism
- 自己準同型 endomorphismであり、かつ同型 isomorphismである射のこと
- 冪等射
- 自己順同型射 endomorphisについてe∘e=eが成り立つ時冪等射
- 自己準同型についてTODO
色んな圏
具体的なの
- Sets: 集合と全域関数の圏
- Sets_fin: 全ての有限集合とその間の全域関数(上の方の例はこれ)
- Post: 数学でよく見る構造的集合
- 半順序集合 partially ordered set(poset)である集合Aは反射律、推移律、反対称律を満たす二項関係を持つ
- 関数が単調(monotone)である限り、対象は圏の中にとどまるため、「構造」が保存される <- とどまる???
Cat
: 圏と関手functorの圏モノイド
: 集合Mで、MxM=>Mの二項演算と特定の単位元(unit)を持ち、結合則と単位元則(って言うのかな?)を満たすGroups
: 群- モノイドGのうち、全ての要素gに対して逆射を持つもの
- Gは唯一つの対象を持つ圏で、全ての射が同型射
- Scalazには無くて、Spireってのが内容被ってるらしいけど、今見たらSpireなかった
抽象的なの?
- 普遍性 universal property / 普遍写像性 universal mapping property =>
UMP
- 全ての射が正しく合成出来ること
- 始対象 initial 0
- 任意の対象Cに対して 0 => Cを満たす一意の射を持つ対象
- 終対象 terminal 1
- 任意の対象Cに対して C => 1を満たす一意の射を持つ対象
- 全ての始対象(終対象)は同型を除いて一意
- 同じ圏内に始対象C,C'があるならば...0と0'が一意に同型なのが明らか
Sets圏における始対象は空集合で、任意の単集合が終対象となる
積について
任意の要素 c ∈ A × B に対して、c = (fst ∘ c, snd ∘ c) - 面白い
- 任意の圏Cにおいて、対象P, A, Bに対し p_1: P => A, p_2: P => Bとなる時...
- x_1: X => A, x_2: X => Bに対し x_1=p_\1μ かつ x_2=p_2μが成立する一意の射μ: X => Pが存在する(普遍)
AとBの積の全てが同型をの除いて一意
双対性
- 逆圏? opposite category / dual <=> 双対圏 <=> Cop
- 任意の圏Cと同じ対象を持つが、Cop内の射はCではf: D => C
- CopはCの射を形式的に逆向きにしたもの
- 双対性原理
- 余積 coproduct: 積の双対 <=> 直和とも言う (
co-
が余
になる)- 積はscala.Product, case classなどの直積型
- 余積は直和型 sum type, union type(, sealed trait)に関連する
- Scalaの
A with B
は論理積なるほど確かに <- これも最近ねこはるさんのなんかあったな- Scalaz.UnionTypes、既に無かったので当時のを見る
- 否定は
A => Nothing
- Scalazの
\/
も直和型、はい Scalazには
Coproduct
もある: 型コンストラクタのためのEither?どゆこと?小さい圏と大きい圏
- 任意の圏Cにおいて、Cの対象の集まりC_0と、Cの者の集まりC_1が"集合"である時、小さい圏という
- その他の場合は大きい圏
- Hom集合 Hom-set: Hom(A, B): 対象AとBの射の集合
任意の圏 C は、C の全ての対象 X と Y について、HomC(X, Y) = { f ∈ C1 | f: X = Y } という集まりが集合であるとき、局所的に小さいといい、これを hom集合という
- 自然変換 natural transformation: 関手の射
- 圏C,Dについて、関手C=>Dを新たな圏の対象として考えて、これらの対象間の射を自然変換と呼ぶ
- Scalazの
NaturalTransformation