Skip to content

Commit

Permalink
Issue #89 - add monix-types
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandru committed Mar 15, 2016
1 parent ad34435 commit b7854f2
Show file tree
Hide file tree
Showing 16 changed files with 1,099 additions and 10 deletions.
48 changes: 40 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ lazy val scalaMacroDependencies = Seq(
lazy val unidocSettings = baseUnidocSettings ++ Seq(
autoAPIMappings := true,
unidocProjectFilter in (ScalaUnidoc, unidoc) :=
inProjects(executionJVM, tasksJVM, streamsJVM),
inProjects(executionJVM, tasksJVM, streamsJVM, typesJVM),

scalacOptions in (ScalaUnidoc, unidoc) +=
"-Xfatal-warnings",
Expand Down Expand Up @@ -206,11 +206,27 @@ lazy val monix = project.in(file("."))
executionJVM, executionJS,
tasksJVM, tasksJS,
streamsJVM, streamsJS,
typesJVM, typesJS,
monixJVM, monixJS,
docs, tckTests)
.settings(sharedSettings)
.settings(doNotPublishArtifact)
.settings(scalaStyleSettings)

lazy val monixJVM = project.in(file("monix/jvm"))
.dependsOn(executionJVM, tasksJVM, streamsJVM, typesJVM)
.aggregate(executionJVM, tasksJVM, streamsJVM, typesJVM)
.settings(crossSettings)
.settings(name := "monix")

lazy val monixJS = project.in(file("monix/js"))
.enablePlugins(ScalaJSPlugin)
.dependsOn(executionJS, tasksJS, streamsJS, typesJS)
.aggregate(executionJS, tasksJS, streamsJS, typesJS)
.settings(crossSettings)
.settings(scalaJSSettings)
.settings(name := "monix")

lazy val executionJVM = project.in(file("monix-execution/jvm"))
.settings(crossSettings)
.settings(testSettings)
Expand Down Expand Up @@ -246,10 +262,7 @@ lazy val tasksJS = project.in(file("monix-tasks/js"))
.settings(tasksCommon)

lazy val streamsCommon =
crossSettings ++ testSettings ++ scalaMacroDependencies ++ Seq(
name := "monix-streams",
libraryDependencies += "com.github.mpilquist" %%% "simulacrum" % "0.7.0"
)
crossSettings ++ testSettings ++ Seq(name := "monix-streams")

lazy val streamsJVM = project.in(file("monix-streams/jvm"))
.dependsOn(executionJVM, tasksJVM)
Expand All @@ -262,16 +275,35 @@ lazy val streamsJS = project.in(file("monix-streams/js"))
.settings(streamsCommon)
.settings(scalaJSSettings)

lazy val typesCommon =
crossSettings ++ testSettings ++ scalaMacroDependencies ++ Seq(
name := "monix-types",
libraryDependencies ++= Seq(
"com.github.mpilquist" %%% "simulacrum" % "0.7.0",
"org.typelevel" %% "cats-core" % "0.4.1"
)
)

lazy val typesJVM = project.in(file("monix-types/jvm"))
.dependsOn(tasksJVM, streamsJVM)
.settings(typesCommon)

lazy val typesJS = project.in(file("monix-types/js"))
.enablePlugins(ScalaJSPlugin)
.dependsOn(tasksJS, streamsJS)
.settings(typesCommon)
.settings(scalaJSSettings)

lazy val docs = project.in(file("docs"))
.dependsOn(executionJVM, tasksJVM, streamsJVM)
.dependsOn(executionJVM, tasksJVM, streamsJVM, typesJVM)
.settings(sharedSettings)
.settings(doNotPublishArtifact)
.settings(site.settings)
.settings(tutSettings)
.settings(docsSettings)

lazy val tckTests = project.in(file("tckTests"))
.dependsOn(streamsJVM)
.dependsOn(monixJVM)
.settings(sharedSettings)
.settings(doNotPublishArtifact)
.settings(
Expand All @@ -281,7 +313,7 @@ lazy val tckTests = project.in(file("tckTests"))
))

lazy val benchmarks = project.in(file("benchmarks"))
.dependsOn(streamsJVM)
.dependsOn(monixJVM)
.enablePlugins(JmhPlugin)
.settings(sharedSettings)
.settings(doNotPublishArtifact)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ abstract class ObservableLike[+A, Self[+T] <: ObservableLike[T, Self]] { self: S
/** Creates a new Observable that emits the events of the source and
* then it also emits the given elements (appended to the stream).
*/
def endWith[B >: A](elems: B*): Self[B] =
def endWith[B >: A](elems: Seq[B]): Self[B] =
self.transform(self => self ++ Observable.fromIterable(elems))

/** Emits the given exception instead of `onComplete`.
Expand Down Expand Up @@ -1148,7 +1148,7 @@ abstract class ObservableLike[+A, Self[+T] <: ObservableLike[T, Self]] { self: S
/** Creates a new Observable that emits the given elements and then it
* also emits the events of the source (prepend operation).
*/
def startWith[B >: A](elems: B*): Self[B] =
def startWith[B >: A](elems: Seq[B]): Self[B] =
self.transform(self => Observable.fromIterable(elems) ++ self)

/** Returns a new Observable that uses the specified `Scheduler` for
Expand Down
94 changes: 94 additions & 0 deletions monix-types/shared/src/main/scala/monix/types/FFoldable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2014-2016 by its authors. Some rights reserved.
* See the project homepage at: https://monix.io
*
* 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 monix.types

import algebra.Monoid
import simulacrum.typeclass
import scala.language.{higherKinds, implicitConversions}

/** Data structures that can be folded to a summary value,
* possibly lazily or asynchronously.
*
* The main operation is `foldLeftF` that folds `fa`
* from left to right, or from first to last. Beyond this it
* provides many other useful methods related to
* folding over `F[A]` values.
*
* Note that a corresponding `foldRight` is not provided,
* because that would be incompatible with large or infinite
* streams.
*
* See: [[http://www.cs.nott.ac.uk/~pszgmh/fold.pdf A tutorial on the universality and expressiveness of fold]].
*/

@typeclass trait FFoldable[F[_]] {
/** Left associative asynchronous fold on 'F' using the function 'f'. */
def foldLeftF[A, S](fa: F[A], seed: S)(f: (S, A) => S): F[S]

/** Given a sequences, produces a new sequence that will expose the
* count of the source.
*/
def countF[A](fa: F[A]): F[Long] =
foldLeftF(fa, 0L)((acc,_) => acc + 1)

/** Check whether at least one element satisfies the predicate.
*
* If there are no elements, the result is `false`.
*/
def existsF[A](fa: F[A])(p: A => Boolean): F[Boolean] =
foldLeftF(fa, false) { (acc, elem) => acc || p(elem) }

/** Find the first element matching the predicate, if one exists. */
def findOptF[A](fa: F[A])(p: A => Boolean): F[Option[A]] =
foldLeftF(fa, Option.empty[A]) {
(acc, elem) => acc match {
case None => if (p(elem)) Some(elem) else None
case ref @ Some(_) => ref
}
}

/** Folds a `Monoid`. */
def foldF[A](fa: F[A])(implicit A: Monoid[A]): F[A] =
foldLeftF(fa, A.empty)(A.combine)

/** Fold implemented by mapping `A` values into `B` and then
* combining them using the given `Monoid[B]` instance.
*/
def foldMapF[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): F[B] =
foldLeftF(fa, B.empty)((b, a) => B.combine(b, f(a)))

/** Check whether all elements satisfies the predicate.
*
* If at least one element doesn't satisfy the predicate,
* the result is `false`.
*/
def forAllF[A](fa: F[A])(p: A => Boolean): F[Boolean] =
foldLeftF(fa, true) { (acc, elem) => acc && p(elem) }

/** Checks if the source sequence is empty. */
def isEmptyF[A](fa: F[A]): F[Boolean] =
foldLeftF(fa, true)((_,_) => false)

/** Checks if the source sequence is non-empty. */
def nonEmptyF[A](fa: F[A]): F[Boolean] =
foldLeftF(fa, false)((_,_) => true)

/** Given a sequence of numbers, calculates a sum. */
def sumF[A](fa: F[A])(implicit A: Numeric[A]): F[A] =
foldLeftF(fa, A.zero)(A.plus)
}
55 changes: 55 additions & 0 deletions monix-types/shared/src/main/scala/monix/types/MonadCons.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2014-2016 by its authors. Some rights reserved.
* See the project homepage at: https://monix.io
*
* 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 monix.types

import simulacrum._
import scala.language.{higherKinds, implicitConversions}

/** Type-class for monadic data-structures that can expose
* multiple `A` elements.
*/
@typeclass trait MonadCons[F[_]] extends Monad[F] {
/** Builds an instance by joining a head and a lazy tail. */
def cons[A](head: A, tail: => F[A]): F[A]

/** Alias for `flatMap`. */
def concatMap[A,B](fa: F[A])(f: A => F[B]): F[B]

/** Alias for `flatten`. */
def concat[A](ffa: F[F[A]]): F[A] =
concatMap(ffa)(fa => fa)

/** Concatenates the source with `other`. */
@op("++") def followWith[A](fa: F[A])(that: => F[A]): F[A] =
flatMap(cons(fa, pure(that)))(fa => fa)
/** Appends the given `elem` at the end. */
@op(":+") def endWithElem[A](fa: F[A])(elem: A): F[A] =
followWith(fa)(pure(elem))
/** Prepends the given `elem` at the start. */
@op("+:") def startWithElem[A](fa: F[A])(elem: A): F[A] =
followWith(pure(elem))(fa)

/** Repeats the source, continuously. */
def repeat[A](fa: F[A]): F[A] =
followWith(fa)(repeat(fa))

override def flatten[A](ffa: F[F[A]]): F[A] =
concat(ffa)
override def flatMap[A, B](fa: F[A])(f: (A) => F[B]): F[B] =
concatMap(fa)(f)
}
50 changes: 50 additions & 0 deletions monix-types/shared/src/main/scala/monix/types/MonadConsError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2014-2016 by its authors. Some rights reserved.
* See the project homepage at: https://monix.io
*
* 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 monix.types

import scala.language.{higherKinds, implicitConversions}

/** Type-class for monadic data-structures that can expose
* multiple `A` elements.
*/
trait MonadConsError[F[_],E] extends MonadCons[F] with Recoverable[F,E] {
/** A variant of [[concatMap]] that delays any triggered errors
* for as long as possible.
*
* Typically this means delaying any errors until the source
* and any child produced by the given function are complete.
* Since this can involve delaying multiple errors, it is
* recommended for the final error to be a composite.
*/
def concatMapDelayError[A,B](fa: F[A])(f: A => F[B]): F[B]

/** A variant of [[concat]] that delays any triggered errors
* for as long as possible.
*
* Typically this means delaying any errors until the source
* and any child produced by it are complete. Since this can
* involve delaying multiple errors, it is recommended for
* the final error to be a composite.
*/
def concatDelayError[A](ffa: F[F[A]]): F[A] =
concatMapDelayError(ffa)(identity)

/** Mirrors the source, but ends it in error. */
def endWithError[A](fa: F[A], error: E): F[A] =
followWith(fa)(raiseError(error))
}
55 changes: 55 additions & 0 deletions monix-types/shared/src/main/scala/monix/types/Nondeterminism.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2014-2016 by its authors. Some rights reserved.
* See the project homepage at: https://monix.io
*
* 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 monix.types

import simulacrum.typeclass
import scala.concurrent.duration.FiniteDuration
import scala.language.{higherKinds, implicitConversions}

@typeclass trait Nondeterminism[F[_]] {
/** Given a list of non-deterministic structures, mirrors the
* first that manages to emit an element or that completes and
* ignore or cancel the rest.
*/
def firstStartedOf[A](seq: Seq[F[A]]): F[A]

/** Delays the execution of the instance and consequently the execution of
* any side-effects, by the specified `timespan`.
*/
def delayExecution[A](fa: F[A], timespan: FiniteDuration): F[A]

/** Delays the execution of the instance and consequently the execution of
* any side-effects, until the given `trigger` emits an element or completes.
*/
def delayExecutionWith[A,B](fa: F[A], trigger: F[B]): F[A]

/** Executes the source immediately, but delays the signaling by
* the specified `timespan`. In case `F` is a sequence,
* then the delay will be applied to each element, but not
* to completion or the signaling of an error.
*/
def delayResult[A](fa: F[A], timespan: FiniteDuration): F[A]

/** Executes the source immediately, but delays the signaling
* until the specified `selector` emits an element or completes.
* In case `F` is a sequence, then the delay will be applied
* to each element, but not to completion or the
* signaling of an error.
*/
def delayResultBySelector[A,B](fa: F[A])(selector: A => F[B]): F[A]
}
Loading

0 comments on commit b7854f2

Please sign in to comment.