Skip to content

Commit

Permalink
Tests on Algebras
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefanos Stefanou committed Nov 3, 2023
1 parent 7489375 commit 2b3f034
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/main/scala/mycats/algebras/Validated.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package mycats.algebras
import mycats.examples.common.Utils.NonEmptyList

sealed trait Validated[+E,+A] extends Product with Serializable{}
case class Valid[+A](valid:A) extends Validated[Nothing,A]
case class Invalid[+E](invalid:E) extends Validated[E,Nothing]


7 changes: 7 additions & 0 deletions src/main/scala/mycats/algebras/ValidatedNel.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mycats.algebras
import mycats.examples.common.Utils.NonEmptyList

object ValidatedNel {
type ValidatedNel[A] = Validated[NonEmptyList[String],A]

}
12 changes: 3 additions & 9 deletions src/main/scala/mycats/examples/ValidatedExample.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
package mycats.examples

import mycats.algebras.ValidatedNel.ValidatedNel
import mycats.algebras.{Invalid, Valid, Validated}
import mycats.examples.common.Expense
import mycats.examples.common.Utils.NonEmptyList
import mycats.instances.ValidatedInstances.validatedApplicativeErrorInstance
import mycats.lib.obj.Semigroup
import mycats.instances.ValidatedNelInstances.{semigroupOfNonEmptyList, validatedNelInstance}
import mycats.lib.syntax.ApplySyntax.Tuple2ApplyOps
import mycats.instances.ValidatedNelInstances._
object ValidatedExample {
type ValidatedNel[A] = Validated[NonEmptyList[String],A]
val validatedNelInstance = validatedApplicativeErrorInstance[NonEmptyList[String],Long]
implicit def semigroupOfNonEmptyList[A]:Semigroup[NonEmptyList[A]] = new Semigroup[NonEmptyList[A]] {
override def combine(x: NonEmptyList[A], y: NonEmptyList[A]): NonEmptyList[A] = x.appendedAll(y)
}


private def expenseIdValidation(expenseID:Long):ValidatedNel[Long]=
if(expenseID<=0) Invalid(List("Expense ID cannot be negative")) else Valid(expenseID)

Expand Down
4 changes: 0 additions & 4 deletions src/main/scala/mycats/examples/common/Utils.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
package mycats.examples.common




object Utils{
type NonEmptyList[A] = List[A] //Fake nonEmptyList
}
2 changes: 1 addition & 1 deletion src/main/scala/mycats/instances/OptionInstances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object OptionInstances {
case _ => None
}
}
override def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] = ???
override def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] = map(product(ff,fa))(tu2=>tu2._1(tu2._2))
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match {
case Some(value) => Some(f(value))
case None => None
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/mycats/instances/ValidatedInstances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import mycats.lib.obj.Semigroup

object ValidatedInstances {

implicit def validatedApplicativeErrorInstance[E,A](implicit semigroupOfE:Semigroup[E])=
implicit def validatedApplicativeErrorInstance[E](implicit semigroupOfE:Semigroup[E])=
new ApplicativeError[({type Valid[C] = Validated[E, C]})#Valid,E] with BiFunctor[Validated]{
override def raiseError[A](e: E): Validated[E, A] = Invalid(e)
override def handleErrorWith[A](fa: Validated[E, A])(f: E => Validated[E, A]): Validated[E, A] = fa match {
Expand Down Expand Up @@ -34,3 +34,4 @@ object ValidatedInstances {
}
}
}

13 changes: 13 additions & 0 deletions src/main/scala/mycats/instances/ValidatedNelInstances.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mycats.instances
import mycats.algebras.Validated
import mycats.examples.common.Utils.NonEmptyList
import mycats.instances.ValidatedInstances.validatedApplicativeErrorInstance
import mycats.lib.morphisms.bi.BiFunctor
import mycats.lib.morphisms.error.ApplicativeError
import mycats.lib.obj.Semigroup

object ValidatedNelInstances {
implicit def semigroupOfNonEmptyList[A]:Semigroup[NonEmptyList[A]] = (x: NonEmptyList[A], y: NonEmptyList[A]) => x.appendedAll(y)
implicit val validatedNelInstance: ApplicativeError[({type Valid[C] = Validated[NonEmptyList[String], C]})#Valid, NonEmptyList[String]] with BiFunctor[Validated] = validatedApplicativeErrorInstance[NonEmptyList[String]]

}
3 changes: 3 additions & 0 deletions src/main/scala/mycats/lib/syntax/ApplySyntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ object ApplySyntax {
def mapN[C](f:Tuple2[A,B]=>C)(implicit apply:Apply[F]): F[C] =
apply.map[(A,B),C](apply.product[A,B](ab._1,ab._2))(f)
}
implicit class applySyntaxOps[F[_],A](fa:F[A]){
def ap[B](f:F[A=>B])(implicit applyF:Apply[F]):F[B]=applyF.ap(f)(fa)
}
}
9 changes: 9 additions & 0 deletions src/main/scala/mycats/lib/syntax/SemigroupSyntax.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mycats.lib.syntax
import mycats.lib.obj.Semigroup

object SemigroupSyntax {
implicit class SemigroupSyntaxOps[A](va:A){
def combine(vb:A)(implicit semigroupInstance:Semigroup[A]):A = semigroupInstance.combine(va,vb)
}

}
Binary file modified src/test/scala/.DS_Store
Binary file not shown.
Binary file added src/test/scala/mycats/.DS_Store
Binary file not shown.
23 changes: 11 additions & 12 deletions src/test/scala/mycats/instances/OptionInstancesSpec.scala
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
package mycats.instances

import mycats.instances.OptionInstances._
import mycats.lib.syntax.ApplySyntax.applySyntaxOps
import mycats.lib.syntax.InvariantSyntax.InvariantSyntaxOps
import mycats.lib.syntax.SemigroupalSyntax.SemigroupalSyntaxOps
class OptionInstancesSpec extends org.scalatest.funsuite.AnyFunSuite {
val opt1:Option[String]=Some("The meaning of life")
val opt2:Option[Int]=Some(42)
val opt3:Option[String]=Some("42")

test("Semigroupal Optional: Product should return F[(A,B}]") {
val opt1:Option[String]=Some("The meaning of life")
val opt2:Option[Int]=Some(42)
assert((opt1).product(opt2)==Some(("The meaning of life",42)),"product does not return F[(A,B)] on Option Algebra")
assert((opt1).product(opt2)==Some(("The meaning of life",42)),"Semigroupal's .product does not return F[(A,B)] when applied in Option Algebra")
}
//Functor, ap , imap
test("Invariant Optional: imap should return F[B] given A=>B and B=>A") {
val opt1:Option[String]=Some("The meaning of life")
val opt2:Option[Int]=Some(42)
assert((opt1).product(opt2)==Some(("The meaning of life",42)),"product does not return F[(A,B)] on Option Algebra")
assert(opt3.imap(Integer.valueOf)(String.valueOf)==Some(42),"Invariant Functor's .imap does not transform context when applied in Option Algebra")
}

test("Functor Optional: map should transform context") {
val opt1:Option[String]=Some("The meaning of life")
val opt2:Option[Int]=Some(42)
assert((opt1).product(opt2)==Some(("The meaning of life",42)),"product does not return F[(A,B)] on Option Algebra")
assert(opt3.map(Integer.valueOf)==Some(42),"Functor's .map does not transform context when applied in Option Algebra")
}
test("Apply Optional: ap should do its ap thing") {
val opt1:Option[String]=Some("The meaning of life")
val opt2:Option[Int]=Some(42)
assert((opt1).product(opt2)==Some(("The meaning of life",42)),"product does not return F[(A,B)] on Option Algebra")
val fnInContext: Option[Int=>String] = Some(String.valueOf)
assert(opt2.ap(fnInContext)==opt3,"product does not return F[(A,B)] on Option Algebra")
}
}
37 changes: 37 additions & 0 deletions src/test/scala/mycats/instances/ValidatedInstancesSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package mycats.instances

import mycats.algebras.ValidatedNel.ValidatedNel
import mycats.algebras.{Invalid, Valid, Validated}
import mycats.examples.common.Expense
import mycats.examples.common.Utils.NonEmptyList
import mycats.instances.ValidatedNelInstances.validatedNelInstance
import mycats.lib.syntax.ApplySyntax.Tuple2ApplyOps
import mycats.instances.ValidatedNelInstances._
import mycats.lib.syntax.SemigroupSyntax._
class ValidatedNelInstancesSpec extends org.scalatest.funsuite.AnyFunSuite {
test("Semigroup NonEmptyList: combine should behave as expected") {
val nonEmpty1:NonEmptyList[Int] = 1::2::3::Nil
val nonEmpty2:NonEmptyList[Int] = 4::5::6::Nil
assert(nonEmpty1.combine(nonEmpty2)==(1 to 6).toList,"Semigroupal's .product does not return F[(A,B)] when applied in Option Algebra")
}
//BiFunctor (bimap)
//ApplicativeError (raiseError,handleErrorWith,handleError)
//Applicative (pure)
//Apply (ap)
//Functor (map)
//Arity (map2,product2)
//Invariant(imap)
//Semigroupal(product)



private def expenseIdValidation(expenseID:Long):ValidatedNel[Long]=
if(expenseID<=0) Invalid(List("Expense ID cannot be negative")) else Valid(expenseID)

private def expenseAmountValidation(expenseAmount:Double):ValidatedNel[Double]=
if(expenseAmount<=0) Invalid(List("Expense amount cannot be negative")) else Valid(expenseAmount)

private def mkExpenseMapN(id:Long,amount:Double):ValidatedNel[Expense] =
(expenseIdValidation(id), expenseAmountValidation(amount)).mapN((Expense.apply _).tupled)

}

0 comments on commit 2b3f034

Please sign in to comment.