设为首页 加入收藏

TOP

Scalaz(21)-类型例证:Liskov and Leibniz - type evidence(一)
2017-10-10 12:13:26 】 浏览:4426
Tags:Scalaz 类型 例证 Liskov and Leibniz type evidence

  Leskov,Leibniz,别扭的名字,是什么地干活?碰巧从scalaz源代码里发现了这么个东西:scalaz/BindSyntax.scala

/** Wraps a value `self` and provides methods related to `Bind` */ final class BindOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Bind[F]) extends Ops[F[A]] { ////
  import Liskov.<~<, Leibniz.=== def flatMap[B](f: A => F[B]) = F.bind(self)(f) def >>=[B](f: A => F[B]) = F.bind(self)(f) def ?[B](f: A => F[B]) = F.bind(self)(f) def join[B](implicit ev: A <~< F[B]): F[B] = F.bind(self)(ev(_)) def μ[B](implicit ev: A <~< F[B]): F[B] = F.bind(self)(ev(_)) def >>[B](b: => F[B]): F[B] = F.bind(self)(_ => b) def ifM[B](ifTrue: => F[B], ifFalse: => F[B])(implicit ev: A === Boolean): F[B] = { val value: F[Boolean] = ev.subst(self) F.ifM(value, ifTrue, ifFalse) } ////
}

原来Liskov和Leibniz都是scalaz库的type class。Leskov <~< 和 Leibniz === 都是类型操作符号,实际上是scalaz自己版本的类型限制操作符 <:< 和 =:= 。发现这两个函数看起来特别奇怪才打起了彻底了解一下Leskov和Leibeniz的主意:

 def join[B](implicit ev: A <~< F[B]): F[B] = F.bind(self)(ev(_)) def ifM[B](ifTrue: => F[B], ifFalse: => F[B])(implicit ev: A === Boolean): F[B] = { val value: F[Boolean] = ev.subst(self) F.ifM(value, ifTrue, ifFalse) }

在这两个函数的隐式参数分别使用了<~<和=== 。既然与标准scala库的<:<和=:=相对应,那么我们可以先了解一下<:<和=:=的用法:

A =:= B 意思是A必须是B类型的,如:A =:= Int 意思是A必须是Int类型的。而A <:< B 意思是A必须是B的子类,或者说是我们可以在任何时候用A来替代B。那么既然已经知道了A的类型为什么还需要再框定它呢?实际上的确在某些场合需要对A的类型进行进一步框定,看看下面的例子:

case class Foo[A](a: A) {  //type A 可以是任何类型
  def getLength(implicit ev: A =:= String): Int = a.length  //A必须是String
  def getSquare(implicit ev: A <:< Int): Int = a * a //A必须是Int或子类
} Foo("word length").getLength                      //> res0: Int = 11
Foo(3).getSquare                                  //> res1: Int = 9
Foo("word length").getSquare   //cannot prove that String <:< Int
Foo(3).getLength               //cannot prove that Int =:= String

class Foo[A]的类型参数可以是任何类型,意思是我们可以用任何类型来实例化Foo。然后我们在class内部用implicit ev更近一步的限定了A的类型。这样我们才能正确使用getLength和getSquare函数,否则发生编译错误。这个例子基本上能把=:=,<:<解释清楚了。

那么既然scalaz的<~<和===对应了<:<和=:=,那么先在上面的例子中用scalaz版本试试:

 1 package Exercises  2 import scalaz._  3 import Scalaz._  4 import Liskov.<~<, Leibniz.===
 5 object evidence {  6 case class Foo[A](a: A) {  //type A 可以是任何类型
 7   def getLength(implicit ev: A === String): Int = ev(a).length  //A必须是String
 8   def getSquare(implicit ev: A <~< Int): Int = ev(a) * ev(a) //A必须是Int或子类
 9 } 10 Foo("word length").getLength                      //> res0: Int = 11
11 Foo(3).getSquare                                  //> res1: Int = 9
12 Foo("word length").getSquare   //could not find implicit value for parameter ev: scalaz.Liskov.<~<[String,Int]
13 Foo(3).getLength               //could not find implicit value for parameter ev: scalaz.Leibniz.===[Int,String]

我们看到可以得到相同的效果。
再看看原理,就用scalaz版的作为研究对象吧。因为Liskov和Leibniz都是scalaz的type class,对于隐性参数我们要进行隐式转换解析。先看看Leibniz的一些定义:scalaz/Leibniz.scala

sealed abstract class Leibniz[-L, +H >: L, A >: L <: H, B >: L <: H] { def apply(a: A): B = subst[Id](a) def subst[F[_ >: L <: H]](p: F[A]): F[B] ...

先不用理会这些类型参数限定,很乱,总之绕来绕去就是A和B在一个类型区域内。值得注意的是apply,和subst这个抽象函数:输入参数F[A]返回结果F[B]。因为A === String其实就是Leibniz[A,String]的一种表达方式,我们需要解析Leibniz实例。在Leibniz.scala内发现了这个:

object Leibniz extends LeibnizInstances with LeibnizFunctions{ /** `(A === B)` is a supertype of `Leibniz
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(20)-Monad: Validatio.. 下一篇Scalaz(22)- 泛函编程思维: C..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目