-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #138 from Kevin-Lee/task/132/add-optiont
Close #132 - Add OptionT
- Loading branch information
Showing
3 changed files
with
211 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package just.fp | ||
|
||
import just.fp.syntax.{implicitToSome, none} | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2019-11-29 | ||
*/ | ||
final case class OptionT[F[_], A](run: F[Option[A]]) { | ||
def map[B](f: A => B)(implicit F: Functor[F]): OptionT[F, B] = | ||
OptionT(F.map(run)(_.map(f))) | ||
|
||
def ap[B](fa: OptionT[F, A => B])(implicit F: Applicative[F]): OptionT[F, B] = | ||
OptionT( | ||
F.ap(run)(F.map(fa.run) { | ||
case Some(ab) => | ||
{ | ||
case Some(a) => | ||
ab(a).some | ||
case None => | ||
none[B] | ||
} | ||
case None => | ||
_ => none[B] | ||
}) | ||
) | ||
|
||
def flatMap[B](f: A => OptionT[F, B])(implicit M: Monad[F]): OptionT[F, B] = | ||
OptionT( | ||
M.flatMap(run) { | ||
case Some(a) => | ||
f(a).run | ||
case None => | ||
M.pure(none[B]) | ||
} | ||
) | ||
|
||
def isDefined(implicit F: Functor[F]): F[Boolean] = | ||
F.map(run)(_.isDefined) | ||
|
||
def isEmpty(implicit F: Functor[F]): F[Boolean] = | ||
F.map(run)(_.isEmpty) | ||
|
||
} | ||
|
||
object OptionT extends OptionTMonadInstance { | ||
def pure[F[_]: Applicative, A](a: => A): OptionT[F, A] = | ||
OptionT(Applicative[F].pure(a.some)) | ||
|
||
def some[F[_]: Applicative, A](a: => A): OptionT[F, A] = pure(a) | ||
|
||
def none[F[_]: Applicative, A]: OptionT[F, A] = | ||
OptionT(Applicative[F].pure(None)) | ||
} | ||
|
||
private trait OptionTFunctor[F[_]] extends Functor[OptionT[F, *]] { | ||
implicit def F: Functor[F] | ||
|
||
override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = | ||
fa.map(f)(F) | ||
} | ||
|
||
private trait OptionTApplicative[F[_]] extends Applicative[OptionT[F, *]] with OptionTFunctor[F] { | ||
implicit def F: Applicative[F] | ||
|
||
override def pure[A](a: => A): OptionT[F, A] = OptionT(F.pure(a.some)) | ||
|
||
override def ap[A, B](fa: => OptionT[F, A])(fab: => OptionT[F, A => B]): OptionT[F, B] = | ||
fa.ap(fab)(F) | ||
} | ||
|
||
private trait OptionTMonad[F[_]] extends Monad[OptionT[F, *]] with OptionTApplicative[F] { | ||
implicit def F: Monad[F] | ||
} | ||
|
||
sealed abstract class OptionTFunctorInstance { | ||
implicit def OptionTFunctor[F[_]](implicit F0: Functor[F]): Functor[OptionT[F, *]] = new OptionTFunctor[F] { | ||
override implicit val F: Functor[F] = F0 | ||
} | ||
} | ||
|
||
sealed abstract class OptionTApplicativeInstance extends OptionTFunctorInstance { | ||
implicit def OptionTApplicative[F[_]](implicit F0: Applicative[F]): Applicative[OptionT[F, *]] = | ||
new OptionTApplicative[F] { | ||
override implicit val F: Applicative[F] = F0 | ||
} | ||
} | ||
|
||
sealed abstract class OptionTMonadInstance extends OptionTApplicativeInstance { | ||
|
||
implicit def OptionTMonad[F[_]](implicit F0: Monad[F]): Monad[OptionT[F, *]] = new OptionTMonad[F] { | ||
|
||
override implicit val F: Monad[F] = F0 | ||
|
||
override def flatMap[A, B](ma: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = | ||
ma.flatMap(f)(F) | ||
|
||
} | ||
|
||
implicit def OptionTEqual[F[_], A](implicit EQ: Equal[F[Option[A]]]): Equal[OptionT[F, A]] = | ||
new Equal[OptionT[F, A]] { | ||
override def equal(x: OptionT[F, A], y: OptionT[F, A]): Boolean = | ||
EQ.equal(x.run, y.run) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package just.fp | ||
|
||
import hedgehog._ | ||
import hedgehog.runner._ | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2019-09-19 | ||
*/ | ||
object OptionTSpec extends Properties { | ||
|
||
type EitherStringOr[A] = Either[String, A] | ||
type OptionTEither[A] = OptionT[EitherStringOr, A] | ||
type OptionTId[A] = OptionT[Id, A] | ||
|
||
override def tests: List[Test] = List( | ||
property("testOptionTFunctorLaws", OptionTFunctorLaws.laws) | ||
, property("testOptionTApplicativeLaws", OptionTApplicativeLaws.laws) | ||
, property("testOptionTMonadLaws", OptionTMonadLaws.laws) | ||
, property("testOptionTIdFunctorLaws", OptionTIdFunctorLaws.laws) | ||
, property("testOptionTIdApplicativeLaws", OptionTIdApplicativeLaws.laws) | ||
, property("testOptionTIdMonadLaws", OptionTIdMonadLaws.laws) | ||
) | ||
|
||
object OptionTFunctorLaws { | ||
|
||
def genOptionT: Gen[OptionTEither[Int]] = Gens.genOptionT[EitherStringOr, Int](Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.functorLaws.laws[OptionTEither]( | ||
genOptionT | ||
, Gens.genIntToInt | ||
) | ||
} | ||
|
||
object OptionTApplicativeLaws { | ||
|
||
def genOptionT: Gen[OptionTEither[Int]] = Gens.genOptionT[EitherStringOr, Int](Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.applicativeLaws.laws[OptionTEither]( | ||
genOptionT | ||
, Gens.genIntFromMinToMax | ||
, Gens.genIntToInt | ||
) | ||
} | ||
|
||
object OptionTMonadLaws { | ||
|
||
def genOptionT: Gen[OptionTEither[Int]] = Gens.genOptionT[EitherStringOr, Int](Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.monadLaws.laws[OptionTEither]( | ||
genOptionT | ||
, Gens.genIntFromMinToMax | ||
, Gens.genIntToInt | ||
, Gens.genAToMonadA(Gens.genIntToInt) | ||
) | ||
} | ||
|
||
object OptionTIdFunctorLaws { | ||
|
||
def genOptionT: Gen[OptionTId[Int]] = Gens.genOptionT(Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.functorLaws.laws[OptionTId]( | ||
genOptionT | ||
, Gens.genIntToInt | ||
) | ||
} | ||
|
||
object OptionTIdApplicativeLaws { | ||
|
||
def genOptionT: Gen[OptionTId[Int]] = Gens.genOptionT(Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.applicativeLaws.laws[OptionTId]( | ||
genOptionT | ||
, Gens.genIntFromMinToMax | ||
, Gens.genIntToInt | ||
) | ||
} | ||
|
||
object OptionTIdMonadLaws { | ||
|
||
def genOptionT: Gen[OptionTId[Int]] = Gens.genOptionT(Gens.genIntFromMinToMax) | ||
|
||
def laws: Property = | ||
Specs.monadLaws.laws[OptionTId]( | ||
genOptionT | ||
, Gens.genIntFromMinToMax | ||
, Gens.genIntToInt | ||
, Gens.genAToMonadA(Gens.genIntToInt) | ||
) | ||
} | ||
|
||
} |