在前几篇关于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 tests.
//|
+ functor.invariantFunctor.composite: OK, passed tests.
//|
+ functor.identity: OK, passed tests.
//|
+ functor.composite: OK, passed 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定律:

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

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

Scalaz(9)- typeclass:checking instance abiding the laws的更多相关文章

  1. Scalaz(8)- typeclass:Monoid and Foldable

    Monoid是种最简单的typeclass类型.我们先看看scalaz的Monoid typeclass定义:scalaz/Monoid.scala trait Monoid[F] extends S ...

  2. Scalaz(7)- typeclass:Applicative-idomatic function application

    Applicative,正如它的名称所示,就是FP模式的函数施用(function application).我们在前面的讨论中不断提到FP模式的操作一般都在管道里进行的,因为FP的变量表达形式是这样 ...

  3. Scalaz(6)- typeclass:Functor-just map

    Functor是范畴学(Category theory)里的概念.不过无须担心,我们在scala FP编程里并不需要先掌握范畴学知识的.在scalaz里,Functor就是一个普通的typeclass ...

  4. Scalaz(4)- typeclass:标准类型-Equal,Order,Show,Enum

    Scalaz是由一堆的typeclass组成.每一个typeclass具备自己特殊的功能.用户可以通过随意多态(ad-hoc polymorphism)把这些功能施用在自己定义的类型上.scala这个 ...

  5. Scalaz(5)- typeclass:my typeclass scalaz style-demo

    我们在上一篇讨论中介绍了一些基本的由scalaz提供的typeclass.这些基本typeclass主要的作用是通过操作符来保证类型安全,也就是在前期编译时就由compiler来发现错误.在这篇讨论中 ...

  6. Scalaz(25)- Monad: Monad Transformer-叠加Monad效果

    中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾 ...

  7. Scalaz(43)- 总结 :FP就是实用的编程模式

    完成了对Free Monad这部分内容的学习了解后,心头豁然开朗,存在心里对FP的疑虑也一扫而光.之前也抱着跟大多数人一样的主观概念,认为FP只适合学术性探讨.缺乏实际应用.运行效率低,很难发展成现实 ...

  8. Scalaz(41)- Free :IO Monad-Free特定版本的FP语法

    我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段 ...

  9. Scalaz(40)- Free :versioned up,再回顾

    在上一篇讨论里我在设计示范例子时遇到了一些麻烦.由于Free Monad可能是一种主流的FP编程规范,所以在进入实质编程之前必须把所有东西都搞清楚.前面遇到的问题主要与scalaz Free的Free ...

随机推荐

  1. iOS开发----优秀文章推荐

    UI界面 iOS和Android 界面设计尺寸规范  http://www.alibuybuy.com/posts/85486.html iPhone app界面设计尺寸规范  http://www. ...

  2. WPF入门教程系列八——布局之Grid与UniformGrid(三)

    五. Grid Grid顾名思义就是“网格”,它的子控件被放在一个一个实现定义好的小格子里面,整齐配列. Grid和其他各个Panel比较起来,功能最多也最为复杂.要使用Grid,首先要向RowDef ...

  3. js中NAN、NULL、undefined的区别

    NaN:保留字(表明数据类型不是数字) undefined:对象属性或方法不存在,或声明了变量但从未赋值.即当你使用了对象未定的属性或者未定义的方法时或当你声明一个变量,但你确从未对其进行赋值,便对其 ...

  4. Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

    概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍.内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原 ...

  5. Sybase数据库收集表及其索引的统计信息

    更新表及其索引的统计信息: update table statistics 表名 go update index statistics 表名 go 建议此操作在闲时操作.

  6. Linux演示 dd测试IO

    dd测试IO,经常会用到,用来简单测试某个目录的读写性能. 本次测试环境:自己电脑的ubuntu系统-其他Unix/Linux系统也可以用dd. Tips:dd操作需要三思而行,搞清楚确认没问题再进行 ...

  7. 轻松自动化---selenium-webdriver(python) (十一)

    本节重点: 控制滚动条到底部 有时候我们需要控制页面滚动条上的滚动条,但滚动条并非页面上的元素,这个时候就需要借助js是来进行操作.一般用到操作滚动条的会两个场景: 注册时的法律条文需要阅读,判断用户 ...

  8. SQL Server逻辑读、预读和物理读

    SQL Server数据存储的形式 预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存 ...

  9. ASP.NET 程序提交表单数据中带有html标签不能提交或者提交报错问题

    今天在公司做另外的一个项目,又奇葩的遇到一个问题. 在本地自己电脑上怎么测试都是正常的.但是先上服务器就出问题: 用富文本编辑器上传一篇文章,始终报错,又没提示具体什么错误,也没说代码错误,点击提交按 ...

  10. Android SlidingMenu 仿网易新闻客户端布局

    前面两篇文章中的SlidingMenu都出现在左侧,今天来模仿一下网易新闻客户端左右两边都有SlidingMenu的效果,以下是网易新闻客户端效果: 不扯闲话了,直接进入正题吧 frame_conte ...