fi.pelam.csv.util.Pipeline.scala Maven / Gradle / Ivy
/*
* This file is part of pelam-scala-csv
*
* Copyright © Peter Lamberg 2015 ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fi.pelam.csv.util
/**
* A monad abstraction that can be used to build up a chain of
* transformations of state. An input state can then be passed
* through the pipeline with the [[Pipeline!.run run]] method.
*
* The state is queried for success via the trait [[StageResult]].
*
* If the state does not report as success after executing a stage,
* the rest of the pipeline is bypassed.
*
* == Code example ==
*
* {{{
* case class State(override val success: Boolean = true, value: Int = 0) extends SuccessState
*
* val pipeline = for (
* _ <- Pipeline.Stage((a: State) => a.copy(value = a.value + 1));
* _ <- Pipeline.Stage((a: State) => a.copy(value = a.value + 10));
* finalState <- Pipeline.Stage((a: State) => a.copy(value = a.value + 100))
* ) yield finalState
*
* println(pipeline.run(State()))
* // Will print State(true,111)
* }}}
*
* @tparam S The type of the state to be threaded through pipeline stages.
*/
// TODO: Make it more clear in the example what the stages do.
// TODO: Is it accurate to call this a monad?
sealed trait Pipeline[S <: StageResult[S]] {
def map(f: S => S): Pipeline[S]
def flatMap(inner: S => Pipeline[S]): Pipeline[S]
def run(inputState: S): S
}
object Pipeline {
final case class Stage[S <: StageResult[S]](stageFunc: S => S) extends Pipeline[S] {
override def map(mapFunc: S => S) = Stage[S](state => mapFunc(stageFunc(state)))
override def flatMap(inner: S => Pipeline[S]): Pipeline[S] = FlatmapStage(this, inner)
override def run(inputState: S) = stageFunc(inputState).stageNumberIncremented()
}
private final case class FlatmapStage[S <: StageResult[S]](outer: Pipeline[S],
inner: S => Pipeline[S],
mapFunc: S => S = (state: S) => state) extends Pipeline[S] {
override def map(newMapFunc: S => S) = FlatmapStage(outer, inner,
(state: S) => mapFunc(newMapFunc(state)))
override def flatMap(newInner: S => Pipeline[S]): Pipeline[S] = FlatmapStage(this, newInner)
override def run(inputState: S) = {
val outerResult = outer.run(inputState)
if (outerResult.success) {
// Call the inner (and downstream in the pipeline) stages.
mapFunc(inner(outerResult).run(outerResult))
} else {
// Don't run further stages if one had error, but run the final map.
mapFunc(outerResult)
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy