Skip to content

Commit

Permalink
fixed stack safety issues for pure values
Browse files Browse the repository at this point in the history
  • Loading branch information
etorreborre committed Mar 23, 2017
1 parent 0e0534e commit 7bccde1
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 12 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ You can also check out [this presentation](http://bit.ly/eff_flatmap_2016) at fl

You add `eff` as an sbt dependency:
```scala
libraryDependencies += "org.atnos" %% "eff" % "4.0.0"
libraryDependencies += "org.atnos" %% "eff" % "4.0.1"

// to write types like Reader[String, ?]
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.3")
Expand Down
10 changes: 10 additions & 0 deletions jvm/src/test/scala/org/atnos/eff/ErrorEffectSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ErrorEffect.{ok => OK, ErrorOrOk}

import scala.collection.mutable.ListBuffer
import cats.data._
import cats.implicits._
import cats.Eval
import org.atnos.eff.all._
import org.atnos.eff.syntax.all._
Expand All @@ -24,6 +25,8 @@ class ErrorEffectSpec extends Specification { def is = s2"""
A thrown exception can be ignored $ignored

An action with a given failure type can be translated to an action with another failure type $local

The error effect is stack-safe $stackSafety
"""

type R = Fx.fx1[ErrorOrOk]
Expand Down Expand Up @@ -138,5 +141,12 @@ class ErrorEffectSpec extends Specification { def is = s2"""
ErrorEffect2.runError(action2[Fx.fx1[ErrorOrOk2]]).run ==== Left(Right(Error2(Error1("boom"))))
}

def stackSafety = {
val list = (1 to 5000).toList
val action = list.traverse(i => ErrorEffect.ok(i.toString))

ErrorEffect.runError(action).run ==== Right(list.map(_.toString))
}

}

9 changes: 8 additions & 1 deletion jvm/src/test/scala/org/atnos/eff/FutureEffectSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ class FutureEffectSpec(implicit ee: ExecutionEnv) extends Specification with Sca
Attempted Future calls with timeout can be memoized $e12

TimedFuture calls can be memoized with a memo effect $e13
addLast can be used to make sure an action executes when a future fails $e11
addLast can be used to make sure an action executes when a future fails $e14

Future effects can be detached safely with detachA $e15

"""

Expand Down Expand Up @@ -179,6 +180,12 @@ class FutureEffectSpec(implicit ee: ExecutionEnv) extends Specification with Sca
lastActionDone must beTrue
}

def e15 = {
val list = (1 to 5000).toList
val action = list.traverse(i => futureDelay(i)).detach
action.runNow(scheduler, ee.executionContext) must be_===(list).awaitFor(1 second)
}

/**
* HELPERS
*/
Expand Down
6 changes: 6 additions & 0 deletions notes/4.0.1.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Stack-safety fixes.

The stack-safety sage continues.

* fixed a stack-safety issue when running a interpreting pure values
* fixed a stack-safety issue when injecting an effect into a larger stack with `into`
12 changes: 6 additions & 6 deletions shared/src/main/scala/org/atnos/eff/Eff.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ trait EffImplicits {
override def map[A, B](fa: Eff[AnyRef, A])(f: A => B): Eff[AnyRef, B] =
fa match {
case Pure(a, l) =>
pure(f(a)).addLast(l)
Impure(NoEffect(a), Continuation.unit.map(f), l)

case Impure(union, continuation, last) =>
Impure(union, continuation map f, last)
Expand Down Expand Up @@ -366,17 +366,17 @@ trait EffInterpretation {
case Impure(u: Union[_, _], continuation, last) =>
val ta = u.tagged.valueUnsafe.asInstanceOf[M[A]]
last match {
case Last(Some(l)) => ta.map(x => Left(continuation(x).addLast(last)))
case Last(None) => ta.map(x => Left(continuation(x)))
case Last(Some(l)) => ta.map(x => Left(Impure(NoEffect(x.asInstanceOf[Any]), continuation, last)))
case Last(None) => ta.map(x => Left(Impure(NoEffect(x.asInstanceOf[Any]), continuation)))
}

case ap @ ImpureAp(unions, continuation, last) =>
val effects = unions.unions.map(_.tagged.valueUnsafe.asInstanceOf[M[Any]])
val sequenced = applicative.sequence(effects)

last match {
case Last(Some(_)) => sequenced.map(x => Left(continuation(x).addLast(last)))
case Last(None) => sequenced.map(x => Left(continuation(x)))
case Last(Some(_)) => sequenced.map(xs => Left(Impure(NoEffect(xs), continuation, last)))
case Last(None) => sequenced.map(xs => Left(Impure(NoEffect(xs), continuation)))
}
}

Expand All @@ -387,7 +387,7 @@ trait EffInterpretation {
eff match {
case Pure(a, Last(Some(l))) => l.value; Option(a)
case Pure(a, _) => Option(a)
case Impure(NoEffect(a), c, l) => runPure(c(a).addLast(l))
case Impure(NoEffect(a), c, l) => runPure(c(a).addLast(l))
case _ => None
}

Expand Down
2 changes: 1 addition & 1 deletion shared/src/main/scala/org/atnos/eff/Interpret.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ trait Interpret {
interpreter.onPure(a).addLast(interpretLast(last))

case Impure(NoEffect(a), c, last) =>
runInterpreter(c(a).addLast(last))(interpreter)
Impure(NoEffect(a), interpretContinuation(c), interpretLast(last))

case Impure(u: Union[_,_], c, last) =>
m.project(u) match {
Expand Down
2 changes: 1 addition & 1 deletion shared/src/main/scala/org/atnos/eff/Unions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ trait UnionInto[R, S] {
Eff.pure(a).addLast(last.interpret(into))

case Impure(NoEffect(a), c, l) =>
into(c(a).addLast(l))
Impure(NoEffect(a), c.interpretEff(into)(into), l.interpret(into))

case Impure(u: Union[_, _], c, l) =>
Impure(apply(u), c.interpretEff(into)(into), l.interpret(into))
Expand Down
2 changes: 1 addition & 1 deletion try-eff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
test -e ~/.coursier/cr || (mkdir -p ~/.coursier && wget -q -O ~/.coursier/cr https://git.io/vgvpD && chmod +x ~/.coursier/cr)
CLASSPATH="$(~/.coursier/cr fetch -q -p \
\
org.atnos:eff_2.12:4.0.0 \
org.atnos:eff_2.12:4.0.1 \
com.lihaoyi:ammonite_2.12.1:0.8.1 \
\
)" java ammonite.Main --predef 'import org.atnos.eff._, all._, syntax.all._'
2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version in ThisBuild := "4.0.0"
version in ThisBuild := "4.0.1"

0 comments on commit 7bccde1

Please sign in to comment.