官术网_书友最值得收藏!

Higher kinded types

Our example of the glass has become a bit boring. To make it fascinating again, we'll add another abstraction, a jar. This is how our model will look after that:

sealed trait Contents
case class Water(purity: Int) extends Contents
case class Whiskey(label: String) extends Contents
sealed trait Container[C <: Contents] { def contents: C }
case class Glass[C<: Contents](contents: C) extends Container[C]
case class Jar[C <: Contents](contents: C) extends Container[C]

The glass and the jar can both be filled with any contents. For instance, this is how it can be done:

def fillGlass[C <: Contents](c: C): Glass[C] = Glass(c)
def fillJar[C <: Contents](c: C): Jar[C] = Jar(c)

As we can see, both methods look identical with respect to the type used to construct the result. The parameterized type that is used to construct types is called a type constructor. As a consistent language, Scala allows you to abstract over type constructors in the same way it allows you to abstract over functions via higher order functions (more about this in the next chapter). This abstraction over type constructors is called higher kinded types. The syntax requires us to use an underscore to denote the expected type parameter on the definition side. The implementation should then use the type constructor without type constraints.

We can use a type constructor to provide a generic filling functionality. Of course, we can't get rid of the specific knowledge about how to fill our containers, but we can move it to the type level:

sealed trait Filler[CC[_]] {
def fill[C](c: C): CC[C]
}
object GlassFiller extends Filler[Glass] {
override def fill[C](c: C): Glass[C] = Glass(c)
}
object JarFiller extends Filler[Jar] {
override def fill[C](c: C): Jar[C] = Jar(c)
}

In the preceding code, we're using the type constructor CC[_] to denote both Glass and Jar in the Filler trait. We can now use created abstractions to define a generic filling functionality:

def fill[C, G[_]](c: C)(F: Filler[G]): G[C] = F.fill(c)

The G[_] type is a type constructor for glass and jar, and Filler[G] is a higher order type that uses this type constructor to build a full G[C] for any content, C. This is how the generic fill method can be used in practice:

val fullGlass: Glass[Int] = fill(100)(GlassFiller)
val fullJar: Jar[Int] = fill(200)(JarFiller)

This might not look like a huge win over the specific methods for now because we've provided our type constructors explicitly. The real advantage will become obvious the moment we start talking about implicits in Chapter 4, Getting to know Implicits and Type Classes.

主站蜘蛛池模板: 青龙| 图木舒克市| 乳山市| 英吉沙县| 斗六市| 西城区| 门头沟区| 中西区| 霍邱县| 尚义县| 高雄市| 宕昌县| 石渠县| 城固县| 中江县| 读书| 科尔| 蕲春县| 磐安县| 宁波市| 客服| 南阳市| 咸阳市| 乐都县| 伊川县| 临朐县| 清远市| 长阳| 和龙市| 永福县| 永康市| 疏附县| 武强县| 佛学| 科技| 黄冈市| 马关县| 铁力市| 荆门市| 南溪县| 怀柔区|