Scalaz(9)- typeclass:checking instance abiding the laws

  在前几篇关于Functor和Applilcative typeclass的讨论中我们自定义了一个类型Configure,Configure类型的定义是这样的:

case class Configure[+A](get: A)
object Configure {
    implicit val configFunctor = new Functor[Configure] {
        def map[A,B](ca: Configure[A])(f: A => B): Configure[B] = Configure(f(ca.get))
    }
    implicit val configApplicative = new Applicative[Configure] {
        def point[A](a: => A) = Configure(a)
        def ap[A,B](ca: => Configure[A])(cfab: => Configure[A => B]): Configure[B] = cfab map {fab => fab(ca.get)}
    }
}

通过定义了Configure类型的Functor和Applicative隐式实例(implicit instance),我们希望Configure类型既是一个Functor也是一个Applicative。那么怎么才能证明这个说法呢?我们只要证明Configure类型的实例能遵循它所代表的typeclass操作定律就行了。Scalaz为大部分typeclass提供了测试程序(scalacheck properties)。在scalaz/scalacheck-binding/src/main/scala/scalaz/scalacheck/scalazProperties.scala里我们可以发现有关functor scalacheck properties:

 object functor {
    def identity[F[_], X](implicit F: Functor[F], afx: Arbitrary[F[X]], ef: Equal[F[X]]) =
      forAll(F.functorLaw.identity[X] _)

    def composite[F[_], X, Y, Z](implicit F: Functor[F], af: Arbitrary[F[X]], axy: Arbitrary[(X => Y)],
                                   ayz: Arbitrary[(Y => Z)], ef: Equal[F[Z]]) =
      forAll(F.functorLaw.composite[X, Y, Z] _)

    def laws[F[_]](implicit F: Functor[F], af: Arbitrary[F[Int]], axy: Arbitrary[(Int => Int)],
                   ef: Equal[F[Int]]) = new Properties("functor") {
      include(invariantFunctor.laws[F])
      property("identity") = identity[F, Int]
      property("composite") = composite[F, Int, Int, Int]
    }
  }

可以看到:functor.laws[F[_]]主要测试了identity, composite及invariantFunctor的properties。在scalaz/Functor.scala文件中定义了这几条定律:

 trait FunctorLaw extends InvariantFunctorLaw {
    /** The identity function, lifted, is a no-op. */
    def identity[A](fa: F[A])(implicit FA: Equal[F[A]]): Boolean = FA.equal(map(fa)(x => x), fa)

    /**
     * A series of maps may be freely rewritten as a single map on a
     * composed function.
     */
    def composite[A, B, C](fa: F[A], f1: A => B, f2: B => C)(implicit FC: Equal[F[C]]): Boolean = FC.equal(map(map(fa)(f1))(f2), map(fa)(f2 compose f1))
  }
 。。。

我们在下面试着对那个Configure类型进行Functor实例和Applicative实例的测试:

import scalaz._
import Scalaz._
import shapeless._
import scalacheck.ScalazProperties._
import scalacheck.ScalazArbitrary._
import scalacheck.ScalaCheckBinding._
import org.scalacheck.{Gen, Arbitrary}
implicit def cofigEqual[A]: Equal[Configure[A]] = Equal.equalA
                                                  //> cofigEqual: [A#2921073]=> scalaz#31.Equal#41646[Exercises#29.ex1#59011.Confi
                                                  //| gure#2921067[A#2921073]]
implicit def configArbi[A](implicit a: Arbitrary[A]): Arbitrary[Configure[A]] =
   a map { b => Configure(b) }                    //> configArbi: [A#2921076](implicit a#2921242: org#15.scalacheck#121951.Arbitra
                                                  //| ry#122597[A#2921076])org#15.scalacheck#121951.Arbitrary#122597[Exercises#29.
                                                  //| ex1#59011.Configure#2921067[A#2921076]]

除了需要的import外还必须定义Configure类型的Equal实例以及任意测试数据产生器(test data generator)configArbi[A]。我们先测试Functor属性:

functor.laws[Configure].check                     //> 
+ functor.invariantFunctor.identity: OK, passed 100 tests.
                                                  //| 
+ functor.invariantFunctor.composite: OK, passed 100 tests.
                                                  //| 
+ functor.identity: OK, passed 100 tests.
                                                  //| 
+ functor.composite: OK, passed 100 tests.

成功通过Functor定律测试。

再看看Applicative的scalacheck property:scalaz/scalacheck/scalazProperties.scala

 object applicative {
    def identity[F[_], X](implicit f: Applicative[F], afx: Arbitrary[F[X]], ef: Equal[F[X]]) =
      forAll(f.applicativeLaw.identityAp[X] _)

    def homomorphism[F[_], X, Y](implicit ap: Applicative[F], ax: Arbitrary[X], af: Arbitrary[X => Y], e: Equal[F[Y]]) =
      forAll(ap.applicativeLaw.homomorphism[X, Y] _)

    def interchange[F[_], X, Y](implicit ap: Applicative[F], ax: Arbitrary[X], afx: Arbitrary[F[X => Y]], e: Equal[F[Y]]) =
      forAll(ap.applicativeLaw.interchange[X, Y] _)

    def mapApConsistency[F[_], X, Y](implicit ap: Applicative[F], ax: Arbitrary[F[X]], afx: Arbitrary[X => Y], e: Equal[F[Y]]) =
      forAll(ap.applicativeLaw.mapLikeDerived[X, Y] _)

    def laws[F[_]](implicit F: Applicative[F], af: Arbitrary[F[Int]],
                   aff: Arbitrary[F[Int => Int]], e: Equal[F[Int]]) = new Properties("applicative") {
      include(ScalazProperties.apply.laws[F])
      property("identity") = applicative.identity[F, Int]
      property("homomorphism") = applicative.homomorphism[F, Int, Int]
      property("interchange") = applicative.interchange[F, Int, Int]
      property("map consistent with ap") = applicative.mapApConsistency[F, Int, Int]
    }
  }

applicative.laws定义了4个测试Property再加上apply的测试property。这些定律(laws)在scalaz/Applicative.scala里定义了:

  trait ApplicativeLaw extends ApplyLaw {
    /** `point(identity)` is a no-op. */
    def identityAp[A](fa: F[A])(implicit FA: Equal[F[A]]): Boolean =
      FA.equal(ap(fa)(point((a: A) => a)), fa)

    /** `point` distributes over function applications. */
    def homomorphism[A, B](ab: A => B, a: A)(implicit FB: Equal[F[B]]): Boolean =
      FB.equal(ap(point(a))(point(ab)), point(ab(a)))

    /** `point` is a left and right identity, F-wise. */
    def interchange[A, B](f: F[A => B], a: A)(implicit FB: Equal[F[B]]): Boolean =
      FB.equal(ap(point(a))(f), ap(f)(point((f: A => B) => f(a))))

    /** `map` is like the one derived from `point` and `ap`. */
    def mapLikeDerived[A, B](f: A => B, fa: F[A])(implicit FB: Equal[F[B]]): Boolean =
      FB.equal(map(fa)(f), ap(fa)(point(f)))
  }

再测试一下Configure类型是否也遵循Applicative定律:

applicative.laws[Configure].check                 //> 
+ applicative.apply.functor.invariantFunctor.identity: OK, passed 100 tests
                                                  //| 
                                                  //|   .
                                                  //| 
+ applicative.apply.functor.invariantFunctor.composite: OK, passed 100 test
                                                  //| 
                                                  //|   s.
                                                  //| 
+ applicative.apply.functor.identity: OK, passed 100 tests.
                                                  //| 
+ applicative.apply.functor.composite: OK, passed 100 tests.
                                                  //| 
+ applicative.apply.composition: OK, passed 100 tests.
                                                  //| 
+ applicative.identity: OK, passed 100 tests.
                                                  //| 
+ applicative.homomorphism: OK, passed 100 tests.
                                                  //| 
+ applicative.interchange: OK, passed 100 tests.
                                                  //| 
+ applicative.map consistent with ap: OK, passed 100 tests.

成功通过了Applicative定律测试。现在我们可以说Configure类型既是Functor也是Applicative。






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值