All Downloads are FREE. Search and download functionalities are using the official Maven repository.

quasar.fp.process.scala Maven / Gradle / Ivy

There is a newer version: 28.1.6
Show newest version
/*
 * Copyright 2014–2016 SlamData Inc.
 *
 * 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 quasar.fp

import quasar.Predef._

import scala.collection.{Seq => SSeq}

import scalaz._, Scalaz._
import scalaz.concurrent.Task
import scalaz.stream._

trait ProcessOps {
  implicit class PrOps[F[_], O](self: Process[F, O]) {
    def cleanUpWith(t: F[Unit]): Process[F, O] =
      self.onComplete(Process.eval(t).drain)

    final def evalScan1(f: (O, O) => F[O])(implicit monad: Monad[F]): Process[F, O] = {
      self.zipWithPrevious.evalMap {
        case (None, next) => monad.point(next)
        case (Some(prev), next) => f(prev, next)
      }
    }

    /** Exposes the effect from the first `Await` encountered, the inner process
      * emits the same values, in the same order as this process.
      */
    final def firstStep[F2[x] >: F[x], O2 >: O](implicit F: Monad[F2], C: Catchable[F2]): F2[Process[F2, O2]] = {
      val (hd, tl) = self.unemit
      tl.unconsOption[F2, O2] map {
        case Some((x, xs)) => Process.emitAll(hd :+ x) ++ xs
        case None          => Process.emitAll(hd)
      }
    }

    /** Step through `Await`s in this `Process`, combining effects via `Bind`,
      * until the predicate returns true or the stream ends. The returned inner
      * `Process` emits the same values, in the same order as this process.
      */
    final def stepUntil[F2[x] >: F[x], O2 >: O](p: SSeq[O2] => Boolean)(implicit F: Monad[F2], C: Catchable[F2]): F2[Process[F2, O2]] =
      firstStep[F2, O2] flatMap { next =>
        val (hd, tl) = next.unemit

        if (hd.isEmpty || p(hd))
          (Process.emitAll(hd) ++ tl).point[F2]
        else
          tl.stepUntil(p).map(Process.emitAll(hd) ++ _)
      }
  }

  implicit class ProcessOfTaskOps[O](self: Process[Task,O]) {
    // Is there a better way to implement this?
    def onHaltWithLastElement(f: (Option[O], Cause) => Process[Task,O]): Process[Task,O] = {
      Process.await(TaskRef[Option[O]](None)){ lastA =>
        self.observe(Process.constant((a:O) => lastA.write(Some(a)))).onHalt{ cause =>
          Process.await(lastA.read)( a => f(a,cause))
        }
      }
    }
    def cleanUpWithA(f: Option[O] => Task[Unit]): Process[Task,O] = {
      self.onHaltWithLastElement((a, cause) => Process.eval_(f(a)).causedBy(cause))
    }
  }

  implicit class TaskOps[A](t: Task[A]) {
    def onSuccess(f: A => Task[Unit]): Task[A] = {
      t.flatMap(a => f(a).as(a))
    }
  }
}

object ProcessOps extends ProcessOps




© 2015 - 2025 Weber Informatics LLC | Privacy Policy