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 scalazspire 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的更多相关文章

  1. Scala Types 2

    存在类型 形式: forSome { type ... } 或 forSome { val ... } 主要为了兼容 Java 的通配符 示例 Array[_] // 等价于 Array[T] for ...

  2. Scala Types 1

    在 Scala 中所有值都有一种对应的类型 单例类型 形式:value.type,返回类型 value / null 场景1:链式API调用时的类型指定 class Super { def m1(t: ...

  3. 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 ...

  4. scala速成记录1

    选择  Learning Scala这本书,两百多页,足够薄. 安装 http://www.scala-lang.org/  下载Binary的版本.bin里边有所有操作系统下运行的可以运行的交互式s ...

  5. Scala 中的函数式编程基础(二)

    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...

  6. geotrellis使用(十九)spray-json框架介绍

    Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 spray-json简介 spray-json使用 ...

  7. 论文笔记之:Visual Tracking with Fully Convolutional Networks

    论文笔记之:Visual Tracking with Fully Convolutional Networks ICCV 2015  CUHK 本文利用 FCN 来做跟踪问题,但开篇就提到并非将其看做 ...

  8. Akka(33): Http:Marshalling,to Json

    Akka-http是一项系统集成工具.这主要依赖系统之间的数据交换功能.因为程序内数据表达形式与网上传输的数据格式是不相同的,所以需要对程序高级结构化的数据进行转换(marshalling or se ...

  9. 【原创】大叔问题定位分享(11)Spark中对大表子查询加limit为什么会报Broadcast超时错误

    当两个表需要join时,如果一个是大表,一个是小表,正常的map-reduce流程需要shuffle,这会导致大表数据在节点间网络传输,常见的优化方式是将小表读到内存中并广播到大表处理,避免shuff ...

随机推荐

  1. 【codeforces 797C】Minimal string

    [题目链接]:http://codeforces.com/contest/797/problem/C [题意] 一开始,给你一个字符串s:两个空字符串t和u; 你有两种合法操作; 1.将s的开头字符加 ...

  2. [TJOI2014] [Bzoj3996] 线性代数 [网络流,最小割]

    由原式,可以推出D=Σ(i=1,n,Σ(j=1,n,A[i]*A[j]*B[i][j]))-Σ(i=1,n,A[i]*C[i]) $D=\sum\limits_{i=1}^{n}\sum\limits ...

  3. du 和 df的不同

    http://blog.sina.com.cn/s/blog_9c8286b7010108aj.html du和df命令都被用于获得文件系统大小的信息:df用于报告文件系统的总块数及剩余块数,du - ...

  4. 对SPI、IIC、IIS、UART、CAN、SDIO、GPIO的解释

    SPI SPI(Serial Peripheral Interface:串行外设接口); SPI总线由三条信号线组成:串行时钟(SCLK).串行数据输出(SDO).串行数据输入(SDI).SPI总线可 ...

  5. J - Simpsons’ Hidden Talents

    Homer: Marge, I just figured out a way to discover some of the talents we weren’t aware we had. Marg ...

  6. 洛谷 P1586 四方定理

    P1586 四方定理 题目描述 四方定理是众所周知的:任意一个正整数nn,可以分解为不超过四个整数的平方和.例如:25=1^{2}+2^{2}+2^{2}+4^{2}25=1​2​​+2​2​​+2​ ...

  7. EXP/IMP version

    在imp数据的时候,有时候imp命令会不识别dump文件.这通常是因为dump是由高版本的exp 导出的而imp是低版本的. 这种情况下只能是用低版本的exp重新导出.

  8. HDU 4362

    方程很简单 p[i][j] = min{dp[i-1][k] + |pos[i-1][k] - pos[i][k]|} + v[i][j]; 循环求值肯定TLE,主要是绝对值不方便.好吧,我真的BI了 ...

  9. Advapi32.dll 函数接口说明

    Advapi32.dll 函数接口说明             函数原型                               说明    AbortSystemShutDown         ...

  10. P1043 数字游戏

    P1043 数字游戏 题目描述 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共n ...