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]实例。可是自己定义实例实在太麻烦,能不能根据现有的实例转化成新的实例呢?当然是是可以的。

示例

还是拿cats里面的例子讲解。
先定义类型类,然后给个contramap实现:

trait Printable[A] {def format(value: A): Stringdef contramap[B](func: B => A): Printable[B] =new Printable[B] {def format(value: B): String =self.format(func(value))}
}

理论上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))}
  1. 假设我们已经有两个实例Printable[Int]Printable[Boolean]
implicit val stringPrintable: Printable[String] =new Printable[String] {def format(value: String): String =s"'${value}'"}implicit val booleanPrintable: Printable[Boolean] =new Printable[Boolean] {def format(value: Boolean): String =if(value) "yes" else "no"}
  1. 现在有个case class
// Box有个类型参数
final case class Box[A](value: A)

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相关推荐

  1. 协变 (Covariant)、逆变 (Contravariant) 与不变 (Invariant)

    协变(Covariant).逆变(Contravariant)与不变(Invariant) 1. 定义 2. 例子 3. 有什么用? 4. 应对 5. 其他问题 6. 特例(Dart 语言) 7. 写 ...

  2. scala 协变和逆变_Scala方差:协变,不变和逆变

    scala 协变和逆变 In this post, we are going to discuss about Scala Variance and it's use cases. 在本文中,我们将讨 ...

  3. 学习Scala — 8个不做和7个不做

    Today I would like to share with you my view on learning Scala, what approach to take, what to do an ...

  4. lockfree buffer test

    性能测试(3): 对无锁队列boost::lockfree::queue和moodycamel::ConcurrentQueue做一个性能对比测试 版权声明:本文为博主zieckey原创文章,转载时请 ...

  5. 29muduo_net库源码分析(五)

    1.进程(线程)wait/notify (1)pipe (2)socketpair (3)eventfd,eventfd是一个比 pipe更高效的线程间事件通知机制,一方面它比 pipe 少用一个fi ...

  6. Linux中锁的总结

    目录 1 前言 2 注意事项 2.1 明确锁的范围 2.2 减少锁的粒度 3 避免死锁的建议 1 前言 实际开发过程中,使用锁会带来一定性能的损失,但如果使用锁也能满足性能要求,对于锁的使用就无妨.使 ...

  7. 【 C++ 技术】 C++ 高性能服务器网络框架设计细节

    作者:范蠡  原文:C++ 高性能服务器网络框架设计细节 前言 这篇文章我们将介绍服务器的开发,并从多个方面探究如何开发一款高性能高并发的服务器程序.需要注意的是一般大型服务器,其复杂程度在于其业务, ...

  8. muduo源码剖析——以三个切片浅析muduo库代码设计的严谨性、高效性与灵活性

    0 前言 陈硕大佬的muduo网络库的源码我已经看了好久了,奈何本人实力有限,每每看到其代码设计的精巧之处只能内心称赞,无法用言语表达出来.实在令人汗颜.最近在看到网络设计部分时有了一些体会,结合自己 ...

  9. muduo网络库浅谈(一)

    muduo网络库浅谈(一) 序言 第一章 muduo的关键结构 class EventLoop class Channel class Poller 番外 定时任务 class Timestamp c ...

最新文章

  1. NC19814最短路 LCA+bfs暴力
  2. mysql 全局变量_详细讲解mysql全局变量与局部变量
  3. python读取扫描形成的pdf_Python利用PyPDF2库获取PDF文件总页码实例
  4. vb6 获取zip列表_深入学习redis(压缩列表)
  5. windows添加删除程序打不开解决方案
  6. jquery获取动态表格行号_jquery得到表格当前行数据
  7. 做一个小淘气轮廓--文章和论文专辑
  8. Java后端Controller层解析Json数组转对象
  9. Sniffer和网络执法官软件助你维护网络
  10. 学生管理系统IPO图_玩不起了?海底捞取消大学生69折优惠,海底捞:暂不会调整...
  11. bam文件读取_科学网—Pacbio Sequel两种bam文件解析 - 卢锐的博文
  12. 阿里云ECS部署L2TP/IPSEC,访问服务器内网
  13. 计算机工作自动化的特点,计算机内部自动化操作等特点.doc
  14. 计算机网络基础与应用复习提纲,计算机网络及应用期末复习提纲
  15. anaconda 创建虚拟环境(自己版本)
  16. mysql易百_MySQL DATE类型(来自易百)
  17. elasticsearch--Master选举
  18. Scratch3.0 Blockly如何对Arduino编译下载
  19. 贪食蛇的C++实现(课程设计)
  20. ld.so.conf.d文件修改命令

热门文章

  1. 常见前端九十道面试题及答案-韩烨
  2. 挑战10的1,143,913次方种算法组合:这都不是事儿~~
  3. 曾鸣:互联网的本质是什么?| 内部干货
  4. 全球及中国铁矿石行业供求状况与投资决策建议报告2022版
  5. 中央财经大学c语言试题答案,中央财经大学C语言题
  6. JAVA毕业设计酒店管理系统设计与实现计算机源码+lw文档+系统+调试部署+数据库
  7. 那些值得借鉴的的国外创意404错误页面设计
  8. 三坐标检测之测量基准面的选择
  9. android摄像头拍照代码,Android调用摄像头拍照开发教程
  10. android 背景图缩放,android背景图按比例缩放方法