Scala: Types of a higher kind
One of the more powerful features Scala has is the ability to generically abstract across things that take type parameters. This feature is known as Higher Kinded Types (HKT).
This feature allows us to write a library that works with a much wider array of classes, whereas without the feature you are condemned to bespoke and error ridden code duplication for each class that may want the functionality.
Type constructors
Essentially what HKT gives us is the ability to generalize across type constructors – where a type constructor is anything that has a type parameter. For instance List[_]* is not a type, the underscore is a hole into which another type may be plugged, constructing a complete type. List[String] and List[Int] being examples of complete (or distinct) types.
Kinds
Now that we have a type constructor we can think of several different kinds of them, classified by how many type parameters they take. The simplest – like List[_] – that take a single param have the kind:
(* -> *)
This says: given one type, produce another. For instance, given String produce the type List[String].
Something that takes two parameters, say Map[_, _], or Function1[_, _] has the kind:
(* -> * -> *)
This says: given one type, then another, produce the final type. For instance given the key type Int and the value type String produce the type Map[Int, String].
Furthermore, you can have kinds that are themselves parameterized by higher kinded types. So, something could not only take a type, but take something that itself takes type parameters. An example would be the covariant functor: Functor[F[_]], it has the kind:
((* -> *) -> *)
This says: given a simple higher kinded type, produce the final type. For instance given a type constructor like List produce the final type Functor[List].
Utility
Say we have some standard pattern for our data-structures where we want to be able to consistently apply an operation of the same shape. Functors are a nice example, the covariant functor allows us to take a box holding things of type A, and a function of A => B and get back a box holding things of type B.
In Java, there is no way to specify that these things share a common interface, or that we simply want transformable boxes. We need to either make this static eg. Guava’s Listsand Iterables, or bespoke on the interface, eg: fugue’s Option or atlassian-util-concurrent’s Promise. There is simply no way to unify these methods on either some super interface or to specify that you have – or require – a “mappable/transformable” box.
With HKT I can represent the covariant functor described above as:
[cc lang=’scala’ ]
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
// implement for java’s List
// note that the presence of mutation in the Java collections
// breaks the Functor laws
import java.util.{ List => JList }
implicit object JavaListFunctor extends Functor[JList] {
import collection.JavaConverters._
def map[A, B](fa: JList[A])(f: A => B): JList[B] =
(for (a B): Box2[B] =
Box2(f(b.a1), f(b.a2))
}
// and use it**
def describe[A, F[_]: Functor](fa: F[A]) =
implicitly[Functor[F]].map(fa)(a => a.toString)
case class Holder(i: Int)
val jlist: JList[Holder] = {
val l = new java.util.ArrayList[Holder]()
l add Holder(1); l add Holder(2); l add Holder(3)
l
}
val list = describe(jlist) // list: java.util.List[String] = [Holder(1), Holder(2), Holder(3)]
val box2 = describe(Box2(Holder(4), Holder(5)) // box: Box2[String] = Box2(Holder(4),Holder(5))
[/cc]
So, we have a describe function that works for any type that we can map over!
We could also use this with a traditional subtyping approach to have our boxes implement the map method directly with the appropriate signature. This is a little more convoluted, but still possible:
[cc lang=’scala’]
/**
* note we need a recursive definition of F as a subtype of Functor
* because we need to refer to it in the return type of map(…)
*/
trait Functor[A, F[_] B): F[B]
}
case class Box[A](a: A) extends Functor[A, Box] {
def map[B](f: A => B) =
Box(f(a))
}
def describe[A, F[A] a.toString)
val box = describe(Box(Holder(6))) // box: Box[String] = Box(Holder(6))
[/cc]
As a bonus, this last example quite nicely shows how subtype polymorphism is strictly less powerful and also more complicated (both syntactically and semantically) than ad-hoc polymorphism via type-classes.
Postscript
These techniques can lead to some very general and powerful libraries, such as scalaz, spire and shapeless. These libraries may take some getting used to, and as many of these generalizations are inspired by the mother of all generalizations – mathematics – they have names that need learning (like Monad).
However, the techniques are useful without needing to use scalaz. HKT is important for creating type-classes, and creating your own type-classes to encapsulate things like JSON encoding may be of value to your project. There are many ways this can be used within Scala.
If you’re interested in reading more, here’s the original paper for Scala. Among other things, it contains the following very useful graphic:
Also note that the Scala 2.11 REPL is getting a :kind command although its output is a little more convoluted due to the presence of variance annotations on type parameters.
* Strictly speaking, in Scala List[_] is actually an existential type. For the purposes of this post I am using the [_] notation to show the existence of type parameters. Thanks to Stephen Compall for pointing this out.
** An alternate syntax for a context-bound is an explicit implicit block:
[cc lang=’scala’]
def describe2[A, F[_]](fa: F[A])(implicit functor: Functor[F]) =
functor.map(fa) { _.toString }
[/cc]
https://www.atlassian.com/blog/archives/scala-types-of-a-higher-kind
Scala: Types of a higher kind的更多相关文章
- Scala Types 2
存在类型 形式: forSome { type ... } 或 forSome { val ... } 主要为了兼容 Java 的通配符 示例 Array[_] // 等价于 Array[T] for ...
- Scala Types 1
在 Scala 中所有值都有一种对应的类型 单例类型 形式:value.type,返回类型 value / null 场景1:链式API调用时的类型指定 class Super { def m1(t: ...
- Beginning Scala study note(8) Scala Type System
1. Unified Type System Scala has a unified type system, enclosed by the type Any at the top of the h ...
- scala速成记录1
选择 Learning Scala这本书,两百多页,足够薄. 安装 http://www.scala-lang.org/ 下载Binary的版本.bin里边有所有操作系统下运行的可以运行的交互式s ...
- Scala 中的函数式编程基础(二)
主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...
- geotrellis使用(十九)spray-json框架介绍
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 spray-json简介 spray-json使用 ...
- 论文笔记之:Visual Tracking with Fully Convolutional Networks
论文笔记之:Visual Tracking with Fully Convolutional Networks ICCV 2015 CUHK 本文利用 FCN 来做跟踪问题,但开篇就提到并非将其看做 ...
- Akka(33): Http:Marshalling,to Json
Akka-http是一项系统集成工具.这主要依赖系统之间的数据交换功能.因为程序内数据表达形式与网上传输的数据格式是不相同的,所以需要对程序高级结构化的数据进行转换(marshalling or se ...
- 【原创】大叔问题定位分享(11)Spark中对大表子查询加limit为什么会报Broadcast超时错误
当两个表需要join时,如果一个是大表,一个是小表,正常的map-reduce流程需要shuffle,这会导致大表数据在节点间网络传输,常见的优化方式是将小表读到内存中并广播到大表处理,避免shuff ...
随机推荐
- 清北学堂模拟赛d1t6 或和异或(xor)
题目描述 LYK最近在研究位运算,它研究的主要有两个:or和xor.(C语言中对于|和^) 为了更好的了解这两个运算符,LYK找来了一个2^n长度的数组.它第一次先对所有相邻两个数执行or操作,得到一 ...
- [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
菜肴制作 bzoj-4010 HNOI-2015 题目大意:给定一张n个点m条边的有向图,求一个toposort,使得:(1)满足编号为1的点尽量在前:(2)满足(1)的情况下编号为2的点尽量在前,以 ...
- N天学习一个linux命令之diff
用途 按行比较文件差异,也可以比较目录 用法 diff [OPTION]... FILES 常用选项 -i --ignore-case 忽略大小写 --ignore-file-name-case 忽略 ...
- Openfire:解决乱码问题
当部署openfire后,创建用户和发送离线消息时会出现中文字符乱码的问题.要解决这个问题需要同时配置openfire和mysql两端. 首先openfire端,在安装页面中指定odbc连接串中需要带 ...
- Hard模式题目
先过一下Hard模式的题目吧. # Title Editorial Acceptance Difficulty Frequency . 65 Valid Number 12.6% Ha ...
- 一个伟大的发现,装X一下。笔记本win7系统64位机器执行unity 时,屏幕模糊解决的方法
笔者笔记本win7系统64位机器执行unity 时.往往切换时unity界面屏幕模糊,后来发现此时须要下载DirectXRuntimes201006x64,安装就可以.
- [ JavaScript ] JavaScript 实现继承.
对于javascript中的继承,因为js中没有后端语言中的类式继承.所以js中的继承,一般都是原型继承(prototype). function P (name){ this.name = name ...
- luogu1005 矩阵取数游戏
题目大意 一个矩阵,每次从每一行的行首或行尾取一个数,每一行的价值为 取的数*2^当前取数的次数,每一次的价值为每一行的价值的和.求得到的价值的最大值. 思路 #include <cstdio& ...
- [NOIP 2016] 蚯蚓
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=4721 [算法] 首先,我们可以维护一个堆,堆中存放蚯蚓的长度,由于除当前蚯蚓其他的蚯 ...
- 插入1000万条数据到mysql数据库表
转自:https://www.cnblogs.com/fanwencong/p/5765136.html 我用到的数据库为,mysql数据库5.7版本的 1.首先自己准备好数据库表 其实我在插入100 ...