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. hdu_1014_Uniform Generator_201310141958

    Uniform Generator Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  2. Java单元測试工具JUnit 5新特性一览

    Java单元測试工具JUnit 5新特性一览 作者:chszs,未经博主同意不得转载. 经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs JUnit是最流行的开源 ...

  3. jvm的运行模式 client和 server两种

    jvm的运行模式 client和 server两种 学习了:https://www.cnblogs.com/fsjohnhuang/p/4270505.html 在jdk 9的情况下,好像没有clie ...

  4. 容器+AOP实现动态部署(三)

    上节咱们谈到容器的基本特性,这次继续说容器怎样与AOP进行结合.增强咱们的对象.为对象加入额外的方法. 咱们下面方图为说明 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkb ...

  5. 用CSS3实现带有阴影效果和颜色渐变的按钮

    这里讲下如何利用css3里的两个新属性 box-shadow和transition来实现如下所示的带有阴影和颜色渐变效果的按钮(下面这个只是图片:本想直接在这个页面下嵌html的,,试了后发现有些cs ...

  6. cocos2d-x 3.0游戏实例学习笔记 《跑酷》第七步--物理碰撞检測(1)

    说明:这里是借鉴:晓风残月前辈的博客,他是将泰然网的跑酷教程,用cocos2d-x 2.X 版本号重写的,眼下我正在学习cocos2d-X3.0 于是就用cocos2d-X 3.0重写,并做相关笔记 ...

  7. ubuntu系统启动qtceator时提示:Qt5.5.1/Tools/QtCreator/lib/qtcreator/plugins/libHelp.so: 无法加载库

    在ubuntu系统下安装好qt5.5后启动qtceator时提示:Qt5.5.1/Tools/QtCreator/lib/qtcreator/plugins/libHelp.so: 无法加载库Qt5. ...

  8. 好记性不如烂笔头——WebService与Remoting

    一.WebService总体上分为5个层次: 1)HTTP传输信道 2)XML的数据格式 3)SOAP的封装协议,用于传输 4)WSDL的描述方式,用于引用 5)UDDI,通用描述.发现与集成服务,用 ...

  9. bzoj3224: Tyvj 1728 普通平衡树(平衡树)

    bzoj3224: Tyvj 1728 普通平衡树(平衡树) 总结 a. cout<<(x=3)<<endl;这句话输出的值是3,那么对应的,在splay操作中,当父亲不为0的 ...

  10. map集合遍历的五种方法

    package com.jackey.topic; import java.util.ArrayList;import java.util.HashMap;import java.util.Itera ...