From c757b370d6ecbb4d75b7a68f3a0a41882c9e6ede Mon Sep 17 00:00:00 2001 From: lemastero Date: Sat, 17 Jul 2021 03:31:37 +0200 Subject: [PATCH 01/10] wild magic --- src/main/scala/ct_other_impls/WildMagic.scala | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 src/main/scala/ct_other_impls/WildMagic.scala diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala new file mode 100644 index 00000000..7084398b --- /dev/null +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -0,0 +1,178 @@ +package ct_other_impls + +import ct_other_impls.Wizard.{ConstUnit, Id} + +/* +Are there any constructions in Category Theory something like: + +Given: +C category +F, G : functors in C +M functor category of category C + +P morphism that map product of any two objects a and b from Category C into object a in M + */ + +object Wizard { + type Id[A] = A // identity functor - what is it in functor category + type ConstUnit[A] = Unit // terminal object in functor category + + type Void[A] = Nothing // initial object in functor category +} + +object WizardData { + case class Reader[-A, +B](run: A => B) // Profunctor-ish? + case class Op[+A, -B](run: B => A) // Profunctor-ish? + case class Ap[+F[+_], -A, +B](run: F[A => B]) // Profunctor-ish? + case class ContraAp[+F[-_], +A, -B](run: F[A => B]) // Profunctor-ish? + case class ApOp[+F[+_], +A, -B](run: F[B => A]) // Profunctor-ish? + case class Kleisli[+F[+_], -A, +B](run: A => F[B]) // Profunctor-ish? + case class Kleisli2[+F[-_], -A, -B](run: A => F[B]) // Nifunctor-ish? + case class ContraKleisli[-F[+_], -A, +B](run: F[A] => B) // Profunctor-ish? + case class CoKleisli[-F[-_], +A, +B](run: F[A] => B) // Bifunctor-ish? + case class Zip[+F[+_], +A, +B](run: F[(A, B)]) // Bifunctor-ish? + case class Alt[+F[+_], +A, +B](run: F[Either[A, B]]) // Bifunctor-ish? +} + +object WizardTypes { + type Arrow[A, B] = A => B // = Function1[A,B] + type Kleisli[F[_],A,B] = A => F[B] // = Function1[A,F[B]] + type CoKleisli[F[_],A,B] = F[A] => B // = Function1[F[A],B] + type Ap[F[_], A, B] = F[A => B] // = F[Function1[A,B]] + + type Op[A, B] = B => A // = Function1[B,A] +// type OpKleisli[F[_],A,B] = B => F[A] // = Function1[B,F[A]] +// type OpCoKleisli[F[_],A,B] = F[B] => A // = Function1[F[B],A] + type OpAp[F[_], A, B] = F[B => A] // = F[Function1[B,A]] + + type Zip[F[_], A, B] = F[(A, B)] // = F[Tuple2[A,B]] + type Alt[F[_], A, B] = F[Either[A, B]] // = F[Either[A,B]] + + type Pure[F[_], B] = B + type Effect[F[_],B] = F[B] +} + +// F F A => B S[F[_], G[_], A, B] = A => B Functor +// F F B => A S[F[_], G[_], A, B] = B => A Contravariant + +// F F A => F[B] S[F[_], G[_], A, B] = A => F[B] FlatMap +// F F F[A] => B S[F[_], G[_], A, B] = F[A] => B CoflatMap + +// F F F[(A, B)] S[F[_], G[_], A, B] = F[(A, B)] Zip +// F F F[A \/ B] S[F[_], G[_], A, B] = F[A \/ B]] Alt + +// F F F[A => B] S[F[_], G[_], A, B] = F[A => B] Ap (Apply) +// F F F[B => A] S[F[_], G[_], A, B] = F[B => A] Divide + +// ConstUnit F B S[F[_], G[_], A, B] = B Pointed (Pure, Applicative) +// ConstUnit Id F[B] S[F[_], G[_], A, B] = F[B] CoPointed (CoApplicative) + +trait Wizard[F[_], G[_]] { + type Spell[_[_], _[_], _, _] // transform 2 type constructors and 2 regular types into sth + def doMagic[A, B](f: Spell[F, G, A, B]): F[A] => G[B] +} + +trait Functor[F[_]] extends Wizard[F, F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Arrow[AA,BB] // AA => BB + + // map(fa)(identity) == fa doMagic(identity)(fa) == fa + // map(map(fa)(f))(g) == map(fa)(f andThen g) doMagic(g)(doMagic(f)(fa)) == doMagic(f andThen g)(fa) + def map[A, B](fa: F[A])(f: A => B): F[B] = doMagic[A, B](f)(fa) +} + +trait FlatMap[F[_]] extends Wizard[F, F] { // Monad + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Kleisli[FF,AA,BB] // AA => FF[BB] + + // flatMap(flatMap(fa)(f))(g) == flatMap(fa)(a => flatMap(f(a))(g)) + def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = doMagic[A, B](f)(fa) +} + +trait Zip[F[_]] extends Wizard[F, F] { // Apply + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Zip[FF,AA,BB] // FF[(AA, BB)] + def tuple2[A, B](fa: F[A])(fab: F[(A, B)]): F[B] = doMagic[A, B](fab)(fa) +} + +trait Alt[F[_]] extends Wizard[F, F] { // Alt + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Alt[FF,AA,BB] // FF[Either[AA, BB]] + def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = doMagic[A, B](fab)(fa) +} + +trait Ap[F[_]] extends Wizard[F, F] { // Apply + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Ap[FF,AA,BB] // FF[AA => BB] + def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = doMagic[A, B](f)(fa) +} + +trait CoFlatMap[F[_]] extends Wizard[F, F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.CoKleisli[FF,AA,BB] // FF[AA] => BB + def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = doMagic[A, B](f)(fa) +} + +// TODO below 2 do not make any sense +//trait ContraCoflatMap[F[_]] extends Wizard[F, F] { +// type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpKleisli[FF,AA,BB] // BB => F[AA] +// def contraExtend[A, B](fa: F[A])(f: B => F[A]): F[B] = doMagic[A, B](f)(fa) +//} +//trait OpCoKleisliWizard[F[_]] extends Wizard[F, F] { +// type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpCoKleisli[FF,AA,BB] // F[BB] => AA +// def contraExtend[A, B](fa: F[A])(f: F[B] => A): F[B] = doMagic[A, B](f)(fa) +//} + +trait Contravariant[F[_]] extends Wizard[F, F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Op[AA,BB] // BB => AA + + // doMagic(identity)(fa) == fa + // contramap( contrampa(fa)(f) )(g) == contramap(fa)(f andThen g) + def contramap[A, B](fa: F[A])(f: B => A): F[B] = doMagic[A, B](f)(fa) +} + +trait Divide[F[_]] extends Wizard[F, F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpAp[FF,AA,BB] // FF[BB => AA] + def contraAp[A, B](fa: F[A])(f: F[B => A]): F[B] = doMagic[A, B](f)(fa) +} + +// TODO Pointed and CoPointed break symmetry + +trait Pointed[F[_]] extends Wizard[ConstUnit, F] { // Applicative + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Pure[FF,BB] // BB + def pure[B](value: B): F[B] = doMagic(value)(()) +} + +trait CoPointed[F[_]] extends Wizard[ConstUnit, Id] { // CoApplicative + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Effect[FF,BB] + + def coPure[B](value: F[B]): Id[B] = doMagic(value)(42) +} + +// TODO you could expand on different shapes but they do not make sense ? + +// ConstUnit F B Pointed Applicative +// ConstUnit Id F[B] CoPointed CoApplicative + +// Id ConstUnit F[B] +// Id ConstUnit B +// ConstUnit Id B + +// F ConstUnit B +// F ConstUnit F[B] +// ConstUnit F F[B] + +// Id F F[B] +// Id F B +// F Id F[B] +// F Id B + + +// TODO +//trait Foldable[F[_]] { +// def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B +//} + +// TODO can we express duplicate and flatten somehow ? + +// TODO what would happen with when combining different types of F like Id ConstUnit with F[A] => F[B] +// a lot of different ways to specify identity (if FF == F and GG == G) + +// TODO Traversable, Distributive, MonadTrans do not fit this framework +// what it tells us about them ? + +// TODO what about Selective From 1771426db6bac45ebe862a0e11bc1808b7d32db6 Mon Sep 17 00:00:00 2001 From: lemastero Date: Sun, 18 Jul 2021 15:58:45 +0200 Subject: [PATCH 02/10] wild magic laws --- src/main/scala/ct_other_impls/WildMagic.scala | 188 ++++++++++++++++-- 1 file changed, 166 insertions(+), 22 deletions(-) diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala index 7084398b..336fa7cf 100644 --- a/src/main/scala/ct_other_impls/WildMagic.scala +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -27,26 +27,28 @@ object WizardData { case class ContraAp[+F[-_], +A, -B](run: F[A => B]) // Profunctor-ish? case class ApOp[+F[+_], +A, -B](run: F[B => A]) // Profunctor-ish? case class Kleisli[+F[+_], -A, +B](run: A => F[B]) // Profunctor-ish? - case class Kleisli2[+F[-_], -A, -B](run: A => F[B]) // Nifunctor-ish? case class ContraKleisli[-F[+_], -A, +B](run: F[A] => B) // Profunctor-ish? + + case class Kleisli2[+F[-_], -A, -B](run: A => F[B]) // Nifunctor-ish? + case class CoKleisli[-F[-_], +A, +B](run: F[A] => B) // Bifunctor-ish? case class Zip[+F[+_], +A, +B](run: F[(A, B)]) // Bifunctor-ish? case class Alt[+F[+_], +A, +B](run: F[Either[A, B]]) // Bifunctor-ish? } object WizardTypes { - type Arrow[A, B] = A => B // = Function1[A,B] - type Kleisli[F[_],A,B] = A => F[B] // = Function1[A,F[B]] - type CoKleisli[F[_],A,B] = F[A] => B // = Function1[F[A],B] - type Ap[F[_], A, B] = F[A => B] // = F[Function1[A,B]] + type Arrow[A, B] = A => B + type Kleisli[F[_],A,B] = A => F[B] + type CoKleisli[F[_],A,B] = F[A] => B + type Ap[F[_], A, B] = F[A => B] - type Op[A, B] = B => A // = Function1[B,A] -// type OpKleisli[F[_],A,B] = B => F[A] // = Function1[B,F[A]] -// type OpCoKleisli[F[_],A,B] = F[B] => A // = Function1[F[B],A] - type OpAp[F[_], A, B] = F[B => A] // = F[Function1[B,A]] + type Op[A, B] = B => A +// type OpKleisli[F[_],A,B] = B => F[A] there are no contravariant monads +// type OpCoKleisli[F[_],A,B] = F[B] => A there are no contravariant comonads + type OpAp[F[_], A, B] = F[B => A] - type Zip[F[_], A, B] = F[(A, B)] // = F[Tuple2[A,B]] - type Alt[F[_], A, B] = F[Either[A, B]] // = F[Either[A,B]] + type Zip[F[_], A, B] = F[(A, B)] + type Alt[F[_], A, B] = F[Either[A, B]] type Pure[F[_], B] = B type Effect[F[_],B] = F[B] @@ -68,43 +70,129 @@ object WizardTypes { // ConstUnit Id F[B] S[F[_], G[_], A, B] = F[B] CoPointed (CoApplicative) trait Wizard[F[_], G[_]] { - type Spell[_[_], _[_], _, _] // transform 2 type constructors and 2 regular types into sth - def doMagic[A, B](f: Spell[F, G, A, B]): F[A] => G[B] + type Spell[_[_], _[_], _, _] // type level function that transforms 2 type constructors and 2 regular types into type + def doMagic[A, B](f: Spell[F, G, A, B]): F[A] => G[B] // function that for every A and B } -trait Functor[F[_]] extends Wizard[F, F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Arrow[AA,BB] // AA => BB +trait Wizard2[F[_]] extends Wizard[F,F] { + type Cat[AA, BB] = Spell[F,F,AA,BB] // type level function that transforms type constructor and 2 regular types into type + override def doMagic[A, B](f: Cat[A,B]): F[A] => F[B] // function that for every A and B +} + +trait Category[Cat[_,_]] { + def id[A]: Cat[A, A] + def compose[A, B, C]: Cat[A, B] => Cat[B, C] => Cat[A, C] +} + +trait Wizard2Law[F[_]] extends Wizard2[F] { + val cat: Category[Cat] - // map(fa)(identity) == fa doMagic(identity)(fa) == fa - // map(map(fa)(f))(g) == map(fa)(f andThen g) doMagic(g)(doMagic(f)(fa)) == doMagic(f andThen g)(fa) + def lawId[A](fa: F[A]): Boolean = + doMagic[A,A](cat.id[A])(fa) == fa + + def lawComp[A,B,C](fa: F[A], f: Cat[A,B], g: Cat[B,C]): Boolean = + doMagic[B,C](g)( doMagic[A,B](f)(fa) ) == doMagic[A,C]( cat.compose(f)(g) )(fa) +} + +trait Functor[F[_]] extends Wizard2[F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Arrow[AA,BB] // AA => BB def map[A, B](fa: F[A])(f: A => B): F[B] = doMagic[A, B](f)(fa) } -trait FlatMap[F[_]] extends Wizard[F, F] { // Monad - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Kleisli[FF,AA,BB] // AA => FF[BB] +object FunctorCat extends Category[Function1] { + def id[A]: A => A = identity[A] + def compose[A, B, C]: (A => B) => (B => C) => (A => C) = + f => g => f andThen g +} + +trait FunctorLaws[F[_]] extends Wizard2Law[F] with Functor[F] { + override val cat: Category[Cat] = FunctorCat +} - // flatMap(flatMap(fa)(f))(g) == flatMap(fa)(a => flatMap(f(a))(g)) +trait FlatMap[F[_]] extends Wizard2[F] { + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Kleisli[FF,AA,BB] // AA => FF[BB] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = doMagic[A, B](f)(fa) } -trait Zip[F[_]] extends Wizard[F, F] { // Apply +trait FlatMapLaws[F[_]] extends Wizard2Law[F] with FlatMap[F] { + + // TODO this is pure, looks like specifying law for FlatMap requires to insist Pointed as subclass + def id[A]: A => F[A] = ??? + + def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = + f => g => a => doMagic[B,C](g)(f(a)) +} + +trait Zip[F[_]] extends Wizard2[F] { type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Zip[FF,AA,BB] // FF[(AA, BB)] def tuple2[A, B](fa: F[A])(fab: F[(A, B)]): F[B] = doMagic[A, B](fab)(fa) } +trait ZipLaws[F[_]] extends Wizard2[F] with Zip[F] { + + // TODO + def idSpell[A]: F[(A,A)] = ??? + + def composeSpell[A,B,C]: Spell[F,F,A,B] => Spell[F,F,B,C] => Spell[F,F,A,C] = + (f : F[(A,B)]) => + (g : F[(B,C)]) => ??? +} + trait Alt[F[_]] extends Wizard[F, F] { // Alt type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Alt[FF,AA,BB] // FF[Either[AA, BB]] def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = doMagic[A, B](fab)(fa) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = ??? // F[Either[A,A]] + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = ??? // F[Either[A,B]] F[Either[B,C]] => F[Either[A,C]] + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } trait Ap[F[_]] extends Wizard[F, F] { // Apply type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Ap[FF,AA,BB] // FF[AA => BB] def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = doMagic[A, B](f)(fa) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = ??? // F[A => A] + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = ??? // F[A => B] F[B => C] => F[A => C] + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } trait CoFlatMap[F[_]] extends Wizard[F, F] { type Spell[FF[_], GG[_], AA, BB] = WizardTypes.CoKleisli[FF,AA,BB] // FF[AA] => BB def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = doMagic[A, B](f)(fa) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = ??? // F[A] => A + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = ??? // F[A] => B F[B] => C F[A] => C + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } // TODO below 2 do not make any sense @@ -123,11 +211,39 @@ trait Contravariant[F[_]] extends Wizard[F, F] { // doMagic(identity)(fa) == fa // contramap( contrampa(fa)(f) )(g) == contramap(fa)(f andThen g) def contramap[A, B](fa: F[A])(f: B => A): F[B] = doMagic[A, B](f)(fa) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = identity[A] + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = f compose g + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } trait Divide[F[_]] extends Wizard[F, F] { type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpAp[FF,AA,BB] // FF[BB => AA] def contraAp[A, B](fa: F[A])(f: F[B => A]): F[B] = doMagic[A, B](f)(fa) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = ??? // F[A => A] + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = ??? // F[B => A] F[C => B] => F[C => A] + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } // TODO Pointed and CoPointed break symmetry @@ -135,12 +251,40 @@ trait Divide[F[_]] extends Wizard[F, F] { trait Pointed[F[_]] extends Wizard[ConstUnit, F] { // Applicative type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Pure[FF,BB] // BB def pure[B](value: B): F[B] = doMagic(value)(()) + + def lawId[A](fa: F[A]): Boolean = { + val lhs1: Spell[F,F,A,A] = ??? // A + val lhs2: F[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1: F[B] = doMagic[A,B](f)(fa) + val lhs2: F[C] = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = g // f: B g: C => C + val rhs: F[C] = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } trait CoPointed[F[_]] extends Wizard[ConstUnit, Id] { // CoApplicative - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Effect[FF,BB] + type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Effect[FF,BB] // FF[BB] def coPure[B](value: F[B]): Id[B] = doMagic(value)(42) + + def lawId[A](fa: Id[A]): Boolean = { + val lhs1: Id[A] = fa + val lhs2: Id[A] = doMagic[A,A](lhs1)(fa) + lhs2 == fa + } + + def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { + val lhs1 = doMagic[A,B](f)(fa) + val lhs2 = doMagic[B,C](g)(lhs1) + val fg: Spell[F,F,A,C] = g // f: F[B] g: F[C] => F[C] + val rhs = doMagic[A,C](fg)(fa) + lhs2 == rhs + } } // TODO you could expand on different shapes but they do not make sense ? From 022cda8d6b2674e54514c12111fbb61997eeef4a Mon Sep 17 00:00:00 2001 From: lemastero Date: Sun, 18 Jul 2021 23:36:25 +0200 Subject: [PATCH 03/10] strange results - contravariant monads and comonads ? yet cannot define Divisible, Alt, Zip --- .../DeclarativeVarianceFun.scala | 17 + src/main/scala/ct_other_impls/WildMagic.scala | 413 ++++++++---------- .../scala/ct_other_impls/WildMagic3.scala | 12 + 3 files changed, 223 insertions(+), 219 deletions(-) create mode 100644 src/main/scala/ct_other_impls/DeclarativeVarianceFun.scala create mode 100644 src/main/scala/ct_other_impls/WildMagic3.scala diff --git a/src/main/scala/ct_other_impls/DeclarativeVarianceFun.scala b/src/main/scala/ct_other_impls/DeclarativeVarianceFun.scala new file mode 100644 index 00000000..a9bafcd3 --- /dev/null +++ b/src/main/scala/ct_other_impls/DeclarativeVarianceFun.scala @@ -0,0 +1,17 @@ +package ct_other_impls + +object DeclarativeVarianceFun { + case class Reader[-A, +B](run: A => B) // Profunctor-ish? + case class Op[+A, -B](run: B => A) // Profunctor-ish? + case class Ap[+F[+_], -A, +B](run: F[A => B]) // Profunctor-ish? + case class ContraAp[+F[-_], +A, -B](run: F[A => B]) // Profunctor-ish? + case class ApOp[+F[+_], +A, -B](run: F[B => A]) // Profunctor-ish? + case class Kleisli[+F[+_], -A, +B](run: A => F[B]) // Profunctor-ish? + case class ContraKleisli[-F[+_], -A, +B](run: F[A] => B) // Profunctor-ish? + + case class Kleisli2[+F[-_], -A, -B](run: A => F[B]) // Nifunctor-ish? + + case class CoKleisli[-F[-_], +A, +B](run: F[A] => B) // Bifunctor-ish? + case class Zip[+F[+_], +A, +B](run: F[(A, B)]) // Bifunctor-ish? + case class Alt[+F[+_], +A, +B](run: F[Either[A, B]]) // Bifunctor-ish? +} diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala index 336fa7cf..a1f9bb3f 100644 --- a/src/main/scala/ct_other_impls/WildMagic.scala +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -1,59 +1,52 @@ package ct_other_impls -import ct_other_impls.Wizard.{ConstUnit, Id} +import ct_other_impls.FPAbs.{ConstUnit, Id} +import educational.abstract_algebra.Monoid /* Are there any constructions in Category Theory something like: -Given: -C category -F, G : functors in C -M functor category of category C +Given: category C, D, E and two mappings F : C -> D and G : C => E +we will note D as F[C] and E as G[C]. + + ------- CxC ---------- + | | | + | | Morphism | + | | | +F | \/ | G + | Morphism[C] | + | | | + | | run | + | | | + \/ \/ \/ + F[C] --------------> G[C] + + + G F + G[C] <=== C ===> F[C] + run: Morphism[A,B] => (F[A] => F[B]) + +|----------------|----------------|--------------|-------------------| +| | Morphism[C] | +|----------------|----------------|--------------|-------------------| +| abstraction | Morphism | identity | composition | +|----------------|----------------|--------------|-------------------| +| Functor | function | identity | andThen | +| FlatMap | Kleisli | pure | kleisli comp. | +| CoflatMap | CoKleisli | extract | cokleisli comp. | +| FlatMap op | Kleisli op | OK | OK | !!! contravariant Monad +| CpflatMap op | CoKleisli op | OK | OK | !!! contravariant Comonad +| Apply | F[A => B] | OK | OK | +| Contravariant | inv. function | identity | compose | +| Apply op | F[B => A] | ? | ? | impossible TODO redefine laws +| Zip | F[(A, B)] | ? | ? | impossible TODO redefine laws +| Alternative | F[A \/ B] | ? | ? | impossible TODO redefine laws +| Pure | B | OK | second | +| Extract | F[B] | OK | first | +|----------------|----------------|--------------|-------------------| -P morphism that map product of any two objects a and b from Category C into object a in M */ -object Wizard { - type Id[A] = A // identity functor - what is it in functor category - type ConstUnit[A] = Unit // terminal object in functor category - - type Void[A] = Nothing // initial object in functor category -} - -object WizardData { - case class Reader[-A, +B](run: A => B) // Profunctor-ish? - case class Op[+A, -B](run: B => A) // Profunctor-ish? - case class Ap[+F[+_], -A, +B](run: F[A => B]) // Profunctor-ish? - case class ContraAp[+F[-_], +A, -B](run: F[A => B]) // Profunctor-ish? - case class ApOp[+F[+_], +A, -B](run: F[B => A]) // Profunctor-ish? - case class Kleisli[+F[+_], -A, +B](run: A => F[B]) // Profunctor-ish? - case class ContraKleisli[-F[+_], -A, +B](run: F[A] => B) // Profunctor-ish? - - case class Kleisli2[+F[-_], -A, -B](run: A => F[B]) // Nifunctor-ish? - - case class CoKleisli[-F[-_], +A, +B](run: F[A] => B) // Bifunctor-ish? - case class Zip[+F[+_], +A, +B](run: F[(A, B)]) // Bifunctor-ish? - case class Alt[+F[+_], +A, +B](run: F[Either[A, B]]) // Bifunctor-ish? -} - -object WizardTypes { - type Arrow[A, B] = A => B - type Kleisli[F[_],A,B] = A => F[B] - type CoKleisli[F[_],A,B] = F[A] => B - type Ap[F[_], A, B] = F[A => B] - - type Op[A, B] = B => A -// type OpKleisli[F[_],A,B] = B => F[A] there are no contravariant monads -// type OpCoKleisli[F[_],A,B] = F[B] => A there are no contravariant comonads - type OpAp[F[_], A, B] = F[B => A] - - type Zip[F[_], A, B] = F[(A, B)] - type Alt[F[_], A, B] = F[Either[A, B]] - - type Pure[F[_], B] = B - type Effect[F[_],B] = F[B] -} - // F F A => B S[F[_], G[_], A, B] = A => B Functor // F F B => A S[F[_], G[_], A, B] = B => A Contravariant @@ -69,222 +62,220 @@ object WizardTypes { // ConstUnit F B S[F[_], G[_], A, B] = B Pointed (Pure, Applicative) // ConstUnit Id F[B] S[F[_], G[_], A, B] = F[B] CoPointed (CoApplicative) -trait Wizard[F[_], G[_]] { - type Spell[_[_], _[_], _, _] // type level function that transforms 2 type constructors and 2 regular types into type - def doMagic[A, B](f: Spell[F, G, A, B]): F[A] => G[B] // function that for every A and B +object FPAbs { + type Id[A] = A // identity functor - what is it in functor category + type ConstUnit[A] = Unit // terminal object in functor category + type Void[A] = Nothing // initial object in functor category } -trait Wizard2[F[_]] extends Wizard[F,F] { - type Cat[AA, BB] = Spell[F,F,AA,BB] // type level function that transforms type constructor and 2 regular types into type - override def doMagic[A, B](f: Cat[A,B]): F[A] => F[B] // function that for every A and B +object FPMorphism { + type Function[A, B] = A => B + type Kleisli[F[_],A,B] = A => F[B] + type CoKleisli[F[_],A,B] = F[A] => B + type Ap[F[_], A, B] = F[A => B] + + type Op[A, B] = B => A + type OpAp[F[_], A, B] = F[B => A] + + type Zip[F[_], A, B] = F[(A, B)] + type Alt[F[_], A, B] = F[Either[A, B]] + + type Value[B] = B + type Effect[F[_],B] = F[B] + + type OpKleisli[F[_],A,B] = B => F[A] + type OpCoKleisli[F[_],A,B] = F[B] => A } -trait Category[Cat[_,_]] { - def id[A]: Cat[A, A] - def compose[A, B, C]: Cat[A, B] => Cat[B, C] => Cat[A, C] +// TODO does FlatMap and Apply recovers Monad laws? +// TODO does CoFlatMap and CoApply recovers Comonad laws? +// TODO how to recover Applicative laws from Apply and Zip? +// TODO how to recover Alternative laws from Apply and Alt? +// TODO does Contravariant and Divide recovers Divide (ContravariantSemigroupal) laws? + +trait FPAbs[F[_], G[_]] { + type Morphism[_, _] // specify some transformation on types A, B possibly using F and G e.g. A => F[B] + + type BiKleisli[A,B] = F[A] => G[B] + def run[A, B](f: Morphism[A, B]): BiKleisli[A,B] } -trait Wizard2Law[F[_]] extends Wizard2[F] { - val cat: Category[Cat] +trait EndoFPAbs[F[_]] extends FPAbs[F,F] - def lawId[A](fa: F[A]): Boolean = - doMagic[A,A](cat.id[A])(fa) == fa +trait EndoFPAbsLaw[F[_]] extends FPAbs[F,F] { + // define category structure on a Morphism + def id[A]: Morphism[A, A] + def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] - def lawComp[A,B,C](fa: F[A], f: Cat[A,B], g: Cat[B,C]): Boolean = - doMagic[B,C](g)( doMagic[A,B](f)(fa) ) == doMagic[A,C]( cat.compose(f)(g) )(fa) + // laws for different FP abstractions (Functor, Monad, Comonad, Applicative, Divide) differs + // only in the category definition + + def abstractionIdentityLaw[A](fa: F[A]): Boolean = + run[A,A](id[A])(fa) == fa + + def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = + run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) } -trait Functor[F[_]] extends Wizard2[F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Arrow[AA,BB] // AA => BB - def map[A, B](fa: F[A])(f: A => B): F[B] = doMagic[A, B](f)(fa) +trait Functor[F[_]] extends EndoFPAbs[F] { + type Morphism[AA, BB] = FPMorphism.Function[AA,BB] + def map[A, B](fa: F[A])(f: A => B): F[B] = run[A, B](f)(fa) } -object FunctorCat extends Category[Function1] { +trait FunctorLaws[F[_]] extends EndoFPAbsLaw[F] with Functor[F] { def id[A]: A => A = identity[A] def compose[A, B, C]: (A => B) => (B => C) => (A => C) = f => g => f andThen g } -trait FunctorLaws[F[_]] extends Wizard2Law[F] with Functor[F] { - override val cat: Category[Cat] = FunctorCat -} - -trait FlatMap[F[_]] extends Wizard2[F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Kleisli[FF,AA,BB] // AA => FF[BB] - def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = doMagic[A, B](f)(fa) +trait FlatMap[F[_]] extends EndoFPAbs[F] { + type Morphism[AA, BB] = FPMorphism.Kleisli[F,AA,BB] + def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = run[A, B](f)(fa) } -trait FlatMapLaws[F[_]] extends Wizard2Law[F] with FlatMap[F] { +abstract class FlatMapLaws[F[_]: Pure] extends EndoFPAbsLaw[F] with FlatMap[F] { - // TODO this is pure, looks like specifying law for FlatMap requires to insist Pointed as subclass - def id[A]: A => F[A] = ??? + // FlatMap and Pointed are connected, give raise to Monad + def id[A]: A => F[A] = implicitly[Pure[F]].pure[A] def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = - f => g => a => doMagic[B,C](g)(f(a)) + f => g => a => run[B,C](g)(f(a)) } -trait Zip[F[_]] extends Wizard2[F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Zip[FF,AA,BB] // FF[(AA, BB)] - def tuple2[A, B](fa: F[A])(fab: F[(A, B)]): F[B] = doMagic[A, B](fab)(fa) +trait Zip[F[_]] extends EndoFPAbs[F] { + type Morphism[AA, BB] = FPMorphism.Zip[F,AA,BB] + def tuple2[A, B](fa: F[A])(fab: F[(A, B)]): F[B] = run[A, B](fab)(fa) } -trait ZipLaws[F[_]] extends Wizard2[F] with Zip[F] { - - // TODO - def idSpell[A]: F[(A,A)] = ??? +//abstract class ZipLaws[F[_] : Pure, A : Monoid] extends EndoFPAbsLaw[F] with Zip[F] { +// def pure[X]: X => F[X] = implicitly[Pure[F]].pure +// val empty: A = implicitly[Monoid[A]].empty +// +// def id: F[(A,A)] = pure((empty,empty)) +// def compose[B,C]: F[(A,B)] => F[(B,C)] => F[(A,C)] = fab => fbc => { +// val foo = run(fab) +// ??? +// } +//} - def composeSpell[A,B,C]: Spell[F,F,A,B] => Spell[F,F,B,C] => Spell[F,F,A,C] = - (f : F[(A,B)]) => - (g : F[(B,C)]) => ??? +trait Alt[F[_]] extends FPAbs[F, F] { + type Morphism[AA, BB] = FPMorphism.Alt[F,AA,BB] + def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = run[A, B](fab)(fa) } -trait Alt[F[_]] extends Wizard[F, F] { // Alt - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Alt[FF,AA,BB] // FF[Either[AA, BB]] - def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = doMagic[A, B](fab)(fa) +//abstract class AltLaws[F[_]] extends EndoFPAbsLaw[F] with Alt[F] { +// def id[A]: F[Either[A,A]] = ??? +// def compose[A, B, C]: F[Either[A,B]] => F[Either[B,C]] => F[Either[A,C]] = ??? +//} - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = ??? // F[Either[A,A]] - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } +trait Apply[F[_]] extends FPAbs[F, F] { + type Morphism[AA, BB] = FPMorphism.Ap[F,AA,BB] + def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = run[A, B](f)(fa) +} - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = ??? // F[Either[A,B]] F[Either[B,C]] => F[Either[A,C]] - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs +abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { + def pure[A]: A => F[A] = implicitly[Pure[F]].pure + def id[A]: F[A => A] = pure(identity[A]) + def compose[A, B, C]: F[A => B] => F[B => C] => F[A => C] = fab => { + val fabbcac: F[(A => B) => ((B => C) => (A => C))] = pure(ab => bc => ab andThen bc) + val fbcac: F[(B => C) => (A => C)] = run[A => B, (B => C) => (A => C)](fabbcac)(fab) + run[B => C,A => C](fbcac) } } -trait Ap[F[_]] extends Wizard[F, F] { // Apply - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Ap[FF,AA,BB] // FF[AA => BB] - def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = doMagic[A, B](f)(fa) - - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = ??? // F[A => A] - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } +trait CoFlatMap[F[_]] extends FPAbs[F, F] { + type Morphism[AA, BB] = FPMorphism.CoKleisli[F,AA,BB] + def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = run[A, B](f)(fa) +} - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = ??? // F[A => B] F[B => C] => F[A => C] - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs - } +abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMap[F] { + def id[A]: F[A] => A = implicitly[Extract[F]].extract + def compose[A, B, C]: (F[A] => B) => (F[B] => C) => F[A] => C = fab => fbc => fa => + implicitly[Extract[F]].extract(run(fbc)(run(fab)(fa))) } -trait CoFlatMap[F[_]] extends Wizard[F, F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.CoKleisli[FF,AA,BB] // FF[AA] => BB - def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = doMagic[A, B](f)(fa) +trait Contravariant[F[_]] extends EndoFPAbs[F] { + type Morphism[AA, BB] = FPMorphism.Op[AA,BB] - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = ??? // F[A] => A - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } + def contramap[A, B](fa: F[A])(f: B => A): F[B] = run[A, B](f)(fa) +} - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = ??? // F[A] => B F[B] => C F[A] => C - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs - } +trait ContravariantLaws[F[_]] extends EndoFPAbsLaw[F] with Contravariant[F] { + def id[A]: A => A = identity[A] + def compose[A, B, C]: (B => A) => (C => B) => (C => A) = f => g => f compose g } -// TODO below 2 do not make any sense -//trait ContraCoflatMap[F[_]] extends Wizard[F, F] { -// type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpKleisli[FF,AA,BB] // BB => F[AA] -// def contraExtend[A, B](fa: F[A])(f: B => F[A]): F[B] = doMagic[A, B](f)(fa) +//trait Divide[F[_]] extends EndoFPAbs[F] { +// type Morphism[AA, BB] = FPMorphism.OpAp[F,AA,BB] +// def contraAp[A, B](fa: F[A])(fba: F[B => A]): F[B] = run[A, B](fba)(fa) //} -//trait OpCoKleisliWizard[F[_]] extends Wizard[F, F] { -// type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpCoKleisli[FF,AA,BB] // F[BB] => AA -// def contraExtend[A, B](fa: F[A])(f: F[B] => A): F[B] = doMagic[A, B](f)(fa) +// +//abstract class DivideLaws[F[_] : Pure : Contravariant] extends EndoFPAbsLaw[F] with Divide[F] { +// def pure[A]: A => F[A] = implicitly[Pure[F]].pure +// def contramap[A,B](fa: F[A], ba: B => A): F[B] = implicitly[Contravariant[F]].contramap(fa)(ba) +// def id[A]: F[A => A] = pure[A => A](identity[A]) +// override def compose[A, B, C]: F[B => A] => F[C => B] => F[C => A] = fba => fcb => { +// val fcabacb: ((C => A) => (B => A)) => (C => B) = ??? +// val fcaba: F[(C => A) => (B => A)] = contramap[C => B, (C => A) => (B => A)](fcb, fcabacb) +// run[B => A,C => A](fcaba)(fba) +// } //} -trait Contravariant[F[_]] extends Wizard[F, F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Op[AA,BB] // BB => AA +// Pointed and CoPointed break symmetry - // doMagic(identity)(fa) == fa - // contramap( contrampa(fa)(f) )(g) == contramap(fa)(f andThen g) - def contramap[A, B](fa: F[A])(f: B => A): F[B] = doMagic[A, B](f)(fa) +trait Pure[F[_]] extends FPAbs[ConstUnit, F] { + type Morphism[AA, BB] = FPMorphism.Value[BB] + def pure[B](b: B): F[B] = run(b)(()) - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = identity[A] - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } + def id[A:Monoid]: Morphism[A, A] = implicitly[Monoid[A]].empty + def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] = _ => identity[C] - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = f compose g - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs - } + def abstractionIdentityLaw[A:Monoid](fa: F[A]): Boolean = + run[A,A](id[A])(fa) == fa + + def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = + run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) } -trait Divide[F[_]] extends Wizard[F, F] { - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.OpAp[FF,AA,BB] // FF[BB => AA] - def contraAp[A, B](fa: F[A])(f: F[B => A]): F[B] = doMagic[A, B](f)(fa) +trait Extract[F[_]] extends FPAbs[ConstUnit, Id] { + type Morphism[AA, BB] = FPMorphism.Effect[F,AA] - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = ??? // F[A => A] - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } + def extract[B](value: F[B]): B = run(value)(()) - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = ??? // F[B => A] F[C => B] => F[C => A] - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs - } -} + def id[A]: Morphism[A, A] = ??? + def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] = fab => _ => fab -// TODO Pointed and CoPointed break symmetry + def abstractionIdentityLaw[A](fa: F[A]): Boolean = + run[A,A](id[A])(fa) == fa // TODO -trait Pointed[F[_]] extends Wizard[ConstUnit, F] { // Applicative - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Pure[FF,BB] // BB - def pure[B](value: B): F[B] = doMagic(value)(()) + def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = + run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) +} - def lawId[A](fa: F[A]): Boolean = { - val lhs1: Spell[F,F,A,A] = ??? // A - val lhs2: F[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } +// There are not contravariant monads ? - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1: F[B] = doMagic[A,B](f)(fa) - val lhs2: F[C] = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = g // f: B g: C => C - val rhs: F[C] = doMagic[A,C](fg)(fa) - lhs2 == rhs - } +trait ContraCoflatMap[F[_]] extends FPAbs[F, F] { + type Morphism[AA, BB] = FPMorphism.OpKleisli[F,AA,BB] + def contraExtend[A, B](fa: F[A])(f: B => F[A]): F[B] = run[A, B](f)(fa) } -trait CoPointed[F[_]] extends Wizard[ConstUnit, Id] { // CoApplicative - type Spell[FF[_], GG[_], AA, BB] = WizardTypes.Effect[FF,BB] // FF[BB] +abstract class ContraCoflatMapLaws[F[_] : Pure : Extract] extends EndoFPAbsLaw[F] with ContraCoflatMap[F] { + def id[A]: A => F[A] = a => implicitly[Pure[F]].pure(a) + def compose[A, B, C]: (B => F[A]) => (C => F[B]) => C => F[A] = bfa => cfb => c => + bfa(implicitly[Extract[F]].extract(cfb(c))) +} - def coPure[B](value: F[B]): Id[B] = doMagic(value)(42) +// There are not contravariant comonads ? - def lawId[A](fa: Id[A]): Boolean = { - val lhs1: Id[A] = fa - val lhs2: Id[A] = doMagic[A,A](lhs1)(fa) - lhs2 == fa - } +trait ContraFlatMap[F[_]] extends FPAbs[F, F] { + type Morphism[AA, BB] = FPMorphism.OpCoKleisli[F,AA,BB] + def contraExtend[A, B](fa: F[A])(f: F[B] => A): F[B] = run[A, B](f)(fa) +} - def lawComp[A,B,C](fa: F[A], f: Spell[F,F,A,B], g: Spell[F,F,B,C]): Boolean = { - val lhs1 = doMagic[A,B](f)(fa) - val lhs2 = doMagic[B,C](g)(lhs1) - val fg: Spell[F,F,A,C] = g // f: F[B] g: F[C] => F[C] - val rhs = doMagic[A,C](fg)(fa) - lhs2 == rhs - } +abstract class ContraFlatMapLaws[F[_] : Pure : Extract] extends EndoFPAbsLaw[F] with ContraFlatMap[F] { + def id[A]: F[A] => A = implicitly[Extract[F]].extract + def compose[A, B, C]: (F[B] => A) => (F[C] => B) => F[C] => A = fba => fcb => fc => + fba(implicitly[Pure[F]].pure(fcb(fc))) } // TODO you could expand on different shapes but they do not make sense ? @@ -304,19 +295,3 @@ trait CoPointed[F[_]] extends Wizard[ConstUnit, Id] { // CoApplicative // Id F B // F Id F[B] // F Id B - - -// TODO -//trait Foldable[F[_]] { -// def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B -//} - -// TODO can we express duplicate and flatten somehow ? - -// TODO what would happen with when combining different types of F like Id ConstUnit with F[A] => F[B] -// a lot of different ways to specify identity (if FF == F and GG == G) - -// TODO Traversable, Distributive, MonadTrans do not fit this framework -// what it tells us about them ? - -// TODO what about Selective diff --git a/src/main/scala/ct_other_impls/WildMagic3.scala b/src/main/scala/ct_other_impls/WildMagic3.scala new file mode 100644 index 00000000..6dc2547b --- /dev/null +++ b/src/main/scala/ct_other_impls/WildMagic3.scala @@ -0,0 +1,12 @@ +package ct_other_impls + +// TODO fit into this pattern situations when there are 3 different effects under F: Selective, etc +trait FPAbs3[F[_]] { + type Morphism1[_, _, _] + type Morphism2[_, _, _] + type Morphism3[_, _, _] + + def run[A, B, C](f: Morphism1[A, B, C]): Morphism2[A, B, C] => Morphism3[A, B, C] +} + +// TODO what about Selective From 10177c91702808051254271dd49a76b84c66df52 Mon Sep 17 00:00:00 2001 From: lemastero Date: Wed, 4 Aug 2021 23:34:40 +0200 Subject: [PATCH 04/10] expand all laws, get rid of stuff that do not works --- project/plugins.sbt | 2 +- src/main/scala/ct_other_impls/WildMagic.scala | 552 +++++++++++------- .../educational/category_theory/Comonad.scala | 2 +- .../educational/category_theory/Monad.scala | 3 +- 4 files changed, 354 insertions(+), 205 deletions(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index c6b17c17..c42c9b3b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ // sbt dependencyUpdates addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.3") -//addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.8") +//addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.20") // sbt scalafmtAll addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") // sbt clean coverage test coverageReport diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala index a1f9bb3f..1164b9cf 100644 --- a/src/main/scala/ct_other_impls/WildMagic.scala +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -1,297 +1,447 @@ package ct_other_impls -import ct_other_impls.FPAbs.{ConstUnit, Id} -import educational.abstract_algebra.Monoid - /* -Are there any constructions in Category Theory something like: - -Given: category C, D, E and two mappings F : C -> D and G : C => E -we will note D as F[C] and E as G[C]. - - ------- CxC ---------- - | | | - | | Morphism | - | | | -F | \/ | G - | Morphism[C] | - | | | - | | run | - | | | - \/ \/ \/ - F[C] --------------> G[C] - - - G F - G[C] <=== C ===> F[C] - run: Morphism[A,B] => (F[A] => F[B]) - -|----------------|----------------|--------------|-------------------| -| | Morphism[C] | -|----------------|----------------|--------------|-------------------| -| abstraction | Morphism | identity | composition | -|----------------|----------------|--------------|-------------------| -| Functor | function | identity | andThen | -| FlatMap | Kleisli | pure | kleisli comp. | -| CoflatMap | CoKleisli | extract | cokleisli comp. | -| FlatMap op | Kleisli op | OK | OK | !!! contravariant Monad -| CpflatMap op | CoKleisli op | OK | OK | !!! contravariant Comonad -| Apply | F[A => B] | OK | OK | -| Contravariant | inv. function | identity | compose | -| Apply op | F[B => A] | ? | ? | impossible TODO redefine laws -| Zip | F[(A, B)] | ? | ? | impossible TODO redefine laws -| Alternative | F[A \/ B] | ? | ? | impossible TODO redefine laws -| Pure | B | OK | second | -| Extract | F[B] | OK | first | -|----------------|----------------|--------------|-------------------| - - */ - -// F F A => B S[F[_], G[_], A, B] = A => B Functor -// F F B => A S[F[_], G[_], A, B] = B => A Contravariant -// F F A => F[B] S[F[_], G[_], A, B] = A => F[B] FlatMap -// F F F[A] => B S[F[_], G[_], A, B] = F[A] => B CoflatMap +Given a category C (of types and pure curried functions) and mapping between this category and itself +F: C -> C + +Let Morph be a Functor (depending on F): +Morph: C -> Morph[C] + +that maps object of C into C and morphisms are defined by: + +def id[A]: Morph[A, A] +def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] + +then following diagram commute: + + Morph[F,A,B] + A ----------------> B + | | +F | | F + | | + \/ \/ + F[A] -------------> F[B] + run(Morph[F,A,B]) + +Examples: + +------------------|------------------------------|--------------- +Morphism | id | abstraction +------------------|------------------------------|---------------- +A => B | Morph[F[_], A, B] = A => B | Functor +B => A | Morph[F[_], A, B] = B => A | Contravariant +A => F[B] | Morph[F[_], A, B] = A => F[B] | FlatMap +F[A] => B | Morph[F[_], A, B] = F[A] => B | CoflatMap +F[A => B] | Morph[F[_], A, B] = F[A => B] | Ap (Apply) +A => Option[B] | Morph[F[_], A, B] = F[A => B] + +|-----------------|----------------|--------------|-------------------| +| abstraction | Morph | identity | composition | +|-----------------|----------------|--------------|-------------------| +| Functor | A => B | identity | andThen | +| FlatMap | A => F[B] | pure | kleisli comp. | +| CoflatMap | F[A] => B | extract | cokleisli comp. | +| Apply | F[A => B] | OK | OK | +| Contravariant | B => A | identity | compose | +| MapFilter | A => Option[B] | OK | OK | +| ContraMapFilter | F[B] | OK | OK | +|-----------------|----------------|--------------|-------------------| -// F F F[(A, B)] S[F[_], G[_], A, B] = F[(A, B)] Zip -// F F F[A \/ B] S[F[_], G[_], A, B] = F[A \/ B]] Alt - -// F F F[A => B] S[F[_], G[_], A, B] = F[A => B] Ap (Apply) -// F F F[B => A] S[F[_], G[_], A, B] = F[B => A] Divide - -// ConstUnit F B S[F[_], G[_], A, B] = B Pointed (Pure, Applicative) -// ConstUnit Id F[B] S[F[_], G[_], A, B] = F[B] CoPointed (CoApplicative) + */ -object FPAbs { - type Id[A] = A // identity functor - what is it in functor category - type ConstUnit[A] = Unit // terminal object in functor category - type Void[A] = Nothing // initial object in functor category -} +// In above definition if we replace morphism with twisted morphism we get: +// F[(A, B)] Morph[F[_], A, B] = F[(A, B)] Zip +// F[A \/ B] Morph[F[_], A, B] = F[A \/ B]] Alt +// F[B => A] Morph[F[_], A, B] = F[B => A] Divide object FPMorphism { - type Function[A, B] = A => B - type Kleisli[F[_],A,B] = A => F[B] - type CoKleisli[F[_],A,B] = F[A] => B - type Ap[F[_], A, B] = F[A => B] + type Function[A, B] = A => B + type Kleisli[F[_],A,B] = A => F[B] + type Ap[F[_], A, B] = F[A => B] + type CoKleisli[F[_],A,B] = F[A] => B + type MapFilter[F[_],A,B] = A => Option[B] - type Op[A, B] = B => A - type OpAp[F[_], A, B] = F[B => A] + type Op[A, B] = B => A + type ContraMapFilter[F[_],A,B] = B => Option[A] + type OpAp[F[_], A, B] = F[B => A] type Zip[F[_], A, B] = F[(A, B)] type Alt[F[_], A, B] = F[Either[A, B]] - - type Value[B] = B - type Effect[F[_],B] = F[B] - - type OpKleisli[F[_],A,B] = B => F[A] - type OpCoKleisli[F[_],A,B] = F[B] => A } -// TODO does FlatMap and Apply recovers Monad laws? -// TODO does CoFlatMap and CoApply recovers Comonad laws? -// TODO how to recover Applicative laws from Apply and Zip? -// TODO how to recover Alternative laws from Apply and Alt? -// TODO does Contravariant and Divide recovers Divide (ContravariantSemigroupal) laws? - -trait FPAbs[F[_], G[_]] { - type Morphism[_, _] // specify some transformation on types A, B possibly using F and G e.g. A => F[B] +trait EndoFPAbs[F[_]] { + type Morph[_, _] // specify some transformation on types A, B possibly using F and G e.g. A => F[B] - type BiKleisli[A,B] = F[A] => G[B] - def run[A, B](f: Morphism[A, B]): BiKleisli[A,B] + type BiKleisli[A,B] = F[A] => F[B] + def run[A, B](f: Morph[A, B]): BiKleisli[A,B] } -trait EndoFPAbs[F[_]] extends FPAbs[F,F] - -trait EndoFPAbsLaw[F[_]] extends FPAbs[F,F] { +trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { // define category structure on a Morphism - def id[A]: Morphism[A, A] - def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] - - // laws for different FP abstractions (Functor, Monad, Comonad, Applicative, Divide) differs - // only in the category definition - - def abstractionIdentityLaw[A](fa: F[A]): Boolean = - run[A,A](id[A])(fa) == fa + def id[A]: Morph[A, A] + def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] + + // id[A] + // F[A] ========> F[A] == F[A] + def abstractionIdentityLaw[A](fa: F[A]): Boolean = { + val lhs: F[A] = run[A, A](id[A])(fa) + lhs == fa + } - def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = - run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) + // Mor[A,B] Mor[B,C] + // F[A] ============> F[B] ===========> F[C] + // + // Mor[A,B] * Mor[C,C] + // F[A] =======================> F[C] + def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morph[A,B], g: Morph[B,C]): Boolean = { + val fb: F[B] = run[A, B](f)(fa) + val lhs: F[C] = run[B, C](g)(fb) + val rhs: F[C] = run[A, C](compose(f)(g))(fa) + lhs == rhs + } } trait Functor[F[_]] extends EndoFPAbs[F] { - type Morphism[AA, BB] = FPMorphism.Function[AA,BB] + override def run[A, B](f: A => B): F[A] => F[B] + type Morph[AA, BB] = FPMorphism.Function[AA,BB] def map[A, B](fa: F[A])(f: A => B): F[B] = run[A, B](f)(fa) } +// fa.map(id) == fa +// fa.map(f).map(g) == fa.map(f andThen g) trait FunctorLaws[F[_]] extends EndoFPAbsLaw[F] with Functor[F] { + + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { + val lhs: F[A] = run[A,A](id[A])(fa) + val rhs: F[A] = fa + lhs == rhs + } + + // id + // F[A] =======> F[A] + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val lhs: F[A] = map[A,A](fa)(identity[A]) + val rhs: F[A] = fa + lhs == rhs + } + + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: A => B, g: B => C): Boolean = { + val lhs1: F[C] = run[B,C](g)( run[A,B](f)(fa) ) + val rhs1: F[C] = run[A,C]( compose(f)(g) )(fa) + lhs1 == rhs1 + } + + // f g f andThen g + // F[A] ====> F[B] =====> F[C] == F[A] ==============> F[B] + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: A => B, g: B => C): Boolean = { + val fb: F[B] = map[A, B](fa)(f) + val rhs1: F[C] = map[B,C](fb)(g) + val lhs1: F[C] = map[A,C](fa)(f andThen g) + lhs1 == rhs1 + } + def id[A]: A => A = identity[A] - def compose[A, B, C]: (A => B) => (B => C) => (A => C) = - f => g => f andThen g + def compose[A, B, C]: (A => B) => (B => C) => (A => C) = f => f andThen _ } trait FlatMap[F[_]] extends EndoFPAbs[F] { - type Morphism[AA, BB] = FPMorphism.Kleisli[F,AA,BB] + type Morph[AA, BB] = FPMorphism.Kleisli[F,AA,BB] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = run[A, B](f)(fa) } +// flatMap(pure) == id +// fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g) ) abstract class FlatMapLaws[F[_]: Pure] extends EndoFPAbsLaw[F] with FlatMap[F] { + def pure[A](a:A): F[A] = implicitly[Pure[F]].pure(a) // FlatMap and Pointed are connected, give raise to Monad - def id[A]: A => F[A] = implicitly[Pure[F]].pure[A] + def id[A]: A => F[A] = pure[A] def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = f => g => a => run[B,C](g)(f(a)) -} -trait Zip[F[_]] extends EndoFPAbs[F] { - type Morphism[AA, BB] = FPMorphism.Zip[F,AA,BB] - def tuple2[A, B](fa: F[A])(fab: F[(A, B)]): F[B] = run[A, B](fab)(fa) -} + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { + val lhs: F[A] = run[A, A](id[A])(fa) + lhs == fa + } -//abstract class ZipLaws[F[_] : Pure, A : Monoid] extends EndoFPAbsLaw[F] with Zip[F] { -// def pure[X]: X => F[X] = implicitly[Pure[F]].pure -// val empty: A = implicitly[Monoid[A]].empty -// -// def id: F[(A,A)] = pure((empty,empty)) -// def compose[B,C]: F[(A,B)] => F[(B,C)] => F[(A,C)] = fab => fbc => { -// val foo = run(fab) -// ??? -// } -//} + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val pa: A => F[A] = pure[A] + val lhs: F[A] = flatMap[A, A](fa)(pa) + lhs == fa + } -trait Alt[F[_]] extends FPAbs[F, F] { - type Morphism[AA, BB] = FPMorphism.Alt[F,AA,BB] - def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = run[A, B](fab)(fa) -} + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: A => F[B], g: B => F[C]): Boolean = { + val fb: F[B] = run[A, B](f)(fa) + val lhs: F[C] = run[B, C](g)(fb) -//abstract class AltLaws[F[_]] extends EndoFPAbsLaw[F] with Alt[F] { -// def id[A]: F[Either[A,A]] = ??? -// def compose[A, B, C]: F[Either[A,B]] => F[Either[B,C]] => F[Either[A,C]] = ??? -//} + val fg: A => F[C] = compose(f)(g) + val rhs: F[C] = run[A, C](fg)(fa) + lhs == rhs + } + + // flatMap(f) flatMap(g) + // F[A] -------------> F[B] -------------> F[C] + + // flatMap(a => flatMap(f(a))(g)) + // F[A] ----------------------------------> F[C] + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: A => F[B], g: B => F[C]): Boolean = { + val fb: F[B] = flatMap[A, B](fa)(f) + val lhs: F[C] = flatMap[B, C](fb)(g) + + def fg: A => F[C] = a => flatMap[B,C](f(a))(g) + val rhs: F[C] = flatMap[A, C](fa)(fg) + lhs == rhs + } +} -trait Apply[F[_]] extends FPAbs[F, F] { - type Morphism[AA, BB] = FPMorphism.Ap[F,AA,BB] +trait Apply[F[_]] extends EndoFPAbs[F] { + type Morph[AA, BB] = FPMorphism.Ap[F,AA,BB] def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = run[A, B](f)(fa) } +// fa.pure(identity) == fa +// fa.ap(f).ap(g) == fa.ap( compose(f,g) ) // compose uses pure abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { def pure[A]: A => F[A] = implicitly[Pure[F]].pure + def id[A]: F[A => A] = pure(identity[A]) def compose[A, B, C]: F[A => B] => F[B => C] => F[A => C] = fab => { val fabbcac: F[(A => B) => ((B => C) => (A => C))] = pure(ab => bc => ab andThen bc) val fbcac: F[(B => C) => (A => C)] = run[A => B, (B => C) => (A => C)](fabbcac)(fab) run[B => C,A => C](fbcac) } + + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { + val lhs: F[A] = run[A, A](id[A])(fa) + lhs == fa + } + + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val lhs: F[A] = ap[A, A](fa)(pure(identity[A])) + lhs == fa + } + + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: Morph[A,B], g: Morph[B,C]): Boolean = { + val lhs: F[C] = run[B, C](g)(run[A, B](f)(fa)) + val rhs: F[C] = run[A, C](compose(f)(g))(fa) + lhs == rhs + } + + def abstractionCompositionLaw2[A,B,C](fa: F[A], fab: F[A => B], fbc: F[B => C]): Boolean = { + val fb: F[B] = ap[A, B](fa)(fab) + val lhs: F[C] = ap[B, C](fb)(fbc) + val fac: F[A => C] = { + val ff1: F[(A => B) => ((B => C) => (A => C))] = pure(ab => bc => ab andThen bc) + val ff2: F[(B => C) => (A => C)] = run[A => B, (B => C) => (A => C)](ff1)(fab) + ap[B => C,A => C](fbc)(ff2) + } + val rhs: F[C] = ap[A, C](fa)(fac) + lhs == rhs + } } -trait CoFlatMap[F[_]] extends FPAbs[F, F] { - type Morphism[AA, BB] = FPMorphism.CoKleisli[F,AA,BB] +trait CoFlatMap[F[_]] extends EndoFPAbs[F] { + type Morph[AA, BB] = FPMorphism.CoKleisli[F,AA,BB] def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = run[A, B](f)(fa) + def duplicate[A](fa: F[A]): F[F[A]] = extend(fa)(identity) } +// fa.extend(extract) == fa +// fa.extend(f).extend(g) == ffa.extend( compose(f,g) ) abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMap[F] { - def id[A]: F[A] => A = implicitly[Extract[F]].extract + def extract[A]: F[A] => A = implicitly[Extract[F]].extract _ + + def id[A]: F[A] => A = extract def compose[A, B, C]: (F[A] => B) => (F[B] => C) => F[A] => C = fab => fbc => fa => - implicitly[Extract[F]].extract(run(fbc)(run(fab)(fa))) + extract(run(fbc)(run(fab)(fa))) + + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = + run[A,A](id[A])(fa) == fa + + // fa.extend(extract) == fa + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val lhs: F[A] = extend[A, A](fa)(extract[A]) + lhs == fa + } + + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: F[A] => B, g: F[B] => C): Boolean = + run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) + + // fa.extend(f).extend(g) == ffa.extend( compose(f,g) ) + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: F[A] => B, g: F[B] => C): Boolean = { + val fb: F[B] = extend[A, B](fa)(f) + val lhs: F[C] = extend[B, C](fb)(g) + val fg: F[A] => C = fa => { + val ff1: F[B] = extend(fa)(f) + val ff2: F[C] = extend(ff1)(g) + extract(ff2) + } + val rhs: F[C] = extend[A, C](fa)(fg) + lhs == rhs + } } trait Contravariant[F[_]] extends EndoFPAbs[F] { - type Morphism[AA, BB] = FPMorphism.Op[AA,BB] + type Morph[AA, BB] = FPMorphism.Op[AA,BB] def contramap[A, B](fa: F[A])(f: B => A): F[B] = run[A, B](f)(fa) } +// fa.contramap(identity) == fa +// fa.contramap(f).contramap(g) == fa.contramap(f compose g) trait ContravariantLaws[F[_]] extends EndoFPAbsLaw[F] with Contravariant[F] { def id[A]: A => A = identity[A] def compose[A, B, C]: (B => A) => (C => B) => (C => A) = f => g => f compose g -} - -//trait Divide[F[_]] extends EndoFPAbs[F] { -// type Morphism[AA, BB] = FPMorphism.OpAp[F,AA,BB] -// def contraAp[A, B](fa: F[A])(fba: F[B => A]): F[B] = run[A, B](fba)(fa) -//} -// -//abstract class DivideLaws[F[_] : Pure : Contravariant] extends EndoFPAbsLaw[F] with Divide[F] { -// def pure[A]: A => F[A] = implicitly[Pure[F]].pure -// def contramap[A,B](fa: F[A], ba: B => A): F[B] = implicitly[Contravariant[F]].contramap(fa)(ba) -// def id[A]: F[A => A] = pure[A => A](identity[A]) -// override def compose[A, B, C]: F[B => A] => F[C => B] => F[C => A] = fba => fcb => { -// val fcabacb: ((C => A) => (B => A)) => (C => B) = ??? -// val fcaba: F[(C => A) => (B => A)] = contramap[C => B, (C => A) => (B => A)](fcb, fcabacb) -// run[B => A,C => A](fcaba)(fba) -// } -//} - -// Pointed and CoPointed break symmetry - -trait Pure[F[_]] extends FPAbs[ConstUnit, F] { - type Morphism[AA, BB] = FPMorphism.Value[BB] - def pure[B](b: B): F[B] = run(b)(()) - def id[A:Monoid]: Morphism[A, A] = implicitly[Monoid[A]].empty - def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] = _ => identity[C] - - def abstractionIdentityLaw[A:Monoid](fa: F[A]): Boolean = + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = run[A,A](id[A])(fa) == fa - def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = + // fa.contramap(identity) == fa + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = + contramap[A,A](fa)(identity[A]) == fa + + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: Morph[A,B], g: Morph[B,C]): Boolean = run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) + + // fa.contramap(f).contramap(g) == fa.contramap(f compose g) + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: B => A, g: C => B): Boolean = { + val fb: F[B] = contramap[A, B](fa)(f) + val lhs: F[C] = contramap[B, C](fb)(g) + val fg: C => A = f compose g + val rhs: F[C] = contramap[A, C](fa)(fg) + lhs == rhs + } } -trait Extract[F[_]] extends FPAbs[ConstUnit, Id] { - type Morphism[AA, BB] = FPMorphism.Effect[F,AA] +trait MapFilter[F[_]] extends EndoFPAbs[F] { + override def run[A, B](f: A => Option[B]): F[A] => F[B] + type Morph[AA, BB] = FPMorphism.MapFilter[F,AA,BB] + def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] = run[A, B](f)(fa) +} - def extract[B](value: F[B]): B = run(value)(()) +trait MapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with MapFilter[F] { - def id[A]: Morphism[A, A] = ??? - def compose[A, B, C]: Morphism[A, B] => Morphism[B, C] => Morphism[A, C] = fab => _ => fab + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { + val lhs = run[A,A](id[A])(fa) + val rhs: F[A] = fa + lhs == rhs + } - def abstractionIdentityLaw[A](fa: F[A]): Boolean = - run[A,A](id[A])(fa) == fa // TODO + // id + // F[A] =======> F[A] + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val lhs: F[A] = mapFilter[A,A](fa)(Some(_)) + val rhs: F[A] = fa + lhs == rhs + } - def abstractionCompositionLaw[A,B,C](fa: F[A], f: Morphism[A,B], g: Morphism[B,C]): Boolean = - run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) -} + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: A => Option[B], g: B => Option[C]): Boolean = { + val lhs1: F[C] = run[B,C](g)( run[A,B](f)(fa) ) + val rhs1: F[C] = run[A,C]( compose(f)(g) )(fa) + lhs1 == rhs1 + } -// There are not contravariant monads ? + // f g compose(f,g) + // F[A] ====> F[B] =====> F[C] == F[A] ==============> F[B] + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: A => Option[B], g: B => Option[C]): Boolean = { + val fb: F[B] = mapFilter[A, B](fa)(f) + val rhs1: F[C] = mapFilter[B,C](fb)(g) + val lhs1: F[C] = mapFilter[A,C](fa)(compose(f)(g)) + lhs1 == rhs1 + } -trait ContraCoflatMap[F[_]] extends FPAbs[F, F] { - type Morphism[AA, BB] = FPMorphism.OpKleisli[F,AA,BB] - def contraExtend[A, B](fa: F[A])(f: B => F[A]): F[B] = run[A, B](f)(fa) + def id[A]: A => Option[A] = Some(_) + def compose[A, B, C]: (A => Option[B]) => (B => Option[C]) => (A => Option[C]) = f => g => a => + for { + b <- f(a) + c <- g(b) + } yield c } -abstract class ContraCoflatMapLaws[F[_] : Pure : Extract] extends EndoFPAbsLaw[F] with ContraCoflatMap[F] { - def id[A]: A => F[A] = a => implicitly[Pure[F]].pure(a) - def compose[A, B, C]: (B => F[A]) => (C => F[B]) => C => F[A] = bfa => cfb => c => - bfa(implicitly[Extract[F]].extract(cfb(c))) +trait ContraMapFilter[F[_]] extends EndoFPAbs[F] { + override def run[A, B](f: B => Option[A]): F[A] => F[B] + type Morph[AA, BB] = FPMorphism.ContraMapFilter[F,AA,BB] + def contramapFilter[A, B](fa: F[A])(f: B => Option[A]): F[B] = run[A, B](f)(fa) } -// There are not contravariant comonads ? -trait ContraFlatMap[F[_]] extends FPAbs[F, F] { - type Morphism[AA, BB] = FPMorphism.OpCoKleisli[F,AA,BB] - def contraExtend[A, B](fa: F[A])(f: F[B] => A): F[B] = run[A, B](f)(fa) -} +trait ContraMapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with ContraMapFilter[F] { + + def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { + val lhs = run[A,A](id[A])(fa) + val rhs: F[A] = fa + lhs == rhs + } + + // id + // F[A] =======> F[A] + def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + val lhs: F[A] = contramapFilter[A,A](fa)(Some(_)) + val rhs: F[A] = fa + lhs == rhs + } + + def abstractionCompositionLaw1[A,B,C](fa: F[A], f: B => Option[A], g: C => Option[B]): Boolean = { + val lhs1: F[C] = run[B,C](g)( run[A,B](f)(fa) ) + val rhs1: F[C] = run[A,C]( compose(f)(g) )(fa) + lhs1 == rhs1 + } + + // f g compose(f,g) + // F[A] ====> F[B] =====> F[C] == F[A] ==============> F[B] + def abstractionCompositionLaw2[A,B,C](fa: F[A], f: B => Option[A], g: C => Option[B]): Boolean = { + val fb: F[B] = contramapFilter[A, B](fa)(f) + val rhs1: F[C] = contramapFilter[B,C](fb)(g) + val lhs1: F[C] = contramapFilter[A,C](fa)(compose(f)(g)) + lhs1 == rhs1 + } -abstract class ContraFlatMapLaws[F[_] : Pure : Extract] extends EndoFPAbsLaw[F] with ContraFlatMap[F] { - def id[A]: F[A] => A = implicitly[Extract[F]].extract - def compose[A, B, C]: (F[B] => A) => (F[C] => B) => F[C] => A = fba => fcb => fc => - fba(implicitly[Pure[F]].pure(fcb(fc))) + def id[A]: A => Option[A] = Some(_) + def compose[A, B, C]: (B => Option[A]) => (C => Option[B]) => (C => Option[A]) = f => g => c => + g(c).flatMap(f) } -// TODO you could expand on different shapes but they do not make sense ? -// ConstUnit F B Pointed Applicative -// ConstUnit Id F[B] CoPointed CoApplicative +// OUTLAWS !!! -// Id ConstUnit F[B] -// Id ConstUnit B -// ConstUnit Id B +trait Alt[F[_]] extends EndoFPAbs[F] { + type Morph[AA, BB] = FPMorphism.Alt[F,AA,BB] + def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = run[A, B](fab)(fa) +} -// F ConstUnit B -// F ConstUnit F[B] -// ConstUnit F F[B] +abstract class AltLaws[F[_]] extends EndoFPAbsLaw[F] with Alt[F] { + def id[A]: F[Either[A,A]] = ??? + def compose[A, B, C]: F[Either[A,B]] => F[Either[B,C]] => F[Either[A,C]] = ??? +} -// Id F F[B] -// Id F B -// F Id F[B] -// F Id B +trait Divide[F[_]] extends EndoFPAbs[F] { + type Morph[AA, BB] = FPMorphism.OpAp[F,AA,BB] + def contraAp[A, B](fa: F[A])(fba: F[B => A]): F[B] = run[A, B](fba)(fa) +} + +// TODO there is problem wit divide laws +//abstract class DivideLaws[F[_] : Pure : Contravariant : Extract] extends EndoFPAbsLaw[F] with Divide[F] { +// def pure[A]: A => F[A] = implicitly[Pure[F]].pure +// def contramap[A,B](fa: F[A], ba: B => A): F[B] = implicitly[Contravariant[F]].contramap(fa)(ba) +// def extract[A,B](fa: F[A]): A = implicitly[Extract[F]].extract(fa) +// +// def id[A]: F[A => A] = pure[A => A](identity[A]) +// +// override def compose[A, B, C]: F[B => A] => F[C => B] => F[C => A] = fba => fcb => { +// type X = Any +// type Y = X => (B => A) +// +// val fgg: F[(X => (B => A)) => (C => B)] = ??? +// val fff: F[X => (B => A)] = run[C=>B,X => (B => A)](fgg)(fcb) +// +// val fg: F[X => (B => A)] = fff +// val ff: F[X] = run[B=>A,X](fg)(fba) +// ??? +// +//// val ba: B => A = extract(fba) +//// val cb: C => B = extract(fcb) +//// pure(cb andThen ba) +// } +//} diff --git a/src/main/scala/educational/category_theory/Comonad.scala b/src/main/scala/educational/category_theory/Comonad.scala index 9bad5349..89adecd6 100644 --- a/src/main/scala/educational/category_theory/Comonad.scala +++ b/src/main/scala/educational/category_theory/Comonad.scala @@ -36,7 +36,7 @@ W[A] W[W[A]] \ | \ | duplicate \ | -extend(duplicate) \ | +extend(duplicate) \ | \| \/ W[W[W[A]]] diff --git a/src/main/scala/educational/category_theory/Monad.scala b/src/main/scala/educational/category_theory/Monad.scala index 7b740e0a..7ff1c84e 100644 --- a/src/main/scala/educational/category_theory/Monad.scala +++ b/src/main/scala/educational/category_theory/Monad.scala @@ -50,12 +50,11 @@ object MonadInstance { def pure[A](a: A): Option[A] = Some(a) - def flatMap[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = { + def flatMap[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = ma match { case Some(a) => f(a) case None => None } - } } } From fd98a379e5eae140b5def49422c423d14e0c132e Mon Sep 17 00:00:00 2001 From: lemastero Date: Wed, 4 Aug 2021 23:37:52 +0200 Subject: [PATCH 05/10] add "de.b-studios" %% "effekt" --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index 87810558..a85969e4 100644 --- a/build.sbt +++ b/build.sbt @@ -27,6 +27,8 @@ libraryDependencies ++= Seq( "io.monix" %% "monix" % "3.4.0", + "de.b-studios" %% "effekt" % "0.4-SNAPSHOT", + // Scalaz "org.scalaz" %% "scalaz-core" % "7.3.4", From 15804e55145633e57de09bdf407a31e61e410316 Mon Sep 17 00:00:00 2001 From: lemastero Date: Wed, 4 Aug 2021 23:47:20 +0200 Subject: [PATCH 06/10] comment out "de.b-studios" %% "effekt" --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a85969e4..e9329904 100644 --- a/build.sbt +++ b/build.sbt @@ -27,7 +27,7 @@ libraryDependencies ++= Seq( "io.monix" %% "monix" % "3.4.0", - "de.b-studios" %% "effekt" % "0.4-SNAPSHOT", +// "de.b-studios" %% "effekt" % "0.4-SNAPSHOT", // Scalaz "org.scalaz" %% "scalaz-core" % "7.3.4", From 39f5191d34f06e16cab8601350edc0afbe3c69c4 Mon Sep 17 00:00:00 2001 From: lemastero Date: Wed, 4 Aug 2021 23:47:35 +0200 Subject: [PATCH 07/10] remove "de.b-studios" %% "effekt" --- build.sbt | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.sbt b/build.sbt index e9329904..87810558 100644 --- a/build.sbt +++ b/build.sbt @@ -27,8 +27,6 @@ libraryDependencies ++= Seq( "io.monix" %% "monix" % "3.4.0", -// "de.b-studios" %% "effekt" % "0.4-SNAPSHOT", - // Scalaz "org.scalaz" %% "scalaz-core" % "7.3.4", From a94bf4e33cf1cad3351bf704636c66f4ce4c727f Mon Sep 17 00:00:00 2001 From: lemastero Date: Thu, 2 Mar 2023 13:02:00 +0100 Subject: [PATCH 08/10] remove Monix --- build.sbt | 8 ++++---- src/main/scala/educational/category_theory/Functor.scala | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 6f708b56..e470d127 100644 --- a/build.sbt +++ b/build.sbt @@ -25,7 +25,7 @@ libraryDependencies ++= Seq( "org.typelevel" %% "cats-mtl-laws" % catsMtlVersion, "org.typelevel" %% "cats-effect" % "3.4.6", - "io.monix" %% "monix" % "3.4.1", + //"io.monix" %% "monix" % "3.4.1", // Scalaz "org.scalaz" %% "scalaz-core" % "7.3.7", @@ -43,13 +43,13 @@ libraryDependencies ++= Seq( "com.chuusai" %% "shapeless" % "2.3.10", // tofu - "tf.tofu" %% "tofu" % "0.11.1", + //"tf.tofu" %% "tofu" % "0.11.1", // izumi - "io.7mind.izumi" %% "fundamentals-bio" % "1.0.10", + //"io.7mind.izumi" %% "fundamentals-bio" % "1.0.10", // HoTT in Scala - "io.github.siddhartha-gadgil" %% "provingground-core-jvm" % "0.1.1", + //"io.github.siddhartha-gadgil" %% "provingground-core-jvm" % "0.1.1", // test "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, diff --git a/src/main/scala/educational/category_theory/Functor.scala b/src/main/scala/educational/category_theory/Functor.scala index 93b2da52..514260a2 100644 --- a/src/main/scala/educational/category_theory/Functor.scala +++ b/src/main/scala/educational/category_theory/Functor.scala @@ -1,6 +1,6 @@ package educational.category_theory -import monix.eval.Task +//import monix.eval.Task trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] @@ -47,9 +47,9 @@ object FunctorInstances { def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) } - val monixTaskFunctor: Functor[Task] = new Functor[Task] { - override def map[A, B](fa: Task[A])(f: A => B): Task[B] = fa.map(f) - } +// val monixTaskFunctor: Functor[Task] = new Functor[Task] { +// override def map[A, B](fa: Task[A])(f: A => B): Task[B] = fa.map(f) +// } def tupleRFunctor[X]: Functor[(X, *)] = new Functor[(X, *)] { From a0b15a9e8771166c7c78c17e02f1b73b4ec3bc55 Mon Sep 17 00:00:00 2001 From: lemastero Date: Thu, 2 Mar 2023 13:02:17 +0100 Subject: [PATCH 09/10] update WildMagic --- src/main/scala/ct_other_impls/WildMagic.scala | 124 +++++++----------- 1 file changed, 48 insertions(+), 76 deletions(-) diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala index 1164b9cf..b8656aa2 100644 --- a/src/main/scala/ct_other_impls/WildMagic.scala +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -2,19 +2,6 @@ package ct_other_impls /* -Given a category C (of types and pure curried functions) and mapping between this category and itself -F: C -> C - -Let Morph be a Functor (depending on F): -Morph: C -> Morph[C] - -that maps object of C into C and morphisms are defined by: - -def id[A]: Morph[A, A] -def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] - -then following diagram commute: - Morph[F,A,B] A ----------------> B | | @@ -24,57 +11,56 @@ F | | F F[A] -------------> F[B] run(Morph[F,A,B]) -Examples: +def id[A]: Morph[A, A] +def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] + +Signatures: + +|-------------------|--------------------------------|--------------- | +| Morphism | id | abstraction | +|-------------------|--------------------------------|----------------| +| A => B | Morph[F[_], A, B] = A => B | Functor | +| B => A | Morph[F[_], A, B] = B => A | Contravariant | +| A => F[B] | Morph[F[_], A, B] = A => F[B] | FlatMap | +| F[A] => B | Morph[F[_], A, B] = F[A] => B | CoflatMap | +| F[A => B] | Morph[F[_], A, B] = F[A => B] | Apply | +| A => Option[B] | Morph[F[_], A, B] = F[A => B] | Filter | -------------------|------------------------------|--------------- -Morphism | id | abstraction -------------------|------------------------------|---------------- -A => B | Morph[F[_], A, B] = A => B | Functor -B => A | Morph[F[_], A, B] = B => A | Contravariant -A => F[B] | Morph[F[_], A, B] = A => F[B] | FlatMap -F[A] => B | Morph[F[_], A, B] = F[A] => B | CoflatMap -F[A => B] | Morph[F[_], A, B] = F[A => B] | Ap (Apply) -A => Option[B] | Morph[F[_], A, B] = F[A => B] +Laws: + +// id[A] +// F[A] ========> F[A] + +// Morph[A,B] * Morph[C,C] +// F[A] ========================> F[C] |-----------------|----------------|--------------|-------------------| | abstraction | Morph | identity | composition | |-----------------|----------------|--------------|-------------------| | Functor | A => B | identity | andThen | -| FlatMap | A => F[B] | pure | kleisli comp. | +| FlatMap | A => F[B] | return | kleisli comp. | | CoflatMap | F[A] => B | extract | cokleisli comp. | -| Apply | F[A => B] | OK | OK | +| Apply | F[A => B] | F[identity] | OK | | Contravariant | B => A | identity | compose | -| MapFilter | A => Option[B] | OK | OK | -| ContraMapFilter | F[B] | OK | OK | +| MapFilter | A => Maybe[B] | Just | OK | +| ContraMapFilter | Maybe[B] => A | OK | OK | |-----------------|----------------|--------------|-------------------| */ -// In above definition if we replace morphism with twisted morphism we get: -// F[(A, B)] Morph[F[_], A, B] = F[(A, B)] Zip -// F[A \/ B] Morph[F[_], A, B] = F[A \/ B]] Alt -// F[B => A] Morph[F[_], A, B] = F[B => A] Divide - object FPMorphism { type Function[A, B] = A => B type Kleisli[F[_],A,B] = A => F[B] type Ap[F[_], A, B] = F[A => B] type CoKleisli[F[_],A,B] = F[A] => B type MapFilter[F[_],A,B] = A => Option[B] - type Op[A, B] = B => A type ContraMapFilter[F[_],A,B] = B => Option[A] - - type OpAp[F[_], A, B] = F[B => A] - type Zip[F[_], A, B] = F[(A, B)] - type Alt[F[_], A, B] = F[Either[A, B]] } trait EndoFPAbs[F[_]] { type Morph[_, _] // specify some transformation on types A, B possibly using F and G e.g. A => F[B] - - type BiKleisli[A,B] = F[A] => F[B] - def run[A, B](f: Morph[A, B]): BiKleisli[A,B] + def run[A, B](f: Morph[A, B]): F[A] => F[B] } trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { @@ -83,7 +69,7 @@ trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] // id[A] - // F[A] ========> F[A] == F[A] + // F[A] ========> F[A] def abstractionIdentityLaw[A](fa: F[A]): Boolean = { val lhs: F[A] = run[A, A](id[A])(fa) lhs == fa @@ -102,6 +88,8 @@ trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { } } +////// Functor ////////// + trait Functor[F[_]] extends EndoFPAbs[F] { override def run[A, B](f: A => B): F[A] => F[B] type Morph[AA, BB] = FPMorphism.Function[AA,BB] @@ -145,21 +133,29 @@ trait FunctorLaws[F[_]] extends EndoFPAbsLaw[F] with Functor[F] { def compose[A, B, C]: (A => B) => (B => C) => (A => C) = f => f andThen _ } +////// FlatMap ////////// + trait FlatMap[F[_]] extends EndoFPAbs[F] { type Morph[AA, BB] = FPMorphism.Kleisli[F,AA,BB] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = run[A, B](f)(fa) } +trait Pure[F[_]] { + def pure[A](value: A): F[A] +} + +// needs Pure/Return so it is Monad + // flatMap(pure) == id // fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g) ) abstract class FlatMapLaws[F[_]: Pure] extends EndoFPAbsLaw[F] with FlatMap[F] { def pure[A](a:A): F[A] = implicitly[Pure[F]].pure(a) // FlatMap and Pointed are connected, give raise to Monad - def id[A]: A => F[A] = pure[A] +def id[A]: A => F[A] = pure[A] - def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = - f => g => a => run[B,C](g)(f(a)) +def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = + f => g => a => run[B,C](g)(f(a)) def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { val lhs: F[A] = run[A, A](id[A])(fa) @@ -248,6 +244,10 @@ trait CoFlatMap[F[_]] extends EndoFPAbs[F] { def duplicate[A](fa: F[A]): F[F[A]] = extend(fa)(identity) } +trait Extract[F[_]] { + def extract[A](w: F[A]): A +} + // fa.extend(extract) == fa // fa.extend(f).extend(g) == ffa.extend( compose(f,g) ) abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMap[F] { @@ -407,41 +407,13 @@ trait ContraMapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with ContraMapFilter[F] // OUTLAWS !!! trait Alt[F[_]] extends EndoFPAbs[F] { - type Morph[AA, BB] = FPMorphism.Alt[F,AA,BB] + type Alt[F[_], A, B] = F[Either[A, B]] + type Morph[AA, BB] = Alt[F,AA,BB] def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = run[A, B](fab)(fa) } -abstract class AltLaws[F[_]] extends EndoFPAbsLaw[F] with Alt[F] { - def id[A]: F[Either[A,A]] = ??? - def compose[A, B, C]: F[Either[A,B]] => F[Either[B,C]] => F[Either[A,C]] = ??? -} - trait Divide[F[_]] extends EndoFPAbs[F] { - type Morph[AA, BB] = FPMorphism.OpAp[F,AA,BB] + type OpAp[F[_], A, B] = F[B => A] + type Morph[AA, BB] = OpAp[F,AA,BB] def contraAp[A, B](fa: F[A])(fba: F[B => A]): F[B] = run[A, B](fba)(fa) } - -// TODO there is problem wit divide laws -//abstract class DivideLaws[F[_] : Pure : Contravariant : Extract] extends EndoFPAbsLaw[F] with Divide[F] { -// def pure[A]: A => F[A] = implicitly[Pure[F]].pure -// def contramap[A,B](fa: F[A], ba: B => A): F[B] = implicitly[Contravariant[F]].contramap(fa)(ba) -// def extract[A,B](fa: F[A]): A = implicitly[Extract[F]].extract(fa) -// -// def id[A]: F[A => A] = pure[A => A](identity[A]) -// -// override def compose[A, B, C]: F[B => A] => F[C => B] => F[C => A] = fba => fcb => { -// type X = Any -// type Y = X => (B => A) -// -// val fgg: F[(X => (B => A)) => (C => B)] = ??? -// val fff: F[X => (B => A)] = run[C=>B,X => (B => A)](fgg)(fcb) -// -// val fg: F[X => (B => A)] = fff -// val ff: F[X] = run[B=>A,X](fg)(fba) -// ??? -// -//// val ba: B => A = extract(fba) -//// val cb: C => B = extract(fcb) -//// pure(cb andThen ba) -// } -//} From 4d1f42459c41ed568090ae4982e15c753c56606e Mon Sep 17 00:00:00 2001 From: lemastero Date: Thu, 2 Mar 2023 19:57:34 +0100 Subject: [PATCH 10/10] cleanup diagrams, expand signatures --- src/main/scala/ct_other_impls/WildMagic.scala | 214 +++++++++--------- 1 file changed, 102 insertions(+), 112 deletions(-) diff --git a/src/main/scala/ct_other_impls/WildMagic.scala b/src/main/scala/ct_other_impls/WildMagic.scala index b8656aa2..d5f65167 100644 --- a/src/main/scala/ct_other_impls/WildMagic.scala +++ b/src/main/scala/ct_other_impls/WildMagic.scala @@ -2,51 +2,66 @@ package ct_other_impls /* - Morph[F,A,B] - A ----------------> B - | | -F | | F - | | - \/ \/ - F[A] -------------> F[B] +Signature: +Morhp : F[_] ~> P[_,_] (converts functor into profunctor) + +run: Morph[F,A,B] => F[A] => F[B] + + F[A] -----------------> F[B] run(Morph[F,A,B]) +Helpers: + def id[A]: Morph[A, A] def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] -Signatures: +Laws: -|-------------------|--------------------------------|--------------- | -| Morphism | id | abstraction | -|-------------------|--------------------------------|----------------| -| A => B | Morph[F[_], A, B] = A => B | Functor | -| B => A | Morph[F[_], A, B] = B => A | Contravariant | -| A => F[B] | Morph[F[_], A, B] = A => F[B] | FlatMap | -| F[A] => B | Morph[F[_], A, B] = F[A] => B | CoflatMap | -| F[A => B] | Morph[F[_], A, B] = F[A => B] | Apply | -| A => Option[B] | Morph[F[_], A, B] = F[A => B] | Filter | + id[A] + F[A] -----------> F[A] -Laws: -// id[A] -// F[A] ========> F[A] + f : Mor[A,B] + F[A] --------------> F[B] + \ | + \ | g: Mor[B,C] + \ | + f * g \ | + \ | + \ | + \ | + \/ -// Morph[A,B] * Morph[C,C] -// F[A] ========================> F[C] +* is compose -|-----------------|----------------|--------------|-------------------| -| abstraction | Morph | identity | composition | -|-----------------|----------------|--------------|-------------------| -| Functor | A => B | identity | andThen | -| FlatMap | A => F[B] | return | kleisli comp. | -| CoflatMap | F[A] => B | extract | cokleisli comp. | -| Apply | F[A => B] | F[identity] | OK | -| Contravariant | B => A | identity | compose | -| MapFilter | A => Maybe[B] | Just | OK | -| ContraMapFilter | Maybe[B] => A | OK | OK | -|-----------------|----------------|--------------|-------------------| +Signatures: - */ +|----------------|--------------------------------|--------------- |-----------------------------------|-----------| +| superpower | Morph | abstraction | run: Morph[F,A,B] => F[A] => F[B] | | +|----------------|--------------------------------|----------------|-----------------------------------|-----------| +| A => B | Morph[F[_], A, B] = A => B | Functor | (A => B) => F[A] => F[B] | map | +| B => A | Morph[F[_], A, B] = B => A | Contravariant | (B => A) => F[A] => F[B] | contramap | +| A => F[B] | Morph[F[_], A, B] = A => F[B] | FlatMap | (A => F[B]) => F[A] => F[B] | flatMap | +| F[A] => B | Morph[F[_], A, B] = F[A] => B | CoflatMap | (F[A] => B) => F[A] => F[B] | coflatMap | +| F[A => B] | Morph[F[_], A, B] = F[A => B] | Apply | F[A => B] => F[A] => F[B] | ap | +| A => Option[B] | Morph[F[_], A, B] = F[A => B] | Filter | A => Option[B] => F[A] => F[B] | filter | +-------------------------------------------------------------------------------------------------------|-----------| + +// Categories? Promonads? + +|-----------------|----------------|---------------|-------------------------------------------------| +| abstraction | Morph | identity | composition | +|-----------------|----------------|---------------|-------------------------------------------------| +| Functor | A => B | A => A | (A => B) -> (B => C) -> (A => C) | +| FlatMap | A => F[B] | A => F[A] | (A => F[B]) => (B => F[C]) => (A => F[C]) | +| CoflatMap | F[A] => B | F[A] => A | (F[A] => B) => (F[B] => C) => F[A] => C | +| Apply | F[A => B] | F[A => A] | (B => A) => (C => B) => (C => A) | +| Contravariant | B => A | A => A | (B => A) => (C => B) => (C => A) | +| MapFilter | A => Maybe[B] | A => Maybe[A] | (A => Opt[B]) => (B => Opt[C]) => (A => Opt[C]) | +|-----------------|----------------|---------------|-------------------------------------------------| + +Opt is Option +*/ object FPMorphism { type Function[A, B] = A => B @@ -58,12 +73,12 @@ object FPMorphism { type ContraMapFilter[F[_],A,B] = B => Option[A] } -trait EndoFPAbs[F[_]] { +trait Abstraction[F[_]] { type Morph[_, _] // specify some transformation on types A, B possibly using F and G e.g. A => F[B] def run[A, B](f: Morph[A, B]): F[A] => F[B] } -trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { +trait AbstractionLaw[F[_]] extends Abstraction[F] { // define category structure on a Morphism def id[A]: Morph[A, A] def compose[A, B, C]: Morph[A, B] => Morph[B, C] => Morph[A, C] @@ -75,8 +90,8 @@ trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { lhs == fa } - // Mor[A,B] Mor[B,C] - // F[A] ============> F[B] ===========> F[C] + // f : Mor[A,B] g: Mor[B,C] + // F[A] ===============> F[B] =============> F[C] // // Mor[A,B] * Mor[C,C] // F[A] =======================> F[C] @@ -90,7 +105,7 @@ trait EndoFPAbsLaw[F[_]] extends EndoFPAbs[F] { ////// Functor ////////// -trait Functor[F[_]] extends EndoFPAbs[F] { +trait Functor[F[_]] extends Abstraction[F] { override def run[A, B](f: A => B): F[A] => F[B] type Morph[AA, BB] = FPMorphism.Function[AA,BB] def map[A, B](fa: F[A])(f: A => B): F[B] = run[A, B](f)(fa) @@ -98,44 +113,35 @@ trait Functor[F[_]] extends EndoFPAbs[F] { // fa.map(id) == fa // fa.map(f).map(g) == fa.map(f andThen g) -trait FunctorLaws[F[_]] extends EndoFPAbsLaw[F] with Functor[F] { +trait FunctorLaws[F[_]] extends AbstractionLaw[F] with Functor[F] { + def id[A]: A => A = identity[A] + def compose[A, B, C]: (A => B) => (B => C) => (A => C) = + f => g => f andThen g - def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { - val lhs: F[A] = run[A,A](id[A])(fa) - val rhs: F[A] = fa - lhs == rhs - } + // XXX laws here and below showed just to understand how they expand + // override to verify if types match // id // F[A] =======> F[A] - def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { - val lhs: F[A] = map[A,A](fa)(identity[A]) + override def abstractionIdentityLaw[A](fa: F[A]): Boolean = { + val lhs: F[A] = run[A,A](identity[A])(fa) val rhs: F[A] = fa lhs == rhs } - def abstractionCompositionLaw1[A,B,C](fa: F[A], f: A => B, g: B => C): Boolean = { - val lhs1: F[C] = run[B,C](g)( run[A,B](f)(fa) ) - val rhs1: F[C] = run[A,C]( compose(f)(g) )(fa) - lhs1 == rhs1 - } - // f g f andThen g - // F[A] ====> F[B] =====> F[C] == F[A] ==============> F[B] - def abstractionCompositionLaw2[A,B,C](fa: F[A], f: A => B, g: B => C): Boolean = { + // F[A] ====> F[B] =====> F[C] = F[A] ==============> F[B] + override def abstractionCompositionLaw[A,B,C](fa: F[A], f: A => B, g: B => C): Boolean = { val fb: F[B] = map[A, B](fa)(f) val rhs1: F[C] = map[B,C](fb)(g) - val lhs1: F[C] = map[A,C](fa)(f andThen g) + val lhs1: F[C] = map[A,C](fa)(g compose f) lhs1 == rhs1 } - - def id[A]: A => A = identity[A] - def compose[A, B, C]: (A => B) => (B => C) => (A => C) = f => f andThen _ } ////// FlatMap ////////// -trait FlatMap[F[_]] extends EndoFPAbs[F] { +trait FlatMap[F[_]] extends Abstraction[F] { type Morph[AA, BB] = FPMorphism.Kleisli[F,AA,BB] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = run[A, B](f)(fa) } @@ -148,41 +154,27 @@ trait Pure[F[_]] { // flatMap(pure) == id // fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g) ) -abstract class FlatMapLaws[F[_]: Pure] extends EndoFPAbsLaw[F] with FlatMap[F] { +abstract class FlatMapLaws[F[_]: Pure] extends AbstractionLaw[F] with FlatMap[F] { def pure[A](a:A): F[A] = implicitly[Pure[F]].pure(a) - // FlatMap and Pointed are connected, give raise to Monad -def id[A]: A => F[A] = pure[A] - -def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = - f => g => a => run[B,C](g)(f(a)) + // XXX FlatMap and Pointed are connected, give raise to Monad (!!!) + def id[A]: A => F[A] = pure[A] + def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = + f => g => a => run[B,C](g)(f(a)) - def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { - val lhs: F[A] = run[A, A](id[A])(fa) - lhs == fa - } - - def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + // below just for illustration how laws expands + override def abstractionIdentityLaw[A](fa: F[A]): Boolean = { val pa: A => F[A] = pure[A] val lhs: F[A] = flatMap[A, A](fa)(pa) lhs == fa } - def abstractionCompositionLaw1[A,B,C](fa: F[A], f: A => F[B], g: B => F[C]): Boolean = { - val fb: F[B] = run[A, B](f)(fa) - val lhs: F[C] = run[B, C](g)(fb) - - val fg: A => F[C] = compose(f)(g) - val rhs: F[C] = run[A, C](fg)(fa) - lhs == rhs - } - // flatMap(f) flatMap(g) // F[A] -------------> F[B] -------------> F[C] // flatMap(a => flatMap(f(a))(g)) // F[A] ----------------------------------> F[C] - def abstractionCompositionLaw2[A,B,C](fa: F[A], f: A => F[B], g: B => F[C]): Boolean = { + override def abstractionCompositionLaw[A,B,C](fa: F[A], f: A => F[B], g: B => F[C]): Boolean = { val fb: F[B] = flatMap[A, B](fa)(f) val lhs: F[C] = flatMap[B, C](fb)(g) @@ -192,14 +184,14 @@ def compose[A,B,C]: (A => F[B]) => (B => F[C]) => (A => F[C]) = } } -trait Apply[F[_]] extends EndoFPAbs[F] { +trait Apply[F[_]] extends Abstraction[F] { type Morph[AA, BB] = FPMorphism.Ap[F,AA,BB] def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = run[A, B](f)(fa) } // fa.pure(identity) == fa // fa.ap(f).ap(g) == fa.ap( compose(f,g) ) // compose uses pure -abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { +abstract class ApplyLaws[F[_] : Pure] extends AbstractionLaw[F] with Apply[F] { def pure[A]: A => F[A] = implicitly[Pure[F]].pure def id[A]: F[A => A] = pure(identity[A]) @@ -209,12 +201,13 @@ abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { run[B => C,A => C](fbcac) } + // below laws for illustration def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { val lhs: F[A] = run[A, A](id[A])(fa) lhs == fa } - def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + override def abstractionIdentityLaw[A](fa: F[A]): Boolean = { val lhs: F[A] = ap[A, A](fa)(pure(identity[A])) lhs == fa } @@ -225,7 +218,7 @@ abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { lhs == rhs } - def abstractionCompositionLaw2[A,B,C](fa: F[A], fab: F[A => B], fbc: F[B => C]): Boolean = { + override def abstractionCompositionLaw[A,B,C](fa: F[A], fab: F[A => B], fbc: F[B => C]): Boolean = { val fb: F[B] = ap[A, B](fa)(fab) val lhs: F[C] = ap[B, C](fb)(fbc) val fac: F[A => C] = { @@ -238,7 +231,7 @@ abstract class ApplyLaws[F[_] : Pure] extends EndoFPAbsLaw[F] with Apply[F] { } } -trait CoFlatMap[F[_]] extends EndoFPAbs[F] { +trait CoFlatMap[F[_]] extends Abstraction[F] { type Morph[AA, BB] = FPMorphism.CoKleisli[F,AA,BB] def extend[A, B](fa: F[A])(f: F[A] => B): F[B] = run[A, B](f)(fa) def duplicate[A](fa: F[A]): F[F[A]] = extend(fa)(identity) @@ -250,18 +243,19 @@ trait Extract[F[_]] { // fa.extend(extract) == fa // fa.extend(f).extend(g) == ffa.extend( compose(f,g) ) -abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMap[F] { +abstract class CoFlatMapLaws[F[_]:Extract] extends AbstractionLaw[F] with CoFlatMap[F] { def extract[A]: F[A] => A = implicitly[Extract[F]].extract _ def id[A]: F[A] => A = extract def compose[A, B, C]: (F[A] => B) => (F[B] => C) => F[A] => C = fab => fbc => fa => extract(run(fbc)(run(fab)(fa))) + // laws for illustration def abstractionIdentityLaw1[A](fa: F[A]): Boolean = run[A,A](id[A])(fa) == fa // fa.extend(extract) == fa - def abstractionIdentityLaw2[A](fa: F[A]): Boolean = { + override def abstractionIdentityLaw[A](fa: F[A]): Boolean = { val lhs: F[A] = extend[A, A](fa)(extract[A]) lhs == fa } @@ -270,7 +264,7 @@ abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMa run[B,C](g)( run[A,B](f)(fa) ) == run[A,C]( compose(f)(g) )(fa) // fa.extend(f).extend(g) == ffa.extend( compose(f,g) ) - def abstractionCompositionLaw2[A,B,C](fa: F[A], f: F[A] => B, g: F[B] => C): Boolean = { + override def abstractionCompositionLaw[A,B,C](fa: F[A], f: F[A] => B, g: F[B] => C): Boolean = { val fb: F[B] = extend[A, B](fa)(f) val lhs: F[C] = extend[B, C](fb)(g) val fg: F[A] => C = fa => { @@ -283,7 +277,7 @@ abstract class CoFlatMapLaws[F[_]:Extract] extends EndoFPAbsLaw[F] with CoFlatMa } } -trait Contravariant[F[_]] extends EndoFPAbs[F] { +trait Contravariant[F[_]] extends Abstraction[F] { type Morph[AA, BB] = FPMorphism.Op[AA,BB] def contramap[A, B](fa: F[A])(f: B => A): F[B] = run[A, B](f)(fa) @@ -291,7 +285,7 @@ trait Contravariant[F[_]] extends EndoFPAbs[F] { // fa.contramap(identity) == fa // fa.contramap(f).contramap(g) == fa.contramap(f compose g) -trait ContravariantLaws[F[_]] extends EndoFPAbsLaw[F] with Contravariant[F] { +trait ContravariantLaws[F[_]] extends AbstractionLaw[F] with Contravariant[F] { def id[A]: A => A = identity[A] def compose[A, B, C]: (B => A) => (C => B) => (C => A) = f => g => f compose g @@ -315,13 +309,19 @@ trait ContravariantLaws[F[_]] extends EndoFPAbsLaw[F] with Contravariant[F] { } } -trait MapFilter[F[_]] extends EndoFPAbs[F] { +trait MapFilter[F[_]] extends Abstraction[F] { override def run[A, B](f: A => Option[B]): F[A] => F[B] type Morph[AA, BB] = FPMorphism.MapFilter[F,AA,BB] def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] = run[A, B](f)(fa) } -trait MapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with MapFilter[F] { +trait MapFilterLaws[F[_]] extends AbstractionLaw[F] with MapFilter[F] { + def id[A]: A => Option[A] = Some(_) + def compose[A, B, C]: (A => Option[B]) => (B => Option[C]) => (A => Option[C]) = f => g => a => + for { + b <- f(a) + c <- g(b) + } yield c def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { val lhs = run[A,A](id[A])(fa) @@ -351,23 +351,18 @@ trait MapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with MapFilter[F] { val lhs1: F[C] = mapFilter[A,C](fa)(compose(f)(g)) lhs1 == rhs1 } - - def id[A]: A => Option[A] = Some(_) - def compose[A, B, C]: (A => Option[B]) => (B => Option[C]) => (A => Option[C]) = f => g => a => - for { - b <- f(a) - c <- g(b) - } yield c } -trait ContraMapFilter[F[_]] extends EndoFPAbs[F] { +trait ContraMapFilter[F[_]] extends Abstraction[F] { override def run[A, B](f: B => Option[A]): F[A] => F[B] type Morph[AA, BB] = FPMorphism.ContraMapFilter[F,AA,BB] def contramapFilter[A, B](fa: F[A])(f: B => Option[A]): F[B] = run[A, B](f)(fa) } - -trait ContraMapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with ContraMapFilter[F] { +trait ContraMapFilterLaws[F[_]] extends AbstractionLaw[F] with ContraMapFilter[F] { + def id[A]: A => Option[A] = Some(_) + def compose[A, B, C]: (B => Option[A]) => (C => Option[B]) => (C => Option[A]) = f => g => c => + g(c).flatMap(f) def abstractionIdentityLaw1[A](fa: F[A]): Boolean = { val lhs = run[A,A](id[A])(fa) @@ -397,22 +392,17 @@ trait ContraMapFilterLaws[F[_]] extends EndoFPAbsLaw[F] with ContraMapFilter[F] val lhs1: F[C] = contramapFilter[A,C](fa)(compose(f)(g)) lhs1 == rhs1 } - - def id[A]: A => Option[A] = Some(_) - def compose[A, B, C]: (B => Option[A]) => (C => Option[B]) => (C => Option[A]) = f => g => c => - g(c).flatMap(f) } +// OUTLAWS !!! Is it possible to define laws for that? -// OUTLAWS !!! - -trait Alt[F[_]] extends EndoFPAbs[F] { +trait Alt[F[_]] extends Abstraction[F] { type Alt[F[_], A, B] = F[Either[A, B]] type Morph[AA, BB] = Alt[F,AA,BB] def either2[A, B](fa: F[A])(fab: F[Either[A, B]]): F[B] = run[A, B](fab)(fa) } -trait Divide[F[_]] extends EndoFPAbs[F] { +trait Divide[F[_]] extends Abstraction[F] { type OpAp[F[_], A, B] = F[B => A] type Morph[AA, BB] = OpAp[F,AA,BB] def contraAp[A, B](fa: F[A])(fba: F[B => A]): F[B] = run[A, B](fba)(fa)