You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
46 lines
1.7 KiB
46 lines
1.7 KiB
package green.thisfieldwas.embracingnondeterminism.data
|
|
|
|
import green.thisfieldwas.embracingnondeterminism.syntax.functor._
|
|
import green.thisfieldwas.embracingnondeterminism.util._
|
|
import org.scalacheck.Arbitrary.arbitrary
|
|
|
|
import scala.reflect.runtime.universe.TypeTag
|
|
|
|
/** Include this trait to check that your context `F[_]` conforms to the Functor
|
|
* laws. Provide an implicit instance of `LiftedGen` for your context `F[_]`
|
|
* and call the `checkFunctorLaws[F]()` function within your laws spec to
|
|
* register the associated properties.
|
|
*/
|
|
trait FunctorLaws { this: Laws =>
|
|
|
|
/** Defined per Functor Laws taken from the Haskell wiki:
|
|
* [[https://wiki.haskell.org/Functor#Functor_Laws]]
|
|
*
|
|
* @param TT
|
|
* The type tag of the context
|
|
* @tparam F
|
|
* The context type being tested
|
|
*/
|
|
def checkFunctorLaws[F[_]: Functor: LiftedGen]()(implicit TT: TypeTag[F[Any]]): Unit = {
|
|
// Mapping the identity function produces the same result as applying the
|
|
// identity function to the context.
|
|
property(s"${TT.name} Functor preserves identity functions") {
|
|
forAll(arbitrary[Double].lift) { fa =>
|
|
// Haskell: fmap id = id
|
|
fa.map(identity) mustBe identity(fa)
|
|
}
|
|
}
|
|
// Mapping composed functions g after f produces the same result as
|
|
// separately mapping f and then g.
|
|
property(s"${TT.name} Functor preserves function composition") {
|
|
forAll(for {
|
|
fa <- arbitrary[Double].lift
|
|
f <- arbitrary[Double => String]
|
|
g <- arbitrary[String => Int]
|
|
} yield (fa, f, g)) { case (fa, f, g) =>
|
|
// Haskell: fmap (f . g) == fmap f . fmap g
|
|
fa.map(g compose f) mustBe fa.map(f).map(g)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|