scala with cats 之 Contravariant Functors and Invariant Functors
Contravariant Functors
概述
Functor的意思是,如果有個F[A]和函數A=>B,那么就能得到一個F[B]。而Contravariant Functors的意思是,如果有個F[B],然后有個函數A => B,那我們就能得到一個F[A]。
用途
假如我們有個類型類,比如說
trait Semigroup[A]{def combine(x:A,y:A):A }我們經常要定義各種實例,比如Semigroup[Int],Semigroup[String],比如Semigroup[Boolean]。這些都是cats內置的實例。如果我們自己有個類型Box,我們要自己定義Semigroup[Box]實例??墒亲约憾x實例實在太麻煩,能不能根據現有的實例轉化成新的實例呢?當然是是可以的。
示例
還是拿cats里面的例子講解。
先定義類型類,然后給個contramap實現:
理論上contramap并不是Printable本身的能力,其實應該屬于Contravariant[F[_]]的能力。所以單獨實現一個Contravariant[Printable]實例比較好,類似下面的Contravariant[Show]:
implicit val catsContravariantForShow: Contravariant[Show] = new Contravariant[Show] {def contramap[A, B](fa: Show[A])(f: B => A): Show[B] =show[B]((fa.show _).compose(f))}3.定義Printable[Box[A]]的實例
// 方法一 自己實現 implicit def boxPrintable[A](implicit p: Printable[A] ): Printable[Box[A]] =new Printable[Box[A]] {def format(box: Box[A]): String =p.format(box.value)} // 方法二 通過contramap方法實現,就不必每次都去寫很多模板代碼implicit def boxPrintable[A](implicit p: Printable[A]): Printable[Box[A]] =p.contramap[Box[A]](_.value)Invariant Functors
概述
Invariant Functors 說起來雖然神秘,但是在scala的領域確是非常的常見。很多數據庫的包,比如slick,quill等。經常我們要把scala程序里面定義的case class 轉為字符串寫到數據庫,還需要把數據庫里面的字符串轉為程序需要的case class。
作為標準庫的設計者會明白,case class 是無限的,但是組成case class 的元素時有限的,就是 scala 的基本內置類型。而數據庫只需要和這些基本內置類型對接好就行了,用戶負責將case class 轉為基本內置類型。
示例
假設現在設計一個標志庫,和數據庫交互方法如下(轉成字符串就算寫入數據庫):
def encode[A](value: A)(implicit c: Codec[A]): String =c.encode(value)def decode[A](value: String)(implicit c: Codec[A]): A =c.decode(value)抽象Codec[A]
使用encode和decode,理論上能把任意的類型A寫入數據庫,也能把數據庫的數據讀成任意類型A,只需要提供類型A的Codec實例即可。Codec簽名如下:
trait Codec[A] { self =>def encode(value: A): Stringdef decode(value: String): Adef imap[B](dec: A => B, enc: B => A): Codec[B] = {new Codec[B] {def encode(value: B): String =self.encode(enc(value))def decode(value: String): B =dec(self.decode(value))}} }添加內置實例
前面說過,case class 是無限的,但是組成case class的元素是有限的,現在在標準庫中添加這些有限的內置示例。
implicit val stringCodec: Codec[String] =new Codec[String] {def encode(value: String): String = valuedef decode(value: String): String = value}implicit val doubleCodec: Codec[Double] =stringCodec.imap[Double](_.toDouble, _.toString)implicit val intCodec: Codec[Int] =stringCodec.imap(_.toInt, _.toString)implicit val booleanCodec: Codec[Boolean] =stringCodec.imap(_.toBoolean, _.toString)用戶自己實現自己的case class 實例
implicit def boxCodec[A](implicit c: Codec[A]): Codec[Box[A]] =c.imap[Box[A]](Box(_), _.value)將自己的實例寫入數據庫或者從數據庫讀出來(假設轉成字符串就算寫入數據庫)
encode(Box(123.4)) // res13: String = "123.4" decode[Box[Double]]("123.4") // res14: Box[Double] = Box(123.4)總結
以上是生活随笔為你收集整理的scala with cats 之 Contravariant Functors and Invariant Functors的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《凯撒的妻子》摘抄
- 下一篇: 迅捷PDF转换器如何将PDF文档转成Wo