3 Commits
0.0.2 ... 0.0.3

Author SHA1 Message Date
6efaac148b Merge pull request #3 from fluencelabs/point
Introducing Point monad
2018-05-21 13:54:20 +03:00
6378af3658 Docs for existential type bound for MonadError 2018-05-21 13:45:19 +03:00
e1b5bfb947 Introducing Point monad 2018-05-21 11:59:37 +03:00
4 changed files with 134 additions and 6 deletions

View File

@ -14,7 +14,7 @@ val scalaV = scalaVersion := "2.12.5"
val commons = Seq(
scalaV,
version := "0.0.2",
version := "0.0.3",
fork in Test := true,
parallelExecution in Test := false,
organization := "one.fluence",

View File

@ -24,7 +24,7 @@ import cats.data.{EitherT, Kleisli}
import cats.syntax.flatMap._
import cats.syntax.compose._
import scala.language.higherKinds
import scala.language.{existentials, higherKinds}
import scala.util.Try
/**
@ -62,9 +62,10 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
* Run the func on input, lifting the error into MonadError effect.
*
* @param input Input
* @param F All internal maps and composes, as well as errors, are to be executed with this Monad
* @param F All internal maps and composes, as well as errors, are to be executed with this MonadError.
* Error type should be a supertype for this arrow's error E.
*/
def runF[F[_]](input: A)(implicit F: MonadError[F, Throwable]): F[B] =
def runF[F[_]](input: A)(implicit F: MonadError[F, EE] forSome {type EE >: E}): F[B] =
runEither(input).flatMap(F.fromEither)
/**
@ -78,9 +79,10 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
/**
* Converts this Func to Kleisli, using MonadError to execute upon and to lift errors into.
*
* @param F All internal maps and composes, as well as errors, are to be executed with this Monad
* @param F All internal maps and composes, as well as errors, are to be executed with this MonadError.
* Error type should be a supertype for this arrow's error E.
*/
def toKleisli[F[_]](implicit F: MonadError[F, Throwable]): Kleisli[F, A, B] =
def toKleisli[F[_]](implicit F: MonadError[F, EE] forSome {type EE >: E}): Kleisli[F, A, B] =
Kleisli(input runF[F](input))
/**
@ -93,6 +95,15 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
import cats.instances.try_._
runF[Try](input).get
}
/**
* Picks a point from the arrow, using the initial element (Unit) on the left.
*
* @param input Point to pick
* @return Picked point
*/
def pointAt(input: A): Point[B] =
catsMonadicalEitherArrowChoice.lmap(this)(_ input)
}
/**
@ -182,6 +193,26 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
EitherT.rightT[F, E](input).subflatMap(f)
}
/**
* Check a condition, lifted with a Func.
*
* @param error Error to produce when condition is not met
* @return Func that takes boolean, checks it, and returns Unit or fails with given error
*/
def cond(error: E): Func[Boolean, Unit] =
liftFuncEither(Either.cond(_, (), error))
/**
* Lift a function which returns a Func arrow with Unit on the left side.
*
* @param f Function to lift
*/
def liftFuncPoint[A, B](f: A Point[B]): Func[A,B] =
new Func[A,B]{
override def apply[F[_] : Monad](input: A): EitherT[F, E, B] =
f(input).apply[F](())
}
/**
* Func that does nothing with input.
*/
@ -215,6 +246,42 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
}
}
/**
* Point type maps from Unit to a particular value of A, so it's just a lazy Func.
*
* @tparam A Output value type
*/
type Point[A] = Func[Unit, A]
/**
* Point must obey MonadErrorLaws
*/
implicit object catsMonadicalEitherPointMonad extends MonadError[Point, E] {
override def flatMap[A, B](fa: Point[A])(f: A Point[B]): Point[B] =
new Func[Unit, B]{
override def apply[F[_] : Monad](input: Unit): EitherT[F, E, B] =
fa[F](()).flatMap(f(_).apply[F](()))
}
override def tailRecM[A, B](a: A)(f: A Point[Either[A, B]]): Point[B] =
new Func[Unit, B]{
override def apply[F[_] : Monad](input: Unit): EitherT[F, E, B] =
Monad[EitherT[F, E, ?]].tailRecM(a)(f(_).apply[F](()))
}
override def raiseError[A](e: E): Point[A] =
liftFuncEither(_ Left(e))
override def handleErrorWith[A](fa: Point[A])(f: E Point[A]): Point[A] =
new Func[Unit, A]{
override def apply[F[_] : Monad](input: Unit): EitherT[F, E, A] =
fa[F](()).leftFlatMap(e f(e).apply[F](()))
}
override def pure[A](x: A): Point[A] =
liftFunc(_ x)
}
/**
* Lifts pure direct and inverse functions into Bijection.
*
@ -237,6 +304,12 @@ abstract class MonadicalEitherArrow[E <: Throwable] {
def liftEitherB[A, B](direct: A Either[E, B], inverse: B Either[E, A]): Bijection[A, B] =
Bijection(liftFuncEither(direct), liftFuncEither(inverse))
/**
* Lifts point functions into Bijection.
*/
def liftPointB[A,B](direct: A Point[B], inverse: B Point[A]): Bijection[A,B] =
Bijection(liftFuncPoint(direct), liftFuncPoint(inverse))
/**
* Bijection that does no transformation.
*/

View File

@ -23,6 +23,13 @@ import org.scalacheck.{Arbitrary, Cogen, Gen}
import org.scalacheck.ScalacheckShapeless._
object PureCodecFuncTestInstances {
implicit def arbCodecError: Arbitrary[CodecError] =
Arbitrary(Gen.alphaLowerStr.map(CodecError(_)))
implicit def eqCodecError: Eq[CodecError] = Eq.fromUniversalEquals
implicit def cogenE: Cogen[CodecError] = Cogen.cogenString.contramap[CodecError](_.message)
implicit def arbFunc[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[PureCodec.Func[A, B]] =
Arbitrary(
Gen

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2017 Fluence Labs Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fluence.codec
import cats.{Eq, Invariant}
import cats.data.EitherT
import cats.laws.discipline.{MonadErrorTests, SemigroupalTests}
import cats.tests.CatsSuite
import fluence.codec
import org.scalacheck.ScalacheckShapeless._
class PureCodecPointLawsSpec extends CatsSuite {
import PureCodecFuncTestInstances._
implicit def eqEitherTFEA: Eq[EitherT[PureCodec.Point, CodecError, Int]] =
Eq.instance{
case (aa,bb)
aa.value.unsafe(()) == bb.value.unsafe(())
}
implicit val iso = SemigroupalTests.Isomorphisms.invariant[PureCodec.Point](
new Invariant[PureCodec.Point]{
override def imap[A, B](fa: codec.PureCodec.Point[A])(f: A B)(g: B A): codec.PureCodec.Point[B] =
fa.map(f)
}
)
checkAll(
"PureCodec.Point.MonadErrorLaws",
MonadErrorTests[PureCodec.Point, CodecError].monadError[Int, String, Double]
)
}