├── .ensime ├── .gitignore ├── LICENSE ├── build.sbt ├── project ├── build.properties └── plugins.sbt ├── src ├── main │ └── scala │ │ ├── Eff.scala │ │ ├── Effective.scala │ │ ├── file │ │ ├── FileIO.scala │ │ └── package.scala │ │ ├── package.scala │ │ ├── state │ │ └── State.scala │ │ └── stdio │ │ └── StdIO.scala └── test │ └── scala │ └── EffSpec.scala ├── tata.txt └── toto.txt /.ensime: -------------------------------------------------------------------------------- 1 | ( 2 | :root-dir "/Users/mandubian/workspaces/mandubian/scalaeff" 3 | :cache-dir "/Users/mandubian/workspaces/mandubian/scalaeff/.ensime_cache" 4 | :name "root" 5 | :java-home "/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home" 6 | :java-flags ("-Xss2m" "-Xms1024m" "-Xmx1024m" "-XX:ReservedCodeCacheSize=128m" "-XX:MaxMetaspaceSize=256m") 7 | :reference-source-roots ("/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/src.zip") 8 | :scala-version "2.11.7" 9 | :compiler-args ("-feature" "-deprecation" "-Xlint" "-Yinline-warnings" "-Yno-adapted-args" "-Ywarn-dead-code" "-Ywarn-numeric-widen" "-Xfuture" "-Ywarn-unused-import") 10 | 11 | :subprojects (( 12 | :name "root" 13 | :source-roots ("/Users/mandubian/workspaces/mandubian/scalaeff/src/main/scala" "/Users/mandubian/workspaces/mandubian/scalaeff/src/test/scala") 14 | :targets ("/Users/mandubian/workspaces/mandubian/scalaeff/target/scala-2.11/classes") 15 | :test-targets ("/Users/mandubian/workspaces/mandubian/scalaeff/target/scala-2.11/test-classes") 16 | :depends-on-modules nil 17 | :compile-deps ("/Users/mandubian/.ivy2/cache/com.chuusai/shapeless_2.11/bundles/shapeless_2.11-2.2.4.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats_2.11/jars/cats_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra_2.11/jars/algebra_2.11-0.3.1.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.11.7.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-macros_2.11/jars/cats-macros_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-free_2.11/jars/cats-free_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-laws_2.11/jars/algebra-laws_2.11-0.3.1.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/discipline_2.11/jars/discipline_2.11-0.4.jar" "/Users/mandubian/.ivy2/cache/com.github.mpilquist/simulacrum_2.11/jars/simulacrum_2.11-0.4.0.jar" "/Users/mandubian/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-std_2.11/jars/algebra-std_2.11-0.3.1.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-laws_2.11/jars/cats-laws_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-state_2.11/jars/cats-state_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.scalacheck/scalacheck_2.11/jars/scalacheck_2.11-1.12.4.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/machinist_2.11/jars/machinist_2.11-0.4.1.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.11.7.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-core_2.11/jars/cats-core_2.11-0.2.0.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-macros_2.11/jars/algebra-macros_2.11-0.3.1.jar") 18 | :runtime-deps nil 19 | :test-deps ("/Users/mandubian/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11/bundles/scala-xml_2.11-1.0.2.jar" "/Users/mandubian/.ivy2/cache/org.scalatest/scalatest_2.11/bundles/scalatest_2.11-2.2.1.jar") 20 | :doc-jars ("/Users/mandubian/.ivy2/cache/org.spire-math/cats-laws_2.11/docs/cats-laws_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scala-sbt/test-interface/docs/test-interface-1.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/machinist_2.11/docs/machinist_2.11-0.4.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scalatest/scalatest_2.11/docs/scalatest_2.11-2.2.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra_2.11/docs/algebra_2.11-0.3.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11/docs/scala-xml_2.11-1.0.2-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-library/docs/scala-library-2.11.7-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scalacheck/scalacheck_2.11/docs/scalacheck_2.11-1.12.4-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/discipline_2.11/docs/discipline_2.11-0.4-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-macros_2.11/docs/algebra-macros_2.11-0.3.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-macros_2.11/docs/cats-macros_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats_2.11/docs/cats_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-state_2.11/docs/cats-state_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-std_2.11/docs/algebra-std_2.11-0.3.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/com.github.mpilquist/simulacrum_2.11/docs/simulacrum_2.11-0.4.0-javadoc.jar" "/Users/mandubian/workspaces/mandubian/scalaeff/target/scala-2.11/scalaeff_2.11-0.1.0-SNAPSHOT-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-free_2.11/docs/cats-free_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-laws_2.11/docs/algebra-laws_2.11-0.3.1-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-reflect/docs/scala-reflect-2.11.7-javadoc.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-core_2.11/docs/cats-core_2.11-0.2.0-javadoc.jar" "/Users/mandubian/.ivy2/cache/com.chuusai/shapeless_2.11/docs/shapeless_2.11-2.2.4-javadoc.jar") 21 | :reference-source-roots ("/Users/mandubian/.ivy2/cache/com.chuusai/shapeless_2.11/srcs/shapeless_2.11-2.2.4-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-macros_2.11/srcs/algebra-macros_2.11-0.3.1-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-laws_2.11/srcs/cats-laws_2.11-0.2.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra_2.11/srcs/algebra_2.11-0.3.1-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-state_2.11/srcs/cats-state_2.11-0.2.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-std_2.11/srcs/algebra-std_2.11-0.3.1-sources.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/machinist_2.11/srcs/machinist_2.11-0.4.1-sources.jar" "/Users/mandubian/.ivy2/cache/com.github.mpilquist/simulacrum_2.11/srcs/simulacrum_2.11-0.4.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.typelevel/discipline_2.11/srcs/discipline_2.11-0.4-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats_2.11/srcs/cats_2.11-0.2.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-macros_2.11/srcs/cats-macros_2.11-0.2.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-library/srcs/scala-library-2.11.7-sources.jar" "/Users/mandubian/.ivy2/cache/org.scalacheck/scalacheck_2.11/srcs/scalacheck_2.11-1.12.4-sources.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11/srcs/scala-xml_2.11-1.0.2-sources.jar" "/Users/mandubian/.ivy2/cache/org.scala-sbt/test-interface/srcs/test-interface-1.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/algebra-laws_2.11/srcs/algebra-laws_2.11-0.3.1-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-core_2.11/srcs/cats-core_2.11-0.2.0-sources.jar" "/Users/mandubian/.ivy2/cache/org.scala-lang/scala-reflect/srcs/scala-reflect-2.11.7-sources.jar" "/Users/mandubian/.ivy2/cache/org.scalatest/scalatest_2.11/srcs/scalatest_2.11-2.2.1-sources.jar" "/Users/mandubian/.ivy2/cache/org.spire-math/cats-free_2.11/srcs/cats-free_2.11-0.2.0-sources.jar"))) 22 | ) 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .history 3 | 4 | target 5 | logs 6 | docker.log 7 | RUNNING_PID 8 | app-2.10 9 | app-2.11 10 | npm-debug.log 11 | 12 | # IntelliJ 13 | .idea/ 14 | .idea_modules/ 15 | 16 | # eclipse 17 | .classpath 18 | .project 19 | .scala_dependencies 20 | .settings 21 | .buildpath 22 | .target 23 | 24 | # Atom 25 | config.json 26 | 27 | # temp file 28 | .~* 29 | *~ 30 | *.orig 31 | DWTODO 32 | 33 | .ensime_cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | cancelable in Global := true 2 | 3 | lazy val commonSettings = Seq( 4 | organization := "scalaeff" 5 | , name := "scalaeff" 6 | , version := "0.1.0-SNAPSHOT" 7 | , scalaVersion := "2.11.7" 8 | , logLevel in update := Level.Warn 9 | ) 10 | 11 | lazy val strictScalac = 12 | scalacOptions ++= Seq( 13 | "-Yrangepos" 14 | , "-Xlint" 15 | ,"-deprecation" 16 | , "-Xfatal-warnings" 17 | , "-feature" 18 | , "-encoding", "UTF-8" 19 | //, "-unchecked" 20 | , "-Yno-adapted-args" 21 | , "-Ywarn-dead-code" 22 | , "-Ywarn-numeric-widen" 23 | , "-Ywarn-value-discard" 24 | , "-Xfuture" 25 | //, "-Ywarn-unused-import" 26 | ) 27 | 28 | resolvers += Resolver.sonatypeRepo("releases") 29 | 30 | 31 | lazy val root = project.in(file(".")) 32 | .settings(commonSettings:_*) 33 | .settings(name := "scalaeff") 34 | .settings( 35 | libraryDependencies ++= Seq( 36 | "com.chuusai" %% "shapeless" % "2.2.5" 37 | , "org.scalatest" %% "scalatest" % "2.2.1" % "test" 38 | , "org.spire-math" %% "cats" % "0.2.0" 39 | // , "org.typelevel" %% "alleycats" % "0.1.2" 40 | , "org.typelevel" %% "export-hook" % "1.1.0" 41 | , "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided" 42 | , compilerPlugin("org.scalamacros" % "paradise" % "2.1.0-M5" cross CrossVersion.full) 43 | ) 44 | ) 45 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.ensime" % "ensime-sbt" % "0.2.1") 2 | -------------------------------------------------------------------------------- /src/main/scala/Eff.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package effects 17 | 18 | import shapeless._ 19 | import syntax.singleton._ 20 | import record._ 21 | import ops.hlist._ 22 | import syntax.SingletonOps 23 | 24 | import cats.Applicative 25 | import scala.language.higherKinds 26 | import scala.language.experimental.macros 27 | 28 | import scala.annotation.implicitNotFound 29 | 30 | 31 | /** Effect Algebraic base */ 32 | trait Effect { 33 | type T 34 | type ResI 35 | type ResO 36 | } 37 | 38 | object Effect { 39 | type Aux[T0, ResI0, ResO0] = Effect { type T = T0; type ResI = ResI0; type ResO = ResO0 } 40 | } 41 | 42 | trait EffectBuilder[E <: Effect] { 43 | 44 | val Res = new MkEffBuilder {} 45 | 46 | trait MkEffBuilder { 47 | def apply[Res](res: Res): MkEff[E, Res] = MkEff(res) 48 | } 49 | 50 | } 51 | 52 | trait Handler[E <: Effect, M[_]] { 53 | type T 54 | type ResI 55 | type ResO 56 | 57 | def handle[A](e: E)(res: ResI)(k: T => ResO => M[A]): M[A] 58 | } 59 | 60 | object Handler { 61 | type Aux[E <: Effect, T0, ResI0, ResO0, M[_]] = Handler[E, M] { type T = T0; type ResI = ResI0; type ResO = ResO0 } 62 | } 63 | 64 | /** EFFECT reification with a resource */ 65 | trait EffectM 66 | case class MkEff[E <: Effect, Res](res: Res) extends EffectM { 67 | def -:[Lbl](lbl: Lbl): MkEff[E, Res @@ Lbl] = MkEff[E, Res @@ Lbl](res) 68 | } 69 | 70 | sealed trait EffM[M[_], A, ESI <: HList, ESO <: HList, HS <: HList] { 71 | import EffM._ 72 | 73 | def run[ESI0 <: HList](es: ESI0)( 74 | implicit ap: Applicative[M] 75 | , iso: IsoList[ESI0, ESI] 76 | , hs: Handlers[M, HS] 77 | ): M[A] = eff(hs.handlers)(iso(es))(a => eso => ap.pure(a)) 78 | 79 | def runPure(es: ESI)( 80 | implicit ev: M[A] =:= A, ap: Applicative[M] 81 | , hs: Handlers[M, HS] 82 | ): M[(ESO, A)] = 83 | eff(hs.handlers)(es)( a => env => ap.pure((env, a)) ) 84 | 85 | def eff[B](hs: HS)(es: ESI)(ce: A => ESO => M[B]): M[B] = this match { 86 | 87 | case Value(a) => ce(a)(es.asInstanceOf[ESO]) 88 | 89 | case EMap(effP, f) => effP.eff(hs)(es)(a => eso => ce(f(a))(eso)) 90 | 91 | case EFlatMap(effP, f, prf, merge) => 92 | val (hs1, hs2) = merge.resplit(hs) 93 | effP.eff(hs1)(prf.downI(es)) { a => eso => 94 | f(a).eff(hs2)(prf.out2in(es, eso)) { a => eso2 => 95 | ce(a)(prf.buildOut(eso, eso2)) 96 | } 97 | } 98 | 99 | case l@LiftP(effP, dropE, rebuildEO) => effP.eff(hs)(dropE.drop(es))(a => eso => ce(a)(rebuildEO.rebuild(es, eso))) 100 | 101 | case New(e, h, effP) => effP.eff(h :: hs)(e :: es)(a => eso => ce(a)(eso.tail)) 102 | 103 | case c: CallP[M, t, e, e1, ESI, eso, hs] => c.execEff(hs)(es)(a => eso => ce(a)(eso)) 104 | 105 | case lbl: Labelled[M, t, e, resi, eso, HS, ESO, lbl] => 106 | val esil = lbl.prf.unlabelI(es.asInstanceOf[MkEff[e, resi @@ lbl] :: HNil]) 107 | 108 | lbl.effM.eff(hs)(esil) { a => eso => 109 | val esol = lbl.prf.labelO(eso) 110 | ce(a)(esol.asInstanceOf[ESO]) 111 | } 112 | } 113 | 114 | def map[B](f: A => B): EffM[M, B, ESI, ESO, HS] = EMap[M, A, B, ESI, ESO, HS](this, f) 115 | 116 | def flatMap[B, ESI2 <: HList, ESO2 <: HList, ESI3 <: HList, ESO3 <: HList, HS2 <: HList, HS3 <: HList](f: A => EffM[M, B, ESI2, ESO2, HS2])( 117 | implicit 118 | flatMappable: FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI3, ESO3], 119 | merge: Merge.Aux[HS, HS2, HS3] 120 | ): EffM[M, B, ESI3, ESO3, HS3] = EFlatMap(this, f, flatMappable, merge) 121 | 122 | // def lift2[Super <: HList, SuperO <: HList]( 123 | // implicit dropE: DropE[ESI, Super], rebuildEO: RebuildEO[Super, ESO, SuperO] 124 | // ): EffM[M, A, Super, SuperO] = 125 | // LiftP[M, A, ESI, ESO, Super, SuperO](this, dropE, rebuildEO) 126 | 127 | def lift[Super <: HList]( 128 | implicit dropE: DropE[ESI, Super], rebuildEO: RebuildEO[Super, ESO, Super] 129 | ): EffM[M, A, Super, Super, HS] = 130 | LiftP[M, A, ESI, ESO, Super, Super, HS](this, dropE, rebuildEO) 131 | 132 | } 133 | 134 | 135 | object EffM { 136 | 137 | case class Value[M[_], ES <: HList, HS <: HList, A](a: A) extends EffM[M, A, ES, ES, HS] { 138 | type T = A 139 | } 140 | 141 | case class New[M[_], A, R, E <: Effect, ES <: HList, ESO <: HList, HS <: HList]( 142 | e: MkEff[E, R], handler: Handler[E, M], effP: EffM[M, A, MkEff[E, R] :: ES, MkEff[E, R] :: ESO, Handler[E, M] :: HS] 143 | ) extends EffM[M, A, ES, ESO, HS] {} 144 | 145 | case class LiftP[M[_], A, ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList, HS <: HList]( 146 | effP: EffM[M, A, ESI, ESO, HS] 147 | , dropE: DropE[ESI, ESI2] 148 | , rebuildEO: RebuildEO[ESI2, ESO, ESO2] 149 | ) extends EffM[M, A, ESI2, ESO2, HS] { 150 | type _Sub = ESI 151 | type _SubO = ESO 152 | } 153 | 154 | abstract class CallP[M[_], T, E <: Effect, E1 <: E, ES <: HList, ESO <: HList, HS <: HList]( 155 | val eff: E1 156 | ) extends EffM[M, T, ES, ESO, HS] { 157 | 158 | def prf: EffElem.Aux[E, eff.ResI, eff.ResO, ES, ESO] 159 | def hdprf: HandlerElem[M, E1, HS] 160 | 161 | def execEff[B](hs: HS)(es: ES)(ce: T => ESO => M[B]): M[B] = { 162 | val m: MkEff[E, eff.ResI] = prf.sel(es) 163 | val handler: Handler.Aux[E1, eff.T, eff.ResI, eff.ResO, M] = hdprf.sel(hs).asInstanceOf[Handler.Aux[E1, eff.T, eff.ResI, eff.ResO, M]] 164 | val res: eff.ResI = m.res 165 | handler.handle(eff)(res) { (a:eff.T) => (resO: eff.ResO) => 166 | val eso = prf.rep(es, MkEff[E, eff.ResO](resO)) 167 | ce(a.asInstanceOf[T])(eso) 168 | } 169 | } 170 | 171 | } 172 | 173 | abstract class Labelled[ 174 | M[_], A, E <: Effect, ResI, 175 | ESO <: HList, HS <: HList, 176 | ESOL <: HList, Lbl 177 | ]( 178 | val lbl: Lbl, val effM: EffM[M, A, MkEff[E, ResI] :: HNil, ESO, HS] 179 | ) extends EffM[M, A, MkEff[E, ResI @@ Lbl] :: HNil, ESOL, HS] { 180 | 181 | def prf: EffLabel.Aux[E, ResI, ESO, ESOL, Lbl] 182 | 183 | } 184 | 185 | case class EFlatMap[ 186 | M[_], A, B, 187 | ESI <: HList, ESO <: HList, 188 | ESI2 <: HList, ESO2 <: HList, 189 | ESI3 <: HList, ESO3 <: HList, 190 | HS <: HList, HS2 <: HList, HS3 <: HList 191 | ]( 192 | effP: EffM[M, A, ESI, ESO, HS] 193 | , f: A => EffM[M, B, ESI2, ESO2, HS2] 194 | , prf: FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI3, ESO3] 195 | , merge: Merge.Aux[HS, HS2, HS3] 196 | ) extends EffM[M, B, ESI3, ESO3, HS3] 197 | 198 | case class EMap[M[_], A, B, ES <: HList, ESO <: HList, HS <: HList]( 199 | effP: EffM[M, A, ES, ESO, HS], f: A => B 200 | ) extends EffM[M, B, ES, ESO, HS] 201 | 202 | def pure[M[_], ES <: HList, A](a: A): EffM[M, A, ES, ES, HNil] = Value(a) 203 | 204 | def call[M[_], E <: Effect, E1 <: E, ES <: HList, HS <: HList](_eff: E1)( 205 | implicit 206 | effElem: EffElem[E, _eff.ResI, _eff.ResO, ES], 207 | handlerElem: HandlerElem[M, E1, HS] 208 | ): EffM[M, _eff.T, ES, effElem.ESO, HS] = { 209 | new CallP[M, _eff.T, E, E1, ES, effElem.ESO, HS](_eff) { 210 | val prf = effElem.asInstanceOf[EffElem.Aux[E, eff.ResI, eff.ResO, ES, effElem.ESO]] 211 | 212 | val hdprf = handlerElem 213 | } 214 | } 215 | 216 | // def callM[E <: Effect, E1 <: E, ES <: HList, M[_]](_eff: E1)( 217 | // implicit 218 | // ctx: Ctx.Aux[M] 219 | // , effElem: EffElem[E, _eff.ResI, _eff.ResO, ES] 220 | // ): EffM[M, _eff.T, ES, effElem.ESO] = { 221 | // new CallP[M, _eff.T, E, E1, ES, effElem.ESO](_eff) { 222 | // val prf = effElem.asInstanceOf[EffElem.Aux[E, eff.ResI, eff.ResO, ES, effElem.ESO]] 223 | // val handler = handler0 224 | // } 225 | // } 226 | 227 | def lift[M[_], A, E <: Effect, ES <: HList, ESO <: HList, Super <: HList, SuperO <: HList, HS <: HList]( 228 | eff: EffM[M, A, ES, ESO, HS] 229 | )( 230 | implicit dropE: DropE[ES, Super], rebuildEO: RebuildEO[Super, ESO, SuperO] 231 | ): EffM[M, A, Super, SuperO, HS] = 232 | LiftP[M, A, ES, ESO, Super, SuperO, HS](eff, dropE, rebuildEO) 233 | 234 | def label[Lbl, M[_], A, E <: Effect, T, ResI, ESO <: HList, ESOL <: HList, HS <: HList] 235 | (lbl: Lbl)(eff: EffM[M, A, MkEff[E, ResI] :: HNil, ESO, HS])( 236 | implicit effLabel: EffLabel.Aux[E, ResI, ESO, ESOL, Lbl] 237 | ): EffM[M, A, MkEff[E, ResI @@ Lbl] :: HNil, ESOL, HS] = 238 | new Labelled[ 239 | M, A, E, ResI, 240 | ESO, HS, 241 | ESOL, Lbl 242 | ](lbl, eff) { 243 | val prf = effLabel 244 | } 245 | 246 | } 247 | 248 | 249 | trait EffElem[E <: Effect, Res, ResO, ES <: HList] { 250 | type ESO <: HList 251 | def sel(es: ES): MkEff[E, Res] 252 | def rep(es: ES, r: MkEff[E, ResO]): ESO 253 | } 254 | 255 | object EffElem { 256 | 257 | type Aux[E <: Effect, Res, ResO, ES <: HList, ESO0 <: HList] = EffElem[E, Res, ResO, ES] { type ESO = ESO0 } 258 | 259 | implicit def mkEffElem[E <: Effect, Res, ResO, ES <: HList, ESO0 <: HList] 260 | (implicit 261 | sel0: Lazy[Selector[ES, MkEff[E, Res]]] 262 | , upd0: Lazy[Rep.Aux[ES, MkEff[E, Res], MkEff[E, ResO], ESO0]] 263 | ): EffElem.Aux[E, Res, ResO, ES, ESO0] = 264 | new EffElem[E, Res, ResO, ES] { 265 | type ESO = ESO0 266 | def sel(es: ES): MkEff[E, Res] = sel0.value(es) 267 | def rep(es: ES, r: MkEff[E, ResO]): ESO0 = upd0.value(es, r) 268 | } 269 | 270 | } 271 | 272 | trait ESLabel[ES <: HList, Lbl] { 273 | type ESLbl <: HList 274 | 275 | def label(es: ES): ESLbl 276 | } 277 | 278 | object ESLabel { 279 | 280 | type Aux[ES <: HList, Lbl, ESLbl0 <: HList] = ESLabel[ES, Lbl] { type ESLbl = ESLbl0 } 281 | 282 | implicit def hnil[Lbl]: ESLabel.Aux[HNil, Lbl, HNil] = new ESLabel[HNil, Lbl] { 283 | type ESLbl = HNil 284 | 285 | def label(es: HNil): HNil = HNil 286 | } 287 | 288 | implicit def next[E <: Effect, Res, Lbl, ES <: HList, ES1 <: HList]( 289 | implicit rec: ESLabel.Aux[ES, Lbl, ES1] 290 | ): ESLabel.Aux[MkEff[E, Res] :: ES, Lbl, MkEff[E, Res @@ Lbl] :: ES1] = new ESLabel[MkEff[E, Res] :: ES, Lbl] { 291 | type ESLbl = MkEff[E, Res @@ Lbl] :: ES1 292 | 293 | def label(es: MkEff[E, Res] :: ES): MkEff[E, Res @@ Lbl] :: ES1 = { 294 | val MkEff(res) = es.head 295 | MkEff[E, Res @@ Lbl](res) :: rec.label(es.tail) 296 | } 297 | } 298 | } 299 | 300 | trait EffLabel[E <: Effect, Res, ESO <: HList, Lbl] { 301 | type ESOL <: HList 302 | 303 | def unlabelI(esil: MkEff[E, Res @@ Lbl] :: HNil): MkEff[E, Res] :: HNil 304 | def labelO(eso: ESO): ESOL 305 | } 306 | 307 | object EffLabel { 308 | type Aux[E <: Effect, Res, ESO <: HList, ESOL0 <: HList, Lbl] = 309 | EffLabel[E, Res, ESO, Lbl] { 310 | type ESOL = ESOL0 311 | } 312 | 313 | implicit def mkEffLabel[E <: Effect, Res, ESO <: HList, ESOL0 <: HList, Lbl]( 314 | implicit 315 | // sel: Selector[ESIL0, MkEff[E, Res @@ Lbl]], 316 | esLabel: ESLabel.Aux[ESO, Lbl, ESOL0] 317 | ): EffLabel.Aux[E, Res, ESO, ESOL0, Lbl] = new EffLabel[E, Res, ESO, Lbl] { 318 | type ESOL = ESOL0 319 | 320 | def unlabelI(esil: MkEff[E, Res @@ Lbl] :: HNil): MkEff[E, Res] :: HNil = { 321 | val mkEffResLbl = esil.head 322 | mkEffResLbl.asInstanceOf[MkEff[E, Res]] :: HNil 323 | } 324 | 325 | def labelO(eso: ESO): ESOL0 = esLabel.label(eso) 326 | } 327 | } 328 | 329 | trait HandlerElem[M[_], E <: Effect, HS <: HList] { 330 | def sel(hs: HS): Handler[E, M] 331 | } 332 | 333 | object HandlerElem { 334 | 335 | implicit def mkHandlerElem[M[_], E <: Effect, HS <: HList] 336 | (implicit 337 | sel0: Lazy[Selector[HS, Handler[E, M]]] 338 | ): HandlerElem[M, E, HS] = 339 | new HandlerElem[M, E, HS] { 340 | def sel(hs: HS): Handler[E, M] = sel0.value(hs) 341 | } 342 | } 343 | 344 | trait Rep[L <: HList, U, V] { 345 | type Out <: HList 346 | def apply(l: L, v: V): Out 347 | } 348 | 349 | object Rep { 350 | type Aux[L <: HList, U, V, Out0 <: HList] = Rep[L, U, V] { type Out = Out0 } 351 | implicit def rep[L <: HList, U, V, Out0 <: HList](implicit rep: Lazy[Replacer.Aux[L, U, V, (U, Out0)]]): Aux[L, U, V, Out0] = 352 | new Rep[L, U, V] { 353 | type Out = Out0 354 | def apply(l: L, v: V): Out = rep.value(l, v)._2 355 | } 356 | } 357 | 358 | 359 | trait FlatMappable[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList] { 360 | type OutESI <: HList 361 | type OutESO <: HList 362 | 363 | def downI(esi: OutESI): ESI 364 | def out2in(esi: OutESI, eso: ESO): ESI2 365 | def buildOut(eso: ESO, eso2: ESO2): OutESO 366 | } 367 | 368 | 369 | object FlatMappable extends FlatMappable2 { 370 | 371 | type Aux[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList, OutESI0 <: HList, OutESO0 <: HList] = 372 | FlatMappable[ESI, ESO, ESI2, ESO2] { type OutESI = OutESI0 ; type OutESO = OutESO0 } 373 | 374 | // ESO == ESI2 375 | implicit def iso[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList]( 376 | implicit isoI: IsoList[ESO, ESI2], 377 | // we need to transform internal output into external output 378 | isoO: IsoList[ESO2, ESO] 379 | ): FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI, ESO] = new FlatMappable[ESI, ESO, ESI2, ESO2] { 380 | type OutESI = ESI 381 | type OutESO = ESO 382 | 383 | def downI(esi: ESI): ESI = esi 384 | def out2in(esi: ESI, eso: ESO): ESI2 = isoI(eso) 385 | def buildOut(eso: ESO, eso2: ESO2): ESO = isoO(eso2) 386 | } 387 | 388 | // ESO >> ESI2 389 | implicit def ESOBiggerThanESI2[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList, ESO3 <: HList]( 390 | implicit drop: DropE[ESI2, ESO], 391 | rebuild: RebuildEO[ESO, ESO2, ESO3] 392 | ): FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI, ESO3] = new FlatMappable[ESI, ESO, ESI2, ESO2] { 393 | type OutESI = ESI 394 | type OutESO = ESO3 395 | 396 | def downI(esi: ESI): ESI = esi 397 | def out2in(esi: ESI, eso: ESO): ESI2 = drop.drop(eso) 398 | def buildOut(eso: ESO, eso2: ESO2): ESO3 = rebuild.rebuild(eso, eso2) 399 | } 400 | 401 | 402 | } 403 | 404 | trait FlatMappable2 extends FlatMappable3 { 405 | 406 | // ESI << ESI2, ESO << ESI2 407 | implicit def ESIAndESOSmallerThanESI2[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList]( 408 | implicit 409 | dropE: DropE[ESI, ESI2] 410 | , rebuildE: RebuildEO[ESI2, ESO, ESI2] 411 | , rebuildE2: RebuildEO[ESO, ESO2, ESO2] 412 | ): FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI2, ESO2] = new FlatMappable[ESI, ESO, ESI2, ESO2] { 413 | type OutESI = ESI2 414 | type OutESO = ESO2 415 | 416 | def downI(esi: ESI2): ESI = dropE.drop(esi) 417 | def out2in(esi: ESI2, eso: ESO): ESI2 = rebuildE.rebuild(esi, eso) 418 | def buildOut(eso: ESO, eso2: ESO2): ESO2 = rebuildE2.rebuild(eso, eso2) 419 | } 420 | 421 | } 422 | 423 | trait FlatMappable3 { 424 | 425 | // MkEff[S, A1] :: MkEff[U, B] :: MkEff[W, D] => MkEff[S, A2] => MkEff[S, A2] :: MkEff[U, B] :: MkEff[V, C] 426 | 427 | // ESO << ESI2 but different type 428 | implicit def ESOSmallerThanESI2Different[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList, ESII <: HList, ESIII <: HList]( 429 | implicit 430 | dropE: DropE[ESO, ESI2] 431 | , removeAll: RemoveAll.Aux[ESI2, ESO, (ESO, ESII)] 432 | , merge: Merge.Aux[ESI, ESII, ESIII] 433 | , dropEI: DropE[ESI, ESIII] 434 | , rebuildE2: RebuildEO[ESO, ESO2, ESO2] 435 | ): FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESIII, ESO2] = new FlatMappable[ESI, ESO, ESI2, ESO2] { 436 | type OutESI = ESIII 437 | type OutESO = ESO2 438 | 439 | def downI(esi: ESIII): ESI = dropEI.drop(esi) 440 | def out2in(esiii: ESIII, eso: ESO): ESI2 = { 441 | val (esi, esii) = merge.resplit(esiii) 442 | removeAll.reinsert((eso, esii)) 443 | } 444 | def buildOut(eso: ESO, eso2: ESO2): ESO2 = rebuildE2.rebuild(eso, eso2) 445 | } 446 | 447 | // ESI ++ ESI2 && ESO ++ ESO2 448 | implicit def ESIESOPrepend[ESI <: HList, ESO <: HList, ESI2 <: HList, ESO2 <: HList, ESI3 <: HList, ESO3 <: HList]( 449 | implicit prepI: Prepend.Aux[ESI, ESI2, ESI3], 450 | prepO: Prepend.Aux[ESO, ESO2, ESO3], 451 | dropI: DropE[ESI, ESI3], 452 | dropI2: DropE[ESI2, ESI3], 453 | rebuildEO2: RebuildEO[ESO, ESO2, ESO3] 454 | ): FlatMappable.Aux[ESI, ESO, ESI2, ESO2, ESI3, ESO3] = new FlatMappable[ESI, ESO, ESI2, ESO2] { 455 | type OutESI = ESI3 456 | type OutESO = ESO3 457 | 458 | def downI(esi: ESI3): ESI = dropI.drop(esi) 459 | def out2in(esi: ESI3, eso: ESO): ESI2 = dropI2.drop(esi) 460 | def buildOut(eso: ESO, eso2: ESO2): ESO3 = rebuildEO2.rebuild(eso, eso2) 461 | } 462 | } 463 | 464 | 465 | trait IsoList[HL1 <: HList, HL2 <: HList] { 466 | def apply(hl1: HL1): HL2 467 | } 468 | 469 | object IsoList { 470 | implicit def hnil = new IsoList[HNil, HNil] { 471 | def apply(hl1: HNil): HNil = HNil 472 | } 473 | 474 | implicit def head[H, HL1 <: HList, HL2 <: HList](implicit iso: IsoList[HL1, HL2]) = 475 | new IsoList[H :: HL1, H :: HL2] { 476 | def apply(hl1: H :: HL1): H :: HL2 = hl1.head :: iso(hl1.tail) 477 | } 478 | 479 | implicit def tail[H, HL1 <: HList, HL2 <: HList, HL3 <: HList]( 480 | implicit sel: Selector[HL2, H], 481 | remove: Remove.Aux[HL2, H, (H, HL3)], 482 | iso: IsoList[HL1, HL3] 483 | ) = new IsoList[H :: HL1, HL2] { 484 | def apply(hl1: H :: HL1): HL2 = remove.reinsert(hl1.head -> iso(hl1.tail)) 485 | } 486 | } 487 | 488 | 489 | /** 490 | * Type class supporting removal of an element from this `HList`. Available only if this `HList` contains an 491 | * element of type `E`. 492 | * 493 | * @author Stacy Curl 494 | */ 495 | // TEMPORARY UNTIL NEXT SHAPELESS VERSION 496 | @implicitNotFound("Implicit not found: shapeless.Ops.Remove[${L}, ${E}]. You requested to remove an element of type ${E}, but there is no unique candidate in the HList ${L}.") 497 | trait Remove[L <: HList, E] extends DepFn1[L] with Serializable { 498 | def reinsert(out: Out): L 499 | } 500 | 501 | trait LowPriorityRemove { 502 | type Aux[L <: HList, E, Out0] = Remove[L, E] { type Out = Out0 } 503 | 504 | implicit def recurse[H, T <: HList, E, OutT <: HList](implicit r : Aux[T, E, (E, OutT)]): Aux[H :: T, E, (E, H :: OutT)] = 505 | new Remove[H :: T, E] { 506 | type Out = (E, H :: OutT) 507 | def apply(l : H :: T): Out = { 508 | val (e, tail) = r(l.tail) 509 | (e, l.head :: tail) 510 | } 511 | 512 | def reinsert(out: (E, H :: OutT)): H :: T = out._2.head :: r.reinsert((out._1, out._2.tail)) 513 | } 514 | } 515 | 516 | object Remove extends LowPriorityRemove { 517 | def apply[L <: HList, E](implicit remove: Remove[L, E]): Aux[L, E, remove.Out] = remove 518 | 519 | implicit def remove[H, T <: HList]: Aux[H :: T, H, (H, T)] = 520 | new Remove[H :: T, H] { 521 | type Out = (H, T) 522 | def apply(l : H :: T): Out = (l.head, l.tail) 523 | 524 | def reinsert(out: (H, T)): H :: T = out._1 :: out._2 525 | } 526 | } 527 | 528 | /** 529 | * Type class supporting removal of a sublist from this `HList`. Available only if this `HList` contains a 530 | * sublist of type `SL`. 531 | * 532 | * The elements of `SL` do not have to be contiguous in this `HList`. 533 | * 534 | * @author Stacy Curl 535 | */ 536 | @implicitNotFound("Implicit not found: shapeless.Ops.RemoveAll[${L}, ${SL}]. You requested to remove elements of the types ${SL}, but not all were found in HList ${L}.") 537 | trait RemoveAll[L <: HList, SL <: HList] extends DepFn1[L] with Serializable { 538 | def reinsert(out: Out): L 539 | } 540 | 541 | object RemoveAll { 542 | def apply[L <: HList, SL <: HList](implicit remove: RemoveAll[L, SL]): Aux[L, SL, remove.Out] = remove 543 | 544 | type Aux[L <: HList, SL <: HList, Out0] = RemoveAll[L, SL] { type Out = Out0 } 545 | 546 | implicit def hlistRemoveAllNil[L <: HList]: Aux[L, HNil, (HNil, L)] = 547 | new RemoveAll[L, HNil] { 548 | type Out = (HNil, L) 549 | def apply(l : L): Out = (HNil, l) 550 | 551 | def reinsert(out: (HNil, L)): L = out._2 552 | } 553 | 554 | implicit def hlistRemoveAll[L <: HList, E, RemE <: HList, Rem <: HList, SLT <: HList] 555 | (implicit rt : Remove.Aux[L, E, (E, RemE)], st : Aux[RemE, SLT, (SLT, Rem)]): Aux[L, E :: SLT, (E :: SLT, Rem)] = 556 | new RemoveAll[L, E :: SLT] { 557 | type Out = (E :: SLT, Rem) 558 | def apply(l : L): Out = { 559 | val (e, rem) = rt(l) 560 | val (sl, left) = st(rem) 561 | (e :: sl, left) 562 | } 563 | 564 | def reinsert(out: (E :: SLT, Rem)): L = 565 | rt.reinsert((out._1.head, st.reinsert((out._1.tail, out._2)))) 566 | } 567 | } 568 | 569 | trait DropE[Small <: HList, Big <: HList] { 570 | def drop(e: Big): Small 571 | } 572 | 573 | object DropE { 574 | implicit object nil extends DropE[HNil, HNil] { 575 | def drop(e: HNil): HNil = HNil 576 | } 577 | 578 | implicit def keep[H, HL1 <: HList, HL2 <: HList](implicit de: DropE[HL1, HL2]) = new DropE[H :: HL1, H :: HL2] { 579 | def drop(e: H :: HL2): H :: HL1 = e.head :: de.drop(e.tail) 580 | } 581 | 582 | implicit def drop[HL1 <: HList, H, HL2 <: HList](implicit de: DropE[HL1, HL2]) = new DropE[HL1, H :: HL2] { 583 | def drop(e: H :: HL2): HL1 = de.drop(e.tail) 584 | } 585 | 586 | } 587 | 588 | 589 | 590 | trait RebuildEO[ESI <: HList, ESO <: HList, ESO2 <: HList] { 591 | def rebuild(esi: ESI, eso: ESO): ESO2 592 | } 593 | 594 | object RebuildEO extends RebuildEO2 { 595 | 596 | implicit object nil extends RebuildEO[HNil, HNil, HNil] { 597 | def rebuild(esi: HNil, eso: HNil): HNil = HNil 598 | } 599 | 600 | implicit def esoHNil[H, ESI <: HList, ESO <: HList]( 601 | implicit rb: RebuildEO[ESI, HNil, ESO] 602 | ) = new RebuildEO[H :: ESI, HNil, H :: ESO] { 603 | def rebuild(esi: H :: ESI, eso: HNil): H :: ESO = esi.head :: rb.rebuild(esi.tail, eso) 604 | } 605 | 606 | implicit def esiHNil[H, ESO <: HList] = new RebuildEO[HNil, ESO, ESO] { 607 | def rebuild(esi: HNil, eso: ESO): ESO = eso 608 | } 609 | 610 | 611 | implicit def sameHead[H, ESI <: HList, ESO <: HList, ESO2 <: HList]( 612 | implicit rb: RebuildEO[ESI, ESO, ESO2] 613 | ) = new RebuildEO[H :: ESI, H :: ESO, H :: ESO2] { 614 | def rebuild(esi: H :: ESI, eso: H :: ESO): H :: ESO2 = eso.head :: rb.rebuild(esi.tail, eso.tail) 615 | } 616 | 617 | implicit def esoHead[H, H2, ESI <: HList, ESO <: HList, ESO2 <: HList]( 618 | implicit rb: RebuildEO[ESI, ESO, ESO2] 619 | ) = new RebuildEO[H :: ESI, H2 :: ESO, H2 :: ESO2] { 620 | def rebuild(esi: H :: ESI, eso: H2 :: ESO): H2 :: ESO2 = eso.head :: rb.rebuild(esi.tail, eso.tail) 621 | } 622 | 623 | 624 | } 625 | 626 | trait RebuildEO2 { 627 | 628 | implicit def esiHead[H, H2, ESI <: HList, ESO <: HList, ESO2 <: HList]( 629 | implicit rb: RebuildEO[ESI, H2 :: ESO, ESO2] 630 | ) = new RebuildEO[H :: ESI, H2 :: ESO, H :: ESO2] { 631 | def rebuild(esi: H :: ESI, eso: H2 :: ESO): H :: ESO2 = esi.head :: rb.rebuild(esi.tail, eso) 632 | } 633 | 634 | } 635 | 636 | trait Merge[HL1 <: HList, HL2 <: HList] { 637 | type Out <: HList 638 | def merge(hl1: HL1, hl2: HL2): Out 639 | def resplit(out: Out): (HL1, HL2) 640 | } 641 | 642 | object Merge extends Merge2 { 643 | type Aux[HL1 <: HList, HL2 <: HList, Out0 <: HList] = Merge[HL1, HL2] { type Out = Out0 } 644 | 645 | // implicit val nil: Merge.Aux[HNil, HNil, HNil] = new Merge[HNil, HNil] { 646 | // type Out = HNil 647 | // def merge(hl1: HNil, hl2: HNil): HNil = HNil 648 | // } 649 | 650 | implicit def rightNil[HL2 <: HList]: Merge.Aux[HNil, HL2, HL2] = new Merge[HNil, HL2] { 651 | type Out = HL2 652 | def merge(hl1: HNil, hl2: HL2): HL2 = hl2 653 | 654 | def resplit(out: HL2): (HNil, HL2) = (HNil, out) 655 | } 656 | 657 | implicit def leftNil[HL1 <: HList]: Merge.Aux[HL1, HNil, HL1] = new Merge[HL1, HNil] { 658 | type Out = HL1 659 | def merge(hl1: HL1, hl2: HNil): HL1 = hl1 660 | 661 | def resplit(out: HL1): (HL1, HNil) = (out, HNil) 662 | } 663 | 664 | implicit def headInHL2[H1, HL1 <: HList, HL2 <: HList, HLO <: HList, HLO2 <: HList]( 665 | implicit rem: Remove.Aux[HL2, H1, (H1, HLO)], 666 | sub: Merge.Aux[HL1, HLO, HLO2] 667 | ): Merge.Aux[H1 :: HL1, HL2, H1 :: HLO2] = new Merge[H1 :: HL1, HL2] { 668 | type Out = H1 :: HLO2 669 | def merge(hl1: H1 :: HL1, hl2: HL2): H1 :: HLO2 = { 670 | val (h1, hlo) = rem(hl2) 671 | h1 :: sub.merge(hl1.tail, hlo) 672 | } 673 | 674 | def resplit(out: H1 :: HLO2): (H1 :: HL1, HL2) = { 675 | val (l1, l2) = sub.resplit(out.tail) 676 | (out.head :: l1, rem.reinsert(out.head, l2)) 677 | } 678 | } 679 | 680 | } 681 | 682 | trait Merge2 { 683 | implicit def headNotInHL2[H1, HL1 <: HList, HL2 <: HList, HLO <: HList]( 684 | implicit sub: Merge.Aux[HL1, HL2, HLO] 685 | ): Merge.Aux[H1 :: HL1, HL2, H1 :: HLO] = new Merge[H1 :: HL1, HL2] { 686 | type Out = H1 :: HLO 687 | def merge(hl1: H1 :: HL1, hl2: HL2): H1 :: HLO = { 688 | hl1.head :: sub.merge(hl1.tail, hl2) 689 | } 690 | 691 | def resplit(out: H1 :: HLO): (H1 :: HL1, HL2) = { 692 | val (l1, l2) = sub.resplit(out.tail) 693 | (out.head :: l1, l2) 694 | } 695 | } 696 | } 697 | 698 | trait Handlers[M[_], HS <: HList] { 699 | val handlers: HS 700 | } 701 | 702 | object Handlers { 703 | 704 | implicit def nil[M[_]]: Handlers[M, HNil] = new Handlers[M, HNil] { 705 | val handlers = HNil 706 | } 707 | 708 | implicit def head[M[_], E <: Effect, HS <: HList]( 709 | implicit 710 | handler: Handler[E, M] 711 | , next: Handlers[M, HS] 712 | ): Handlers[M, Handler[E, M] :: HS] = 713 | new Handlers[M, Handler[E, M] :: HS] { 714 | val handlers = handler :: next.handlers 715 | } 716 | 717 | } 718 | 719 | trait Ctx { 720 | type M[_] 721 | 722 | // def app: Applicative[M] 723 | } 724 | 725 | object Ctx { 726 | type Aux[M0[_]] = Ctx { type M[a] = M0[a] } 727 | 728 | // implicit def toApp(ctx: Ctx): Applicative[ctx.M] = ctx.app 729 | } 730 | 731 | trait Effectful[M0[_], ESI0 <: HList, ESO0 <: HList] { 732 | 733 | implicit val ctx = new Ctx { type M[a] = M0[a] } 734 | 735 | type ESI = ESI0 736 | type ESO = ESO0 737 | 738 | // type Eff[A] = EffM[M0, A, ESI0, ESO0] 739 | 740 | } 741 | 742 | object Effectful { 743 | def apply[M[_], ESI <: HList, ESO <: HList] = new Effectful[M, ESI, ESO] { } 744 | } 745 | 746 | -------------------------------------------------------------------------------- /src/main/scala/Effective.scala: -------------------------------------------------------------------------------- 1 | package effects 2 | 3 | import shapeless.HList 4 | 5 | trait Effective0[M0[_]] { 6 | 7 | implicit val ctx: Ctx.Aux[M0] = new Ctx { type M[a] = M0[a] } 8 | 9 | def apply[A, ESI <: HList, ESO <: HList, HS <: HList](f: Ctx.Aux[M0] => EffM[M0, A, ESI, ESO, HS]): EffM[M0, A, ESI, ESO, HS] = f(ctx) 10 | 11 | } 12 | 13 | 14 | trait Effective[M0[_], ES <: HList] { 15 | 16 | implicit val ctx: Ctx.Aux[M0] = new Ctx { type M[a] = M0[a] } 17 | 18 | def apply[A, ESI <: HList, ESO <: HList, HS <: HList](f: Ctx.Aux[M0] => EffM[M0, A, ESI, ESO, HS])( 19 | implicit iso: IsoList[ESI, ES] 20 | ): EffM[M0, A, ESI, ESO, HS] = f(ctx) 21 | 22 | } 23 | 24 | trait Effectives { 25 | def effective[M[_], ES <: HList]: Effective[M, ES] = new Effective[M, ES] {} 26 | 27 | def effective[M[_]]: Effective0[M] = new Effective0[M] {} 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/file/FileIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /*package effects 17 | package file 18 | 19 | import java.io.{File, BufferedReader, FileReader, FileWriter} 20 | 21 | import cats.data.Xor 22 | 23 | import shapeless._ 24 | 25 | sealed trait Mode 26 | case object Read extends Mode 27 | case object Write extends Mode 28 | 29 | sealed trait FileHandler[M <: Mode] 30 | case class FileRead(reader: BufferedReader) extends FileHandler[Read.type] 31 | case class FileWrite(writer: FileWriter) extends FileHandler[Write.type] 32 | 33 | sealed trait FileError 34 | case object CannotOpen extends FileError 35 | case object EOF extends FileError 36 | 37 | trait Moder[M <: Mode] { 38 | def mode: Mode 39 | 40 | def cast[O <: FileHandler[_]](o: FileHandler[M]): O = o.asInstanceOf[O] 41 | 42 | def castM[O <: FileHandler[_]](o: O): FileHandler[M] = o.asInstanceOf[FileHandler[M]] 43 | } 44 | 45 | object Moder { 46 | implicit object ReadMode extends Moder[Read.type] { val mode = Read } 47 | implicit object WriteMode extends Moder[Write.type] { val mode = Write } 48 | } 49 | 50 | sealed trait FileIO extends Effect 51 | 52 | case class Open[Mo <: Mode](fname: String)(implicit moder: Moder[Mo]) extends FileIO { 53 | type T = Boolean 54 | type ResI = Unit 55 | type ResO = FileStatus[Mo] 56 | 57 | def handle[M[_], X](res: Unit)(k: Boolean => Xor[FileError, FileHandler[Mo]] => M[X]): M[X] = { 58 | val fp = new File(fname) 59 | moder.mode match { 60 | case Read => if(fp.canRead()) { 61 | k(true)(Xor.Right(moder.castM(FileRead(new BufferedReader(new FileReader(fp)))))) 62 | } else { 63 | k(false)(Xor.Left(CannotOpen)) 64 | } 65 | case Write => if(fp.canWrite()) { 66 | k(true)(Xor.Right(moder.castM(FileWrite(new FileWriter(fp))))) 67 | } else { 68 | k(false)(Xor.Left(CannotOpen)) 69 | } 70 | } 71 | } 72 | } 73 | 74 | case class Close[Mo <: Mode](implicit moder: Moder[Mo]) extends FileIO { 75 | type T = Unit 76 | type ResI = FileStatus[Mo] 77 | type ResO = Unit 78 | 79 | def handle[M[_], X](res: FileStatus[Mo])(k: Unit => Unit => M[X]): M[X] = res match { 80 | case Xor.Left(e) => k()() 81 | case Xor.Right(o) => moder.mode match { 82 | case Read => k(moder.cast[FileRead](o).reader.close)() 83 | case Write => k(moder.cast[FileWrite](o).writer.close)() 84 | } 85 | } 86 | } 87 | 88 | case object ReadLine extends FileIO { 89 | type T = Option[String] 90 | type ResI = FileStatus[Read.type] 91 | type ResO = FileStatus[Read.type] 92 | 93 | def handle[M[_], X](res: FileStatus[Read.type])(k: Option[String] => FileStatus[Read.type] => M[X]): M[X] = 94 | res match { 95 | case Xor.Left(e) => k(None)(Xor.Left(e)) 96 | case Xor.Right(FileRead(reader)) => Option(reader.readLine()) match { 97 | case None => k(None)(Xor.Left(EOF)) 98 | case Some(s) => k(Some(s))(res) 99 | } 100 | } 101 | 102 | } 103 | 104 | case class WriteString(s: String) extends FileIO { 105 | type T = Unit 106 | type ResI = FileStatus[Write.type] 107 | type ResO = FileStatus[Write.type] 108 | 109 | def handle[M[_], X](res: FileStatus[Write.type])(k: Unit => FileStatus[Write.type] => M[X]): M[X] = 110 | res match { 111 | case Xor.Left(e) => k(None)(Xor.Left(e)) 112 | case Xor.Right(FileWrite(writer)) => k(writer.write(s))(res) 113 | } 114 | 115 | } 116 | 117 | case object IsEOF extends FileIO { 118 | type T = Boolean 119 | type ResI = FileStatus[Read.type] 120 | type ResO = FileStatus[Read.type] 121 | 122 | def handle[M[_], X](res: FileStatus[Read.type])(k: Boolean => FileStatus[Read.type] => M[X]): M[X] ={ 123 | res match { 124 | case Xor.Left(e) => k(true)(Xor.Left(e)) 125 | case Xor.Right(FileRead(reader)) => 126 | reader.mark(1) 127 | if(reader.read() == -1) { reader.reset(); k(true)(res) } 128 | else { reader.reset(); k(false)(res) } 129 | } 130 | } 131 | } 132 | 133 | 134 | object FileIO { 135 | 136 | def open0[M[_], Mo <: Mode](file: String)( 137 | implicit moder: Moder[Mo] 138 | ): EffM[M, Boolean, MkEff[FileIO, Unit] :: HNil, MkEff[FileIO, FileStatus[Mo]] :: HNil] = 139 | EffM.call[M, FileIO, MkEff[FileIO, Unit] :: HNil](Open[Mo](file)) 140 | 141 | def open[Mo <: Mode](file: String)( 142 | implicit ctx: Ctx, moder: Moder[Mo] 143 | ): EffM[ctx.M, Boolean, MkEff[FileIO, Unit] :: HNil, MkEff[FileIO, FileStatus[Mo]] :: HNil] = 144 | open0(file) 145 | 146 | def close0[M[_], Mo <: Mode]()( 147 | implicit moder: Moder[Mo] 148 | ): EffM[M, Unit, MkEff[FileIO, FileStatus[Mo]] :: HNil, MkEff[FileIO, Unit] :: HNil] = 149 | EffM.call[M, FileIO, MkEff[FileIO, FileStatus[Mo]] :: HNil](Close[Mo]) 150 | 151 | def close[Mo <: Mode]()( 152 | implicit ctx: Ctx, moder: Moder[Mo] 153 | ): EffM[ctx.M, Unit, MkEff[FileIO, FileStatus[Mo]] :: HNil, MkEff[FileIO, Unit] :: HNil] = 154 | EffM.call[ctx.M, FileIO, MkEff[FileIO, FileStatus[Mo]] :: HNil](Close[Mo]) 155 | 156 | def readLine0[M[_]]: EffM[M, Option[String], MkEff[FileIO, FileStatus[Read.type]] :: HNil, MkEff[FileIO, FileStatus[Read.type]] :: HNil] = 157 | EffM.call[M, FileIO, MkEff[FileIO, FileStatus[Read.type]] :: HNil](ReadLine) 158 | 159 | def readLine(implicit ctx: Ctx): EffM[ctx.M, Option[String], MkEff[FileIO, FileStatus[Read.type]] :: HNil, MkEff[FileIO, FileStatus[Read.type]] :: HNil] = 160 | readLine0 161 | 162 | def writeString0[M[_]](s: String): EffM[M, Unit, MkEff[FileIO, FileStatus[Write.type]] :: HNil, MkEff[FileIO, FileStatus[Write.type]] :: HNil] = 163 | EffM.call[M, FileIO, MkEff[FileIO, FileStatus[Write.type]] :: HNil](WriteString(s)) 164 | 165 | def writeString(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[FileIO, FileStatus[Write.type]] :: HNil, MkEff[FileIO, FileStatus[Write.type]] :: HNil] = 166 | writeString0(s) 167 | 168 | def writeLine0[M[_]](s: String): EffM[M, Unit, MkEff[FileIO, FileStatus[Write.type]] :: HNil, MkEff[FileIO, FileStatus[Write.type]] :: HNil] = 169 | writeString0(s + "\n") 170 | 171 | def writeLine(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[FileIO, FileStatus[Write.type]] :: HNil, MkEff[FileIO, FileStatus[Write.type]] :: HNil] = 172 | writeLine0(s) 173 | 174 | def isEof0[M[_]]: EffM[M, Boolean, MkEff[FileIO, FileStatus[Read.type]] :: HNil, MkEff[FileIO, FileStatus[Read.type]] :: HNil] = 175 | EffM.call[M, FileIO, MkEff[FileIO, FileStatus[Read.type]] :: HNil](IsEOF) 176 | 177 | def isEof(implicit ctx: Ctx): EffM[ctx.M, Boolean, MkEff[FileIO, FileStatus[Read.type]] :: HNil, MkEff[FileIO, FileStatus[Read.type]] :: HNil] = 178 | isEof0 179 | 180 | trait Labelled[L] { 181 | def open0[M[_], Mo <: Mode](file: String)( 182 | implicit moder: Moder[Mo] 183 | ): EffM[M, Boolean, MkEff[FileIO@@L, Unit] :: HNil, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil] = 184 | EffM.call[M, FileIO@@L, MkEff[FileIO@@L, Unit] :: HNil](Open[Mo](file)) 185 | 186 | def open[Mo <: Mode](file: String)( 187 | implicit ctx: Ctx, moder: Moder[Mo] 188 | ): EffM[ctx.M, Boolean, MkEff[FileIO@@L, Unit] :: HNil, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil] = 189 | open0(file) 190 | 191 | def close0[M[_], Mo <: Mode]()( 192 | implicit moder: Moder[Mo] 193 | ): EffM[M, Unit, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil, MkEff[FileIO@@L, Unit] :: HNil] = 194 | EffM.call[M, FileIO@@L, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil](Close[Mo]) 195 | 196 | def close[Mo <: Mode]()( 197 | implicit ctx: Ctx, moder: Moder[Mo] 198 | ): EffM[ctx.M, Unit, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil, MkEff[FileIO@@L, Unit] :: HNil] = 199 | EffM.call[ctx.M, FileIO@@L, MkEff[FileIO@@L, FileStatus[Mo]] :: HNil](Close[Mo]) 200 | 201 | def readLine0[M[_]]: EffM[M, Option[String], MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil] = 202 | EffM.call[M, FileIO@@L, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil](ReadLine) 203 | 204 | def readLine(implicit ctx: Ctx): EffM[ctx.M, Option[String], MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil] = 205 | readLine0 206 | 207 | def writeString0[M[_]](s: String): EffM[M, Unit, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil] = 208 | EffM.call[M, FileIO@@L, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil](WriteString(s)) 209 | 210 | def writeString(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil] = 211 | writeString0(s) 212 | 213 | def writeLine0[M[_]](s: String): EffM[M, Unit, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil] = 214 | writeString0(s + "\n") 215 | 216 | def writeLine(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Write.type]] :: HNil] = 217 | writeLine0(s) 218 | 219 | def isEof0[M[_]]: EffM[M, Boolean, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil] = 220 | EffM.call[M, FileIO@@L, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil](IsEOF) 221 | 222 | def isEof(implicit ctx: Ctx): EffM[ctx.M, Boolean, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil, MkEff[FileIO@@L, FileStatus[Read.type]] :: HNil] = 223 | isEof0 224 | } 225 | 226 | def apply[L] = new Labelled[L] {} 227 | 228 | 229 | }*/ 230 | 231 | -------------------------------------------------------------------------------- /src/main/scala/file/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /*package effects 17 | 18 | import cats.data.Xor 19 | 20 | 21 | package object file { 22 | 23 | type FileStatus[Mo <: Mode] = Xor[FileError, FileHandler[Mo]] 24 | 25 | } 26 | */ 27 | -------------------------------------------------------------------------------- /src/main/scala/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import shapeless._ 17 | 18 | import scala.language.experimental.macros 19 | 20 | 21 | package object effects extends Effectives { 22 | 23 | object tag { 24 | def apply[U] = new Tagger[U] 25 | } 26 | 27 | type Tagged[U] = shapeless.tag.Tagged[U] 28 | type @@[+T, U] = shapeless.tag.@@[T, U] 29 | 30 | class Tagger[U] { 31 | def apply[T](t : T) : T @@ U = t.asInstanceOf[T @@ U] 32 | } 33 | 34 | implicit def toTag[T, U](t: T): T @@ U = t.asInstanceOf[T @@ U] 35 | 36 | type -:[E <: Effect, R] = MkEff[E, R] 37 | type @:[I, L] = I @@ L 38 | // type <>[E <: Effect, T] = MkEff[E, T] 39 | 40 | implicit class labellize[M[_], A, E <: Effect, ResI, ESO <: HList, HS <: HList]( 41 | val eff: EffM[M, A, MkEff[E, ResI] :: HNil, ESO, HS] 42 | ) extends AnyVal { 43 | def -:[Lbl, ESOL <: HList](lbl: Lbl)(implicit effLabel: EffLabel.Aux[E, ResI, ESO, ESOL, Lbl]) = EffM.label(lbl)(eff) 44 | } 45 | 46 | // implicit def liftEff[M[_], A, ESI <: HList, ESO <: HList, Super <: HList, SuperO <: HList]( 47 | // eff: EffM[M, A, ESI, ESO] 48 | // )( 49 | // implicit dropE: DropE[ESI, Super], rebuildEO: RebuildEO[Super, ESO, SuperO] 50 | // ): EffM[M, A, Super, SuperO] = eff.lift2[Super, SuperO] 51 | 52 | } -------------------------------------------------------------------------------- /src/main/scala/state/State.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package effects 17 | package state 18 | 19 | import shapeless.{HList, ::, HNil} 20 | 21 | 22 | sealed trait State extends Effect 23 | 24 | case class Get[A]() extends State { 25 | type T = A 26 | type ResI = A 27 | type ResO = A 28 | } 29 | 30 | case class Put[A, B](b: B) extends State { 31 | type T = Unit 32 | type ResI = A 33 | type ResO = B 34 | } 35 | 36 | object State extends EffectBuilder[State] { 37 | 38 | // GET 39 | def get0[M[_], A](): EffM[M, A, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Get[A], M] :: HNil] = 40 | EffM.call[M, State, Get[A], MkEff[State, A] :: HNil, Handler[Get[A], M] :: HNil](Get[A]()) 41 | 42 | def get[A](implicit ctx: Ctx): EffM[ctx.M, A, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Get[A], ctx.M] :: HNil] = 43 | get0[ctx.M, A] 44 | 45 | // PUT 46 | def put0[M[_], A](a: A): EffM[M, Unit, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Put[A, A], M] :: HNil] = 47 | EffM.call[M, State, Put[A, A], MkEff[State, A] :: HNil, Handler[Put[A, A], M] :: HNil](Put[A, A](a)) 48 | 49 | def put[A](a: A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Put[A, A], ctx.M] :: HNil] = 50 | put0[ctx.M, A](a) 51 | 52 | // UPDATE 53 | def update0[M[_], A](f: A => A): EffM[M, Unit, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Get[A], M] :: Handler[Put[A, A], M] :: HNil] = 54 | for { 55 | v <- get0[M, A] 56 | u <- put0(f(v)) 57 | } yield u 58 | 59 | 60 | def update[A](f: A => A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State, A] :: HNil, MkEff[State, A] :: HNil, Handler[Get[A], ctx.M] :: Handler[Put[A, A], ctx.M] :: HNil] = 61 | update0[ctx.M, A](f) 62 | 63 | 64 | // PUTM 65 | def putM0[M[_], A, B](b: B): EffM[M, Unit, MkEff[State, A] :: HNil, MkEff[State, B] :: HNil, Handler[Put[A, B], M] :: HNil] = 66 | EffM.call[M, State, Put[A, B], MkEff[State, A] :: HNil, Handler[Put[A, B], M] :: HNil](Put[A, B](b)) 67 | 68 | def putM[A, B](b: B)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State, A] :: HNil, MkEff[State, B] :: HNil, Handler[Put[A, B], ctx.M] :: HNil] = 69 | putM0[ctx.M, A, B](b) 70 | 71 | // UPDATEM 72 | def updateM0[M[_], A, B](f: A => B): EffM[M, Unit, MkEff[State, A] :: HNil, MkEff[State, B] :: HNil, Handler[Get[A], M] :: Handler[Put[A, B], M] :: HNil] = 73 | for { 74 | v <- get0[M, A] 75 | u <- putM0[M, A, B](f(v)) 76 | } yield u 77 | 78 | def updateM[A, B](f: A => B)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State, A] :: HNil, MkEff[State, B] :: HNil, Handler[Get[A], ctx.M] :: Handler[Put[A, B], ctx.M] :: HNil] = 79 | updateM0[ctx.M, A, B](f) 80 | 81 | // def apply[L] = new Labelled[L] {} 82 | 83 | // trait Labelled[L] { 84 | // // GET 85 | // def get0[M[_], A](): EffM[M, A, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Get[A]@@L, M] :: HNil] = 86 | // EffM.call[M, State@@L, Get[A]@@L, MkEff[State@@L, A] :: HNil, Handler[Get[A]@@L, M] :: HNil](Get[A]()) 87 | 88 | // def get[A](implicit ctx: Ctx): EffM[ctx.M, A, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Get[A]@@L, ctx.M] :: HNil] = 89 | // get0[ctx.M, A] 90 | 91 | // // PUT 92 | // def put0[M[_], A](a: A): EffM[M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Put[A, A]@@L, M] :: HNil] = 93 | // EffM.call[M, State@@L, Put[A, A]@@L, MkEff[State@@L, A] :: HNil, Handler[Put[A, A]@@L, M] :: HNil](Put[A, A](a)) 94 | 95 | // def put[A](a: A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Put[A, A]@@L, ctx.M] :: HNil] = 96 | // put0[ctx.M, A](a) 97 | 98 | // // UPDATE 99 | // def update0[M[_], A](f: A => A): EffM[M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Get[A]@@L, M] :: Handler[Put[A, A]@@L, M] :: HNil] = 100 | // for { 101 | // v <- get0[M, A] 102 | // u <- put0(f(v)) 103 | // } yield u 104 | 105 | 106 | // def update[A](f: A => A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, A] :: HNil, Handler[Get[A]@@L, ctx.M] :: Handler[Put[A, A]@@L, ctx.M] :: HNil] = 107 | // update0[ctx.M, A](f) 108 | 109 | 110 | // // PUTM 111 | // def putM0[M[_], A, B](b: B): EffM[M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, B] :: HNil, Handler[Put[A, B]@@L, M] :: HNil] = 112 | // EffM.call[M, State@@L, Put[A, B]@@L, MkEff[State@@L, A] :: HNil, Handler[Put[A, B]@@L, M] :: HNil](Put[A, B](b)) 113 | 114 | // def putM[A, B](b: B)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, B] :: HNil, Handler[Put[A, B]@@L, ctx.M] :: HNil] = 115 | // putM0[ctx.M, A, B](b) 116 | 117 | // // UPDATEM 118 | // def updateM0[M[_], A, B](f: A => B): EffM[M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, B] :: HNil, Handler[Get[A]@@L, M] :: Handler[Put[A, B]@@L, M] :: HNil] = 119 | // for { 120 | // v <- get0[M, A] 121 | // u <- putM0[M, A, B](f(v)) 122 | // } yield u 123 | 124 | // def updateM[A, B](f: A => B)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[State@@L, A] :: HNil, MkEff[State@@L, B] :: HNil, Handler[Get[A]@@L, ctx.M] :: Handler[Put[A, B]@@L, ctx.M] :: HNil] = 125 | // updateM0[ctx.M, A, B](f) 126 | 127 | // } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/scala/stdio/StdIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Miles Sabin & Pascal Voitot 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package effects 18 | package stdio 19 | 20 | import shapeless._ 21 | 22 | 23 | sealed trait StdIO extends Effect 24 | 25 | case class PutStr(s: String) extends StdIO { 26 | type T = Unit 27 | type ResI = Unit 28 | type ResO = Unit 29 | 30 | // def handle[M[_], X](res: Unit)(k: Unit => Unit => M[X]): M[X] = k(print(s))() 31 | } 32 | 33 | case class PutChar(c: Char) extends StdIO { 34 | type T = Unit 35 | type ResI = Unit 36 | type ResO = Unit 37 | 38 | // def handle[M[_], X](res: Unit)(k: Unit => Unit => M[X]): M[X] = k(print(c))() 39 | } 40 | 41 | case object GetStr extends StdIO { 42 | type T = String 43 | type ResI = Unit 44 | type ResO = Unit 45 | 46 | // def handle[M[_], X](res: Unit)(k: String => Unit => M[X]): M[X] = k(scala.io.StdIn.readLine())() 47 | } 48 | 49 | case object GetChar extends StdIO { 50 | type T = Char 51 | type ResI = Unit 52 | type ResO = Unit 53 | 54 | // def handle[M[_], X](res: Unit)(k: Char => Unit => M[X]): M[X] = k(scala.io.StdIn.readChar())() 55 | 56 | } 57 | 58 | object StdIO extends EffectBuilder[StdIO] { 59 | 60 | // PUTSTR 61 | def putStr0[M[_]](s: String): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil] = 62 | EffM.call[M, StdIO, PutStr, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil](PutStr(s)) 63 | 64 | def putStr(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, ctx.M] :: HNil] = 65 | putStr0(s) 66 | 67 | def putStrLn0[M[_]](s: String): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil] = 68 | putStr0(s + "\n") 69 | 70 | def putStrLn(s: String)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, ctx.M] :: HNil] = 71 | putStrLn0(s) 72 | 73 | // PUTCHAR 74 | def putChar0[M[_]](c: Char): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutChar, M] :: HNil] = 75 | EffM.call[M, StdIO, PutChar, MkEff[StdIO, Unit] :: HNil, Handler[PutChar, M] :: HNil](PutChar(c)) 76 | 77 | def putChar(c: Char)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutChar, ctx.M] :: HNil] = 78 | putChar(c) 79 | 80 | def putCharLn0[M[_]](c: Char): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil] = 81 | putStr0(c + "\n") 82 | 83 | def putCharLn(c: Char)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, ctx.M] :: HNil] = 84 | putCharLn0(c) 85 | 86 | // GETSTR 87 | def getStr0[M[_]](): EffM[M, String, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[GetStr.type, M] :: HNil] = 88 | EffM.call[M, StdIO, GetStr.type, MkEff[StdIO, Unit] :: HNil, Handler[GetStr.type, M] :: HNil](GetStr) 89 | 90 | def getStr()(implicit ctx: Ctx): EffM[ctx.M, String, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[GetStr.type, ctx.M] :: HNil] = 91 | getStr0() 92 | 93 | // GETCHAR 94 | def getChar0[M[_]](): EffM[M, Char, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[GetChar.type, M] :: HNil] = 95 | EffM.call[M, StdIO, GetChar.type, MkEff[StdIO, Unit] :: HNil, Handler[GetChar.type, M] :: HNil](GetChar) 96 | 97 | def getChar()(implicit ctx: Ctx): EffM[ctx.M, Char, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[GetChar.type, ctx.M] :: HNil] = 98 | getChar0() 99 | 100 | // PRINT 101 | def print0[M[_], A](a: A): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil] = 102 | putStr0(a.toString) 103 | 104 | def print[A](a: A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, ctx.M] :: HNil] = 105 | print0(a) 106 | 107 | def println0[M[_], A](a: A): EffM[M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, M] :: HNil] = 108 | putStrLn0(a.toString) 109 | 110 | def println[A](a: A)(implicit ctx: Ctx): EffM[ctx.M, Unit, MkEff[StdIO, Unit] :: HNil, MkEff[StdIO, Unit] :: HNil, Handler[PutStr, ctx.M] :: HNil] = 111 | println0(a) 112 | 113 | } -------------------------------------------------------------------------------- /src/test/scala/EffSpec.scala: -------------------------------------------------------------------------------- 1 | package effects 2 | 3 | 4 | import org.scalatest.concurrent.{AsyncAssertions, PatienceConfiguration, ScalaFutures} 5 | import org.scalatest.{Assertions, BeforeAndAfterAll, FeatureSpec, FlatSpec, Matchers, Suite} 6 | import org.scalatest.concurrent.PatienceConfiguration.Timeout 7 | import org.scalatest.time.{Millis, Seconds, Span => TSpan} 8 | 9 | import cats.data.Xor 10 | 11 | import shapeless._ 12 | import ops.hlist._ 13 | 14 | // import file._ 15 | import state._ 16 | import stdio._ 17 | 18 | object Implicits { 19 | import scala.concurrent.Future 20 | 21 | implicit def handlerGet[A]: Handler.Aux[Get[A], A, A, A, Future] = new Handler[Get[A], Future] { 22 | type T = A 23 | type ResI = A 24 | type ResO = A 25 | 26 | def handle[X](e: Get[A])(a: ResI)(k: T => ResO => Future[X]): Future[X] = k(a)(a) 27 | } 28 | 29 | implicit def handlerPut[A, B]: Handler.Aux[Put[A, B], Unit, A, B, Future] = new Handler[Put[A, B], Future] { 30 | type T = Unit 31 | type ResI = A 32 | type ResO = B 33 | 34 | def handle[X](e: Put[A, B])(res: A)(k: Unit => B => Future[X]): Future[X] = k(())(e.b) 35 | } 36 | 37 | implicit val handlerPutStr: Handler.Aux[PutStr, Unit, Unit, Unit, Future] = new Handler[PutStr, Future] { 38 | type T = Unit 39 | type ResI = Unit 40 | type ResO = Unit 41 | 42 | def handle[X](e: PutStr)(a: Unit)(k: Unit => Unit => Future[X]): Future[X] = k(print(e.s))() 43 | } 44 | 45 | implicit val handlerPutChar: Handler.Aux[PutChar, Unit, Unit, Unit, Future] = new Handler[PutChar, Future] { 46 | type T = Unit 47 | type ResI = Unit 48 | type ResO = Unit 49 | 50 | def handle[X](e: PutChar)(a: Unit)(k: Unit => Unit => Future[X]): Future[X] = k(print(e.c))() 51 | } 52 | 53 | implicit val handlerGetStr: Handler.Aux[GetStr.type, String, Unit, Unit, Future] = new Handler[GetStr.type, Future] { 54 | type T = String 55 | type ResI = Unit 56 | type ResO = Unit 57 | 58 | def handle[X](e: GetStr.type)(a: Unit)(k: String => Unit => Future[X]): Future[X] = k(scala.io.StdIn.readLine())() 59 | } 60 | 61 | implicit val handlerGetChar: Handler.Aux[GetChar.type, Char, Unit, Unit, Future] = new Handler[GetChar.type, Future] { 62 | type T = Char 63 | type ResI = Unit 64 | type ResO = Unit 65 | 66 | def handle[X](e: GetChar.type)(a: Unit)(k: Char => Unit => Future[X]): Future[X] = k(scala.io.StdIn.readChar())() 67 | } 68 | } 69 | 70 | class EffSpec extends FlatSpec with Matchers with ScalaFutures { 71 | 72 | implicit val defaultPatience = 73 | PatienceConfig(timeout = TSpan(300, Seconds), interval = TSpan(5, Millis)) 74 | 75 | sealed trait Label 76 | case object Foo extends Label 77 | case object Bar extends Label 78 | // type Foo = Foo.type 79 | // type Bar = Bar.type 80 | 81 | "Eff" should "simplest State" in { 82 | 83 | // Define our program with Effects in an abstract context of execution M[_] (very simple for now as I need to port the handler code to other effects) 84 | // Please note that M doesn't require anything here 85 | def eff[M[_]] = for { 86 | k <- State.get0[M, Int] 87 | _ <- State.put0[M, Int](k + 2) 88 | l <- State.get0[M, Int] 89 | } yield (l) 90 | 91 | // Define our Handlers for a given context of execution (dummy Future here) 92 | import scala.concurrent.Future 93 | 94 | implicit def handlerGet[A]: Handler.Aux[Get[A], A, A, A, Future] = new Handler[Get[A], Future] { 95 | type T = A 96 | type ResI = A 97 | type ResO = A 98 | 99 | def handle[X](e: Get[A])(a: ResI)(k: T => ResO => Future[X]): Future[X] = k(a)(a) 100 | } 101 | 102 | implicit def handlerPut[A, B]: Handler.Aux[Put[A, B], Unit, A, B, Future] = new Handler[Put[A, B], Future] { 103 | type T = Unit 104 | type ResI = A 105 | type ResO = B 106 | 107 | def handle[X](e: Put[A, B])(res: A)(k: Unit => B => Future[X]): Future[X] = k(())(e.b) 108 | } 109 | 110 | // Execute program 111 | // On execution site, M[_] just requires to be an Applicative to run it 112 | // (and in reality a alleycats Pure would be enough, we don't need the Functor) 113 | import cats.std.future._ 114 | import scala.concurrent.ExecutionContext.Implicits.global 115 | 116 | // Run the program 117 | // if anything is missing (the State initial resource, the Get/Put handlers for Future), 118 | // it won't compile 119 | val r = eff.run(MkEff[State, Int](3) :: HNil).futureValue 120 | 121 | r should equal (5) 122 | } 123 | 124 | import scala.concurrent.Future 125 | import cats.std.future._ 126 | import scala.concurrent.ExecutionContext.Implicits.global 127 | 128 | it should "label" in { 129 | import Implicits._ 130 | 131 | def eff[M[_]] = effective[M]{ implicit ctx => 132 | for { 133 | k <- Foo-:State.get[Int] 134 | k2 <- Bar-:State.get[Int] 135 | _ <- Bar-:State.put[Int](k + k2) 136 | k3 <- Bar-:State.get[Int] 137 | } yield (k3) 138 | } 139 | 140 | val r = eff[Future].run(Foo-:State.Res(3) :: Bar-:State.Res(2) :: HNil).futureValue 141 | println("r:"+r) 142 | r should equal (5) 143 | } 144 | 145 | it should "mix State & StdIO" in { 146 | import Implicits._ 147 | 148 | def eff[M[_]] = effective[M]{ implicit ctx => 149 | for { 150 | _ <- StdIO.putStrLn("Enter a name:") 151 | // n <- StdIO.getStr 152 | n = "toto" 153 | k <- Foo-:State.get[Int] 154 | k2 <- Bar-:State.get[Int] 155 | _ <- Bar-:State.put[Int](n.length + k + k2) 156 | k3 <- Bar-:State.get[Int] 157 | } yield (n -> k3) 158 | } 159 | 160 | val (name, r) = eff[Future].run(Foo-:State.Res(3) :: Bar-:State.Res(2) :: StdIO.Res() :: HNil).futureValue 161 | // r should equal (5 + name.length) 162 | 163 | } 164 | 165 | it should "More complex State+Label+ full effective" in { 166 | import Implicits._ 167 | 168 | val eff = effective[Future, (State-:Int@:Foo.type) :: (State-:String@:Bar.type) :: (StdIO-:Unit) :: HNil]{ implicit ctx => 169 | for { 170 | k0 <- Foo-:State.get[Int] 171 | _ <- Bar-:State.put("works") 172 | _ <- Foo-:State.update((i:Int) => i + 5) 173 | k <- Foo-:State.get[Int] 174 | _ <- StdIO.putStrLn(s"tmp state:$k") 175 | _ <- Bar-:State.update((s:String) => s + s"_$k") 176 | s <- Bar-:State.get[String] 177 | _ <- Foo-:State.updateM[Int, String]((k:Int) => k0.toString + s"_$s") 178 | r <- Foo-:State.get[String] 179 | _ <- StdIO.putStrLn(s"final state:$r") 180 | } yield (r) 181 | } 182 | 183 | 184 | val r = eff.run(Foo-:State.Res(3) :: Bar-:State.Res("") :: StdIO.Res() :: HNil).futureValue 185 | 186 | println("Res:"+r) 187 | r should equal ("3_works_8") 188 | } 189 | 190 | it should "More complex State+Label+ simple effective" in { 191 | import Implicits._ 192 | 193 | val eff = effective[Future]{ implicit ctx => 194 | for { 195 | k0 <- Foo-:State.get[Int] 196 | _ <- Bar-:State.put("works") 197 | _ <- Foo-:State.update((i:Int) => i + 5) 198 | k <- Foo-:State.get[Int] 199 | _ <- StdIO.putStrLn(s"tmp state:$k") 200 | _ <- Bar-:State.update((s:String) => s + s"_$k") 201 | s <- Bar-:State.get[String] 202 | _ <- Foo-:State.updateM[Int, String]((k:Int) => k0.toString + s"_$s") 203 | r <- Foo-:State.get[String] 204 | _ <- StdIO.putStrLn(s"final state:$r") 205 | } yield (r) 206 | } 207 | 208 | 209 | val r = eff.run(Foo-:State.Res(3) :: Bar-:State.Res("") :: StdIO.Res() :: HNil).futureValue 210 | 211 | println("Res:"+r) 212 | r should equal ("3_works_8") 213 | } 214 | 215 | 216 | /* 217 | "Eff" should "flatMap isoList" in { 218 | val eff = State[Foo].put0[Future, Int](3).lift[(State@@Foo<>Int) :: (State@@Bar<>String) :: HNil].flatMap { _ => 219 | State[Bar].put0[Future, String]("works").lift[(State@@Bar<>String) :: (State@@Foo<>Int) :: HNil] 220 | } 221 | 222 | val r = eff.run(MkEff[State@@Bar, String]("") :: MkEff[State@@Foo, Int](0) :: HNil).futureValue 223 | 224 | r should equal (()) 225 | } 226 | 227 | it should "flatMap ESOBiggerThanESI2" in { 228 | val eff = State[Foo].put0[Future, Int](3).lift[(State@@Foo<>Int) :: (State@@Bar<>String) :: HNil].flatMap { _ => 229 | State[Bar].put0[Future, String]("works") 230 | } 231 | 232 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 233 | 234 | r should equal (()) 235 | } 236 | 237 | it should "flatMap ESOSmallerThanESI2" in { 238 | val eff = State[Foo].put0[Future, Int](3).flatMap { _ => 239 | State[Bar].put0[Future, String]("works").lift[(State@@Bar<>String) :: (State@@Foo<>Int) :: HNil] 240 | } 241 | 242 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 243 | 244 | r should equal (()) 245 | } 246 | 247 | it should "flatMap ESIESOPrepend" in { 248 | val eff = State[Foo].put0[Future, Int](3).flatMap { _ => 249 | State[Bar].put0[Future, String]("works") 250 | } 251 | 252 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 253 | 254 | r should equal (()) 255 | } 256 | 257 | it should "State without labels update" in { 258 | 259 | val eff = 260 | for { 261 | _ <- State[Foo].update0[Future, Int](i => i + 3) 262 | k <- State[Foo].get0[Future, Int] 263 | } yield (k) 264 | 265 | val r = eff.run(MkEff[State@@Foo, Int](0) :: HNil).futureValue 266 | 267 | println("Res:"+r) 268 | r should equal (3) 269 | } 270 | 271 | it should "State without labels" in { 272 | 273 | val eff = 274 | for { 275 | _ <- State[Foo].put0[Future, Int](3) 276 | _ <- State[Bar].put0[Future, String]("works") 277 | k <- State[Foo].get0[Future, Int] 278 | k2 <- State[Bar].get0[Future, String] 279 | _ <- State[Foo].putM0[Future, Int, String](k.toString + k2) 280 | k3 <- State[Foo].get0[Future, String] 281 | } yield (k3) 282 | 283 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 284 | 285 | println("Res:"+r) 286 | r should equal ("3works") 287 | } 288 | 289 | it should "State with 0 functions" in { 290 | 291 | val eff = 292 | for { 293 | _ <- State[Foo].put0[Future, Int](3) 294 | _ <- State[Bar].put0[Future, String]("works") 295 | k <- State[Foo].get0[Future, Int] 296 | k2 <- State[Bar].get0[Future, String] 297 | _ <- State[Foo].putM0[Future, Int, String](k.toString + k2) 298 | k3 <- State[Foo].get0[Future, String] 299 | } yield (k3) 300 | 301 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 302 | 303 | println("Res:"+r) 304 | r should equal ("3works") 305 | } 306 | 307 | it should "State effective" in { 308 | 309 | val eff = effective[Future, (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil]{ implicit ctx => 310 | for { 311 | k0 <- State[Foo].get[Int] 312 | _ <- State[Bar].put("works") 313 | _ <- State[Foo].update((i:Int) => i + 5) 314 | k <- State[Foo].get[Int] 315 | _ <- State[Bar].update((s:String) => s + s"_$k") 316 | s <- State[Bar].get[String] 317 | _ <- State[Foo].updateM[Int, String]((k:Int) => k0.toString + s"_$s") 318 | r <- State[Foo].get[String] 319 | } yield (r) 320 | } 321 | 322 | 323 | val r = eff.run(MkEff[State@@Foo, Int](3) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 324 | 325 | println("Res:"+r) 326 | r should equal ("3_works_8") 327 | } 328 | 329 | it should "State effective0" in { 330 | 331 | val eff = effective[Future]{ implicit ctx => 332 | for { 333 | _ <- State[Foo].put(3) 334 | _ <- State[Bar].put("works") 335 | k <- State[Foo].get[Int] 336 | k2 <- State[Bar].get[String] 337 | _ <- State[Foo].putM[Int, String](k.toString + k2) 338 | k3 <- State[Foo].get[String] 339 | } yield (k3) 340 | } 341 | 342 | 343 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: HNil).futureValue 344 | 345 | println("Res:"+r) 346 | r should equal ("3works") 347 | } 348 | 349 | it should "mix State & StdIO" in { 350 | val eff = effective[Future, (State@@Foo<>Int) :: (State@@Bar<>String) :: (StdIO<>Unit) :: HNil]{ implicit ctx => 351 | for { 352 | // _ <- StdIO.println(s"Enter it:") 353 | // s <- StdIO.getStr() 354 | _ <- State[Foo].put(3) 355 | // _ <- State[Bar].put(s"works_$s") 356 | _ <- State[Bar].put(s"works") 357 | k <- State[Foo].get[Int] 358 | _ <- StdIO.println(s"Foo $k") 359 | k2 <- State[Bar].get[String] 360 | _ <- StdIO.println(s"Bar $k2") 361 | _ <- State[Foo].putM[Int, String](k.toString + k2) 362 | k3 <- State[Foo].get[String] 363 | } yield (k3) 364 | // } yield (s -> k3) 365 | } 366 | 367 | // val (s, r) = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: MkEff[StdIO, Unit](()) :: HNil).futureValue 368 | val r = eff.run(MkEff[State@@Foo, Int](0) :: MkEff[State@@Bar, String]("") :: MkEff[StdIO, Unit](()) :: HNil).futureValue 369 | 370 | println("Res:"+r) 371 | // r should equal (s"3works_$s") 372 | r should equal ("3works") 373 | } 374 | 375 | it should "FileIO simple" in { 376 | 377 | val eff = effective[Future, MkEff[FileIO, Unit] :: MkEff[StdIO, Unit] :: HNil] { implicit ctx => 378 | for { 379 | b <- FileIO.open[Read.type]("toto.txt") 380 | _ <- b match { 381 | case true => for { 382 | _ <- StdIO.println("Opened") 383 | k <- FileIO.readLine 384 | _ <- StdIO.println(s"Read $k") 385 | } yield () 386 | 387 | case false => 388 | StdIO.println("Can't Open").lift[(FileIO<>FileStatus[Read.type])::(StdIO<>Unit)::HNil] 389 | } 390 | _ <- FileIO.close[Read.type] 391 | } yield (()) 392 | } 393 | val r = eff.run(MkEff[FileIO, Unit](()) :: MkEff[StdIO, Unit](()) :: HNil).futureValue 394 | println("Res:"+r) 395 | 396 | } 397 | 398 | it should "FileIO write" in { 399 | 400 | val eff = effective[Future] { implicit ctx => 401 | for { 402 | b <- FileIO.open[Write.type]("tata.txt") 403 | _ <- b match { 404 | case true => for { 405 | _ <- StdIO.println("Opened") 406 | _ <- FileIO.writeLine("blabla") 407 | _ <- StdIO.println(s"wrote line") 408 | } yield () 409 | 410 | case false => 411 | StdIO.println("Can't Open").lift[(FileIO<>FileStatus[Write.type])::(StdIO<>Unit)::HNil] 412 | } 413 | _ <- FileIO.close[Write.type] 414 | } yield (()) 415 | } 416 | val r = eff.run(MkEff[FileIO, Unit](()) :: MkEff[StdIO, Unit](()) :: HNil).futureValue 417 | println("Res:"+r) 418 | 419 | } 420 | 421 | 422 | it should "FileIO read/write" in { 423 | 424 | // Just 425 | 426 | 427 | val eff = effective[Future] { implicit ctx => 428 | for { 429 | r <- FileIO[Foo].open[Read.type]("toto.txt") 430 | w <- FileIO[Bar].open[Write.type]("tata.txt") 431 | _ <- { 432 | // In current verion, we still need to lift all FX branches to a common local stack 433 | // to help Scalac in the recursion... 434 | type LocalStack = (FileIO@@Foo<>FileStatus[Read.type])::(FileIO@@Bar<>FileStatus[Write.type])::(StdIO<>Unit)::HNil 435 | 436 | if(r && w) { 437 | def rec: EffM[Future, Unit, LocalStack, LocalStack] = for { 438 | isEof <- FileIO[Foo].isEof 439 | _ <- if(!isEof) { 440 | for { 441 | s <- FileIO[Foo].readLine 442 | _ <- StdIO.print(s"Read $s") 443 | _ <- s match { 444 | case Some(s) => 445 | (for { 446 | _ <- FileIO[Bar].writeLine(s) 447 | _ <- StdIO.println(s"... Wrote $s") 448 | } yield ()).lift[LocalStack] 449 | case None => 450 | StdIO.println(s"... Nothing to write").lift[LocalStack] 451 | } 452 | _ <- rec 453 | } yield () 454 | } else { 455 | StdIO.println("EOF").lift[LocalStack] 456 | } 457 | } yield () 458 | rec 459 | } else { 460 | StdIO.println("Can't Open").lift[LocalStack] 461 | } 462 | } 463 | _ <- FileIO[Foo].close[Read.type] 464 | _ <- FileIO[Bar].close[Write.type] 465 | } yield (()) 466 | } 467 | 468 | // Initialize Resources 469 | val r = eff.run( 470 | MkEff[FileIO@@Foo, Unit](()) :: 471 | MkEff[FileIO@@Bar, Unit](()) :: 472 | MkEff[StdIO, Unit](()) :: 473 | HNil 474 | ).futureValue 475 | println("Res:"+r) 476 | 477 | } 478 | */ 479 | } 480 | 481 | // implicitly[RemoveAll.Aux[ 482 | // MkEff[StdIO, Unit] :: MkEff[FileIO, FileStatus[Read.type]] :: HNil, 483 | // MkEff[FileIO, FileStatus[Read.type]] :: HNil, 484 | // (MkEff[FileIO, FileStatus[Read.type]] :: HNil, MkEff[StdIO, Unit] :: HNil) 485 | // ]] 486 | 487 | // implicitly[Merge.Aux[ 488 | // MkEff[StdIO, Unit] :: MkEff[FileIO, FileStatus[Read.type]] :: HNil, 489 | // MkEff[StdIO, Unit] :: HNil, 490 | // MkEff[StdIO, Unit] :: MkEff[FileIO, FileStatus[Read.type]] :: HNil 491 | // ]] 492 | // FlatMappable.ESOSmallerThanESI2Different[ 493 | // MkEff[FileIO, Unit] :: HNil, 494 | // MkEff[FileIO, FileStatus[Read.type]] :: HNil, 495 | // MkEff[StdIO, Unit] :: MkEff[FileIO, FileStatus[Read.type]] :: HNil, 496 | // MkEff[StdIO, Unit] :: MkEff[FileIO, Unit] :: HNil, 497 | // MkEff[StdIO, Unit] :: HNil, 498 | // MkEff[FileIO, Unit] :: MkEff[StdIO, Unit] :: HNil 499 | // ] 500 | 501 | 502 | // implicitly[FlatMappable.Aux[ 503 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 504 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 505 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 506 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 507 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 508 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil 509 | // ]] 510 | 511 | // implicitly[FlatMappable.Aux[ 512 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 513 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 514 | // (State@@Bar<>String) :: HNil, 515 | // (State@@Bar<>String) :: HNil, 516 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 517 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil 518 | // ]] 519 | 520 | // implicitly[FlatMappable.Aux[ 521 | // (State@@Foo<>Int) :: HNil, 522 | // (State@@Foo<>Int) :: HNil, 523 | // (State@@Bar<>String) :: HNil, 524 | // (State@@Bar<>String) :: HNil, 525 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 526 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil 527 | // ]] 528 | 529 | // FlatMappable.one[ 530 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 531 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 532 | // (State@@Foo<>Int) :: HNil, 533 | // (State@@Foo<>Int) :: HNil, 534 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil 535 | // ] 536 | 537 | // val eff0: EffM[ 538 | // Future, Unit, 539 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 540 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil 541 | // ] = State[Foo].put(3).flatMap { _ => State[Bar].put("works") } 542 | 543 | // implicitly[ 544 | // IsoList[ 545 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 546 | // (State@@Bar<>String) :: (State@@Foo<>Int) :: HNil 547 | // ] 548 | // ] 549 | 550 | // IsoList.tail[ 551 | // (State@@Foo<>Int), (State@@Bar<>String) :: HNil, 552 | // (State@@Bar<>String) :: (State@@Foo<>Int) :: HNil, 553 | // (State@@Bar<>String) :: HNil 554 | // ] 555 | // FlatMappable.iso[ 556 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 557 | // (State@@Foo<>Int) :: (State@@Bar<>String) :: HNil, 558 | // (State@@Bar<>String) :: (State@@Foo<>Int) :: HNil, 559 | // (State@@Bar<>String) :: (State@@Foo<>Int) :: HNil 560 | // ] 561 | -------------------------------------------------------------------------------- /tata.txt: -------------------------------------------------------------------------------- 1 | alpha 2 | beta 3 | gamma 4 | delta 5 | eta 6 | -------------------------------------------------------------------------------- /toto.txt: -------------------------------------------------------------------------------- 1 | alpha 2 | beta 3 | gamma 4 | delta 5 | eta 6 | --------------------------------------------------------------------------------