Skip to content

Commit 846d0a4

Browse files
committed
fix: make the NaturalTransformation lazy in order to not duplicate effects
for Id ~> Action
1 parent 3383b3a commit 846d0a4

File tree

5 files changed

+40
-6
lines changed

5 files changed

+40
-6
lines changed

build.sbt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,13 @@ lazy val mimaSettings =
120120
// Because, in that case, it is very possible to confuse the ownEnv with the env and shutdown the env
121121
// which breaks the execution of the whole specification
122122
ProblemFilters.exclude[DirectMissingMethodProblem]("org.specs2.specification.core.OwnEnv.ownEnv"),
123-
ProblemFilters.exclude[DirectMissingMethodProblem]("org.specs2.specification.core.OwnExecutionEnv.env")
123+
ProblemFilters.exclude[DirectMissingMethodProblem]("org.specs2.specification.core.OwnExecutionEnv.env"),
124+
125+
// issue #1277 NaturalTransformation needs to be made lazier
126+
ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.specs2.fp.NaturalTransformation.apply"),
127+
ProblemFilters.exclude[ReversedMissingMethodProblem]("org.specs2.fp.NaturalTransformation.apply"),
128+
ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.specs2.fp.NaturalTransformation#naturalId.apply"),
129+
ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.specs2.control.Operation#operationToAction.apply")
124130
)
125131
)
126132

common/shared/src/main/scala/org/specs2/control/Operation.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ object Operation:
163163
"Applicative[Operation]"
164164

165165
given operationToAction: NaturalTransformation[Operation, Action] with
166-
def apply[A](operation: Operation[A]): Action[A] =
166+
def apply[A](operation: =>Operation[A]): Action[A] =
167167
operation.toAction
168168

169169
given SafeOperation: Safe[Operation] with
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package user
2+
3+
import org.specs2.*
4+
import org.specs2.concurrent.*
5+
import org.specs2.control.*
6+
import org.specs2.control.origami.*
7+
import org.specs2.control.producer.*
8+
import org.specs2.fp.*
9+
10+
class FoldSpec(ee: ExecutionEnv) extends Specification {
11+
def is = s2"""
12+
13+
A correct implementation of NaturalTransformation from Id to Action must not duplicate effects (see issue #1277) $dontDuplicateEffects
14+
15+
"""
16+
17+
def dontDuplicateEffects = {
18+
val p = Producer.emitAll[Action, Int](1, 2, 3)
19+
20+
// The NaturalTransformation from Id (which is Id[X] X) to Action must
21+
// make sure to not run side effects of X twice.
22+
// This tests guarantees that during the evaluation of `zip` and the various folds
23+
// we don't run the action for collection elements into a mutable map more than necessary.
24+
val vs = p.fold(Folds.list[Int].into[Action] `zip` Folds.list[Int].into[Action])
25+
vs.run(ee) === (List(1, 2, 3), List(1, 2, 3))
26+
}
27+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.specs2.fp
22

33
trait NaturalTransformation[-F[_], +G[_]]:
4-
def apply[A](fa: F[A]): G[A]
4+
def apply[A](fa: =>F[A]): G[A]
55

66
object NaturalTransformation:
77

88
given naturalId[M[_]: Monad]: NaturalTransformation[Id, M] with
9-
def apply[A](fa: Id[A]): M[A] =
9+
def apply[A](fa: =>Id[A]): M[A] =
1010
summon[Monad[M]].point(fa)

html/src/main/scala/org/specs2/reporter/HtmlPrinter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ case class HtmlPrinter(env: Env, searchPage: SearchPage, logger: Logger = Consol
4040

4141
/** @return a SinkTask for the Html output */
4242
def sink(spec: SpecStructure): AsyncSink[Fragment] =
43-
((Statistics.fold `zip` list[Fragment].into[Action] `zip` SimpleTimer.timerFold.into[Action]) <*
44-
fromStart((getHtmlOptions(env.arguments) >>= (options => copyResources(env, options))).void.toAction))
43+
val copyHtmlResources = (getHtmlOptions(env.arguments) >>= (options => copyResources(env, options))).void.toAction
44+
val htmlSink = (Statistics.fold `zip` list[Fragment].into[Action] `zip` SimpleTimer.timerFold.into[Action])
4545
.mapFlatten { case ((stats, fragments), timer) =>
4646
val executedSpec = spec.copy(lazyFragments = () => Fragments(fragments*))
4747
getPandoc(env).flatMap {
4848
case Some(pandoc) => printHtmlWithPandoc(env, executedSpec, stats, timer, pandoc).toAction
4949
case _ => printHtml(env, executedSpec, stats, timer).toAction
5050
}
5151
}
52+
htmlSink.startWith(copyHtmlResources)
5253

5354
/** WITHOUT PANDOC
5455
*/

0 commit comments

Comments
 (0)