Skip to content

Commit

Permalink
Close #61 - Rewrite Functor, Applicative and Monad instances
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-lee committed Sep 19, 2019
1 parent a3a725d commit 3682772
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 68 deletions.
4 changes: 2 additions & 2 deletions src/main/scala-2.10_2.11/just/fp/compat/EitherCompat.scala
Expand Up @@ -6,10 +6,10 @@ package just.fp.compat
*/
object EitherCompat {

@inline final def map[A, B, C](either: Either[A, B], f: B => C): Either[A, C] =
@inline final def map[A, B, C](either: Either[A, B])(f: B => C): Either[A, C] =
either.right.map(f)

@inline final def flatMap[A, B, C](either: Either[A, B], f: B => Either[A, C]): Either[A, C] =
@inline final def flatMap[A, B, C](either: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
either.right.flatMap(f)

}
4 changes: 2 additions & 2 deletions src/main/scala-2.12_2.13/just/fp/compat/EitherCompat.scala
Expand Up @@ -6,10 +6,10 @@ package just.fp.compat
*/
object EitherCompat {

@inline final def map[A, B, C](either: Either[A, B], f: B => C): Either[A, C] =
@inline final def map[A, B, C](either: Either[A, B])(f: B => C): Either[A, C] =
either.map(f)

@inline final def flatMap[A, B, C](either: Either[A, B], f: B => Either[A, C]): Either[A, C] =
@inline final def flatMap[A, B, C](either: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
either.flatMap(f)

}
89 changes: 73 additions & 16 deletions src/main/scala/just/fp/Applicative.scala
Expand Up @@ -52,20 +52,77 @@ trait Applicative[F[_]] extends Functor[F] {
def applicativeLaw: ApplicativeLaw = new ApplicativeLaw {}
}

object Applicative {

implicit def applicativeEither[L]: Applicative[Either[L, ?]] =
new Applicative[Either[L, ?]] {
def pure[A](a: => A): Either[L, A] = Right(a)

def ap[A, B](fa: => Either[L, A])(fb: => Either[L, A => B]): Either[L, B] =
(fa, fb) match {
case (Right(a), Right(bf)) =>
Right(bf(a))
case (Left(l), _) =>
Left(l)
case (_, Left(l)) =>
Left(l)
}
object Applicative extends ApplicativeInstances

private[fp] trait ApplicativeInstances
extends EitherApplicativeInstance
with ListApplicativeInstance
with VectorApplicativeInstance
with FutureApplicativeInstance

private[fp] trait EitherApplicative[A] extends Applicative[Either[A, ?]] with EitherFunctor[A] {

override def pure[B](b: => B): Either[A, B] = Right(b)

override def ap[B, C](fb: => Either[A, B])(fc: => Either[A, B => C]): Either[A, C] =
(fb, fc) match {
case (Right(b), Right(cf)) =>
Right(cf(b))
case (Left(a), _) =>
Left(a)
case (_, Left(a)) =>
Left(a)
}
}
}

private[fp] trait ListApplicative extends Applicative[List] with ListFunctor {

override def pure[A](a: => A): List[A] = List(a)

override def ap[A, B](fa: => List[A])(fab: => List[A => B]): List[B] =
fab.flatMap(f => fa.map(f))
}

private[fp] trait VectorApplicative extends Applicative[Vector] with VectorFunctor {

override def pure[A](a: => A): Vector[A] = Vector(a)

override def ap[A, B](fa: => Vector[A])(fab: => Vector[A => B]): Vector[B] =
fab.flatMap(f => fa.map(f))
}

import scala.concurrent.Future
private[fp] trait FutureApplicative extends Applicative[Future] with FutureFunctor {
import scala.concurrent.ExecutionContext

override implicit def executor: ExecutionContext

override def pure[A](a: => A): Future[A] = Future(a)

override def ap[A, B](fa: => Future[A])(fab: => Future[A => B]): Future[B] =
fab.flatMap(f => fa.map(f))
}

private[fp] trait EitherApplicativeInstance extends EitherFunctorInstance {
implicit def applicativeEither[A]: Applicative[Either[A, ?]] =
new EitherApplicative[A] {}

}

private[fp] trait ListApplicativeInstance extends ListFunctorInstance {
implicit val applicativeList: Applicative[List] = new ListApplicative {}
}

private[fp] trait VectorApplicativeInstance extends VectorFunctorInstance {
implicit val applicativeVector: Applicative[Vector] = new VectorApplicative {}
}

private[fp] trait FutureApplicativeInstance extends FutureFunctorInstance {
import scala.concurrent.ExecutionContext

@SuppressWarnings(Array("org.wartremover.warts.ImplicitParameter"))
implicit def applicativeFuture(implicit executor0: ExecutionContext): Applicative[Future] =
new FutureApplicative {
implicit def executor: ExecutionContext = executor0
}
}
2 changes: 1 addition & 1 deletion src/main/scala/just/fp/EitherT.scala
Expand Up @@ -9,7 +9,7 @@ import just.fp.compat.EitherCompat
*/
final case class EitherT[F[_], A, B](run: F[Either[A, B]]) {
def map[C](f: B => C)(implicit F: Functor[F]): EitherT[F, A, C] =
EitherT(F.map(run)(EitherCompat.map(_, f)))
EitherT(F.map(run)(EitherCompat.map(_)(f)))

def flatMap[C](f: B => EitherT[F, A, C])(implicit M: Monad[F]): EitherT[F, A, C] =
EitherT(
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/just/fp/Equal.scala
Expand Up @@ -59,6 +59,7 @@ object Equal
with StringEqualInstance
with BigIntEqualInstance
with BigDecimalEqualInstance
with EitherEqualInstance
with ListEqualInstance
with VectorEqualInstance

Expand All @@ -69,6 +70,10 @@ trait NatualEqual {
}
}

trait EitherEqualInstance {
implicit def eitherEqual[A, B]: Equal[Either[A, B]] = Equal.equalA[Either[A, B]]
}

trait ListEqualInstance {
implicit def listEqual[A]: Equal[List[A]]= Equal.equalA[List[A]]
}
Expand Down
60 changes: 44 additions & 16 deletions src/main/scala/just/fp/Functor.scala
@@ -1,6 +1,6 @@
package just.fp

import scala.concurrent.ExecutionContext
import just.fp.compat.EitherCompat

/**
* @author Kevin Lee
Expand Down Expand Up @@ -28,26 +28,54 @@ trait Functor[F[_]] {
def functorLaw: FunctorLaw = new FunctorLaw {}
}

object Functor extends ListFunctor with VectorFunctor with FutureFunctor
object Functor extends FunctorInstances

trait ListFunctor {
implicit val listFunctor: Functor[List] = new Functor[List] {
def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
}
private[fp] trait FunctorInstances
extends EitherFunctorInstance
with ListFunctorInstance
with VectorFunctorInstance
with FutureFunctorInstance

private[fp] trait EitherFunctor[A] extends Functor[Either[A, ?]] {
override def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] =
EitherCompat.map(fa)(f)
}

trait VectorFunctor {
implicit val vectorFunctor: Functor[Vector] = new Functor[Vector] {
def map[A, B](fa: Vector[A])(f: A => B): Vector[B] = fa.map(f)
}
private[fp] trait ListFunctor extends Functor[List] {
override def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
}

private[fp] trait VectorFunctor extends Functor[Vector] {
override def map[A, B](fa: Vector[A])(f: A => B): Vector[B] = fa.map(f)
}

trait FutureFunctor {
import scala.concurrent.Future
import scala.concurrent.Future
private[fp] trait FutureFunctor extends Functor[Future] {
import scala.concurrent.ExecutionContext
implicit def executor: ExecutionContext

override def map[A, B](fa: Future[A])(f: A => B): Future[B] =
fa.map(f)(executor)
}

private[fp] trait EitherFunctorInstance {
implicit def eitherFunctor[A]: Functor[Either[A, ?]] = new EitherFunctor[A] {}
}

private[fp] trait ListFunctorInstance {
implicit val listFunctor: Functor[List] = new ListFunctor {}
}

private[fp] trait VectorFunctorInstance {
implicit val vectorFunctor: Functor[Vector] = new VectorFunctor {}
}

private[fp] trait FutureFunctorInstance {
import scala.concurrent.ExecutionContext

@SuppressWarnings(Array("org.wartremover.warts.ImplicitParameter"))
implicit def futureFunctor(implicit ex: ExecutionContext): Functor[Future] =
new Functor[Future] {
def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f)
implicit def futureFunctor(implicit executor0: ExecutionContext): Functor[Future] =
new FutureFunctor {
override implicit def executor: ExecutionContext = executor0
}
}
}
90 changes: 61 additions & 29 deletions src/main/scala/just/fp/Monad.scala
@@ -1,5 +1,7 @@
package just.fp

import just.fp.compat.EitherCompat

/**
* @author Kevin Lee
* @since 2019-03-16
Expand Down Expand Up @@ -39,48 +41,78 @@ trait Monad[M[_]] extends Applicative[M] {
def monadLaw: MonadLaw = new MonadLaw {}
}

object Monad extends IdInstance with ListMonadInstance with VectorMonadInstance with FutureMonadInstance
object Monad extends MonadInstances

trait IdInstance {
private[fp] trait MonadInstances
extends IdInstance
with EitherMonadInstance
with ListMonadInstance
with VectorMonadInstance
with FutureMonadInstance

implicit val idMonad: Monad[Id] = new Monad[Id] {
private[fp] trait IdInstance {

def flatMap[A, B](a: A)(f: A => B): B = f(a)
implicit val idInstance: Functor[Id] with Applicative[Id] with Monad[Id] =
new Functor[Id] with Applicative[Id] with Monad[Id] {
override def pure[A](a: => A): Id[A] = a

def pure[A](a: => A): A = a
}
override def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] = f(ma)
}
}

private[fp] trait EitherMonad[A] extends Monad[Either[A, ?]] with EitherApplicative[A] {

override def flatMap[B, C](ma: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
EitherCompat.flatMap(ma)(f)

override def pure[B](b: => B): Either[A, B] = Right(b)
}

trait ListMonadInstance {
implicit val listMonad: Monad[List] = new Monad[List] {
override def flatMap[A, B](ma: List[A])(f: A => List[B]): List[B] =
ma.flatMap(f)
private[fp] trait ListMonad extends Monad[List] with ListApplicative {
override def flatMap[A, B](ma: List[A])(f: A => List[B]): List[B] =
ma.flatMap(f)

override def pure[A](a: => A): List[A] =
List(a)
}
override def pure[A](a: => A): List[A] = List(a)
}

trait VectorMonadInstance {
implicit val vectorMonad: Monad[Vector] = new Monad[Vector] {
override def flatMap[A, B](ma: Vector[A])(f: A => Vector[B]): Vector[B] =
ma.flatMap(f)
private[fp] trait VectorMonad extends Monad[Vector] with VectorApplicative {

override def pure[A](a: => A): Vector[A] =
Vector(a)
}
override def flatMap[A, B](ma: Vector[A])(f: A => Vector[B]): Vector[B] =
ma.flatMap(f)

override def pure[A](a: => A): Vector[A] = Vector(a)
}

trait FutureMonadInstance {
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.Future
private[fp] trait FutureMonad extends Monad[Future] with FutureApplicative {
import scala.concurrent.ExecutionContext

@SuppressWarnings(Array("org.wartremover.warts.ImplicitParameter"))
implicit def futureMonad(implicit ex: ExecutionContext): Monad[Future] = new Monad[Future] {
override def flatMap[A, B](ma: Future[A])(f: A => Future[B]): Future[B] =
ma.flatMap(f)
override implicit def executor: ExecutionContext

override def pure[A](a: => A): Future[A] =
Future(a)
}
override def flatMap[A, B](ma: Future[A])(f: A => Future[B]): Future[B] =
ma.flatMap(f)

override def pure[A](a: => A): Future[A] = Future(a)
}

private[fp] trait EitherMonadInstance extends EitherApplicativeInstance {
implicit def eitherMonad[A]: Monad[Either[A, ?]] = new EitherMonad[A] {}
}

private[fp] trait ListMonadInstance extends ListApplicativeInstance {
implicit val listMonad: Monad[List] = new ListMonad {}
}

private[fp] trait VectorMonadInstance extends VectorApplicativeInstance {
implicit val vectorMonad: Monad[Vector] = new VectorMonad {}
}

private[fp] trait FutureMonadInstance extends FutureApplicativeInstance {
import scala.concurrent.ExecutionContext

@SuppressWarnings(Array("org.wartremover.warts.ImplicitParameter"))
implicit def futureMonad(implicit executor0: ExecutionContext): Monad[Future] =
new FutureMonad {
override implicit def executor: ExecutionContext = executor0
}
}
2 changes: 1 addition & 1 deletion src/main/scala/just/fp/WriterT.scala
Expand Up @@ -56,7 +56,7 @@ sealed abstract class WriterTMonadInstance extends WriterInstance {
sealed abstract class WriterInstance {

implicit def writerMonad[W](implicit S0: Monoid[W]): Monad[WriterT[Id, W, ?]] = new Monad[WriterT[Id, W, ?]] {
implicit val F: Monad[Id] = idMonad
implicit val F: Monad[Id] = idInstance
implicit val S: Monoid[W] = S0

def flatMap[A, B](fa: WriterT[Id, W, A])(f: A => WriterT[Id, W, B]): WriterT[Id, W, B] =
Expand Down
13 changes: 12 additions & 1 deletion src/test/scala/just/fp/FunctorSpec.scala
Expand Up @@ -9,11 +9,22 @@ import hedgehog.runner._
*/
object FunctorSpec extends Properties {
override def tests: List[Test] = List(
property("testListFunctorLaws", ListFunctorLaws.laws)
property("testEitherFunctorLaws", EitherFunctorLaws.laws)
, property("testListFunctorLaws", ListFunctorLaws.laws)
, property("testVectorFunctorLaws", VectorFunctorLaws.laws)
, property("testFutureFunctorLaws", FutureFunctorLaws.laws)
)

object EitherFunctorLaws {
def genEither: Gen[Either[String, Int]] = Gens.genEither(Gens.genUnicodeString ,Gens.genIntFromMinToMax)

def laws: Property =
Specs.functorLaws.laws[Either[String, ?]](
genEither
, Gens.genIntToInt
)
}

object ListFunctorLaws {
def genList: Gen[List[Int]] = Gens.genList(Gens.genIntFromMinToMax, 20)

Expand Down
7 changes: 7 additions & 0 deletions src/test/scala/just/fp/Gens.scala
Expand Up @@ -216,4 +216,11 @@ object Gens {
genInt: Gen[A])(
implicit ex: scala.concurrent.ExecutionContext): Gen[scala.concurrent.Future[A]] =
genInt.map(n => scala.concurrent.Future(n))

def genOption[A](genA: Gen[A]): Gen[Option[A]] =
genA.option

def genEither[A, B](genA: Gen[A], genB: Gen[B]): Gen[Either[A, B]] =
Gen.choice1(genA.map(Left(_)), genB.map(Right(_)))

}

0 comments on commit 3682772

Please sign in to comment.