Towards equal rights for higher-kinded types
Available from citeseerx.ist.psu.edu
Page 1
Towards equal rights for higher-kinded types
Towards Equal Rights for Higher-kinded Types
Adriaan Moors1?, Frank Piessens1, and Martin Odersky2
1 K.U. Leuven
{adriaan, frank}@cs.kuleuven.be
2 EPFL
martin.odersky@epfl.ch
Abstract. Generics are a very popular feature of contemporary OO languages,
such as Java, C# or Scala. Their support for genericity is lacking, however. The
problem is that they only support abstracting over proper types, and not over
generic types. This limitation makes it impossible to, e.g., define a precise in-
terface for Iterable, a core abstraction in Scala’s collection API. We imple-
mented “type constructor polymorphism” in Scala 2.5, which solves this problem
at the root, thus greatly reducing the duplication of type signatures and code.
1 Introduction
Object-oriented languages such as Java, C#, and (until now) Scala offer only limited
support for genericity, in that generic types are not considered first-class types. In this
section, we illustrate why this limitation is problematic using a core abstraction from
Scala’s collections API and sketch the solution we implemented in Scala 2.5.
The following sections provides a more detailed discussion of type constructor poly-
morphism and elaborate on interesting applications. Finally, the section on related work
discusses how type constructor polymorphism has been known for several decades in
research on functional programming (FP) languages. Industrial-strength FP languages,
such as Haskell, have supported it for at least 15 years.
1.1 The trouble with Iterable
The Iterable interface (an extract is shown below) declares the operations that un-
derlie Scala’s for-comprehensions. map takes a function from El to NewEl and applies
it to every element of the current collection to produce a new collection of NewEl’s.
flatMap generalises this behaviour in that, for every element of type El, the user-
supplied function may produce a collection of NewEl’s, instead of just a single element.
The produced elements will all be merged into one collection. Finally, filter exam-
ines every element in the current collection and returns a new one containing only those
elements that matched the user-supplied predicate p.
? The first author is supported by a grant from the Flemish IWT. Part of the reported work was
performed during a 3-month stay at the third author’s lab.
Accepted to MPOOL 2007: 6th International Workshop on Multiparadigm Programming with Object-Oriented Languages
(Version of July 13, 2007)
Adriaan Moors1?, Frank Piessens1, and Martin Odersky2
1 K.U. Leuven
{adriaan, frank}@cs.kuleuven.be
2 EPFL
martin.odersky@epfl.ch
Abstract. Generics are a very popular feature of contemporary OO languages,
such as Java, C# or Scala. Their support for genericity is lacking, however. The
problem is that they only support abstracting over proper types, and not over
generic types. This limitation makes it impossible to, e.g., define a precise in-
terface for Iterable, a core abstraction in Scala’s collection API. We imple-
mented “type constructor polymorphism” in Scala 2.5, which solves this problem
at the root, thus greatly reducing the duplication of type signatures and code.
1 Introduction
Object-oriented languages such as Java, C#, and (until now) Scala offer only limited
support for genericity, in that generic types are not considered first-class types. In this
section, we illustrate why this limitation is problematic using a core abstraction from
Scala’s collections API and sketch the solution we implemented in Scala 2.5.
The following sections provides a more detailed discussion of type constructor poly-
morphism and elaborate on interesting applications. Finally, the section on related work
discusses how type constructor polymorphism has been known for several decades in
research on functional programming (FP) languages. Industrial-strength FP languages,
such as Haskell, have supported it for at least 15 years.
1.1 The trouble with Iterable
The Iterable interface (an extract is shown below) declares the operations that un-
derlie Scala’s for-comprehensions. map takes a function from El to NewEl and applies
it to every element of the current collection to produce a new collection of NewEl’s.
flatMap generalises this behaviour in that, for every element of type El, the user-
supplied function may produce a collection of NewEl’s, instead of just a single element.
The produced elements will all be merged into one collection. Finally, filter exam-
ines every element in the current collection and returns a new one containing only those
elements that matched the user-supplied predicate p.
? The first author is supported by a grant from the Flemish IWT. Part of the reported work was
performed during a 3-month stay at the third author’s lab.
Accepted to MPOOL 2007: 6th International Workshop on Multiparadigm Programming with Object-Oriented Languages
(Version of July 13, 2007)
Page 2
2 Adriaan Moors, Frank Piessens, and Martin Odersky
trait Iterable[El] {
def map[NewEl](f: El ⇒ NewEl): Iterable[NewEl]
def flatMap[NewEl](f: El ⇒ Iterable[NewEl]): Iterable[NewEl]
def filter(p: El ⇒ Boolean): Iterable[El]
}
The signatures of Iterable’s methods precisely track the type of the elements
in the containers they manipulate. However, little is known (or enforced) about the
container itself. A container of elements of type T produced by one of these methods
must simply be a subtype of Iterable[T]. Actual implementations of these methods
allow for stricter bounds on their return types: in a subclass of Iterable, the precise
type of the produced container is known, and this information should be exposed to the
clients of these methods (to a certain extent).
The obvious3 way to improve Iterable so that subclasses can precisely specify
which type of container they produce, is to abstract over that type. However, we run into
problems when trying to define a type parameter Container that could later be instan-
tiated in a subclass. We must be able to apply different type arguments to Container
(NewEl and El), but this is not allowed:
trait Iterable[El, Container] {
// error: Container does not take type parameters
def map[NewEl](f: El ⇒ NewEl): Container[NewEl]
def flatMap[NewEl](f: El ⇒ Container[NewEl]): Container[NewEl]
def filter(p: El ⇒ Boolean): Container[El]
}
Our extension adds support for type parameters that take type parameters them-
selves. We will elaborate on this in the rest of the paper. First, we show the ad-hoc
solution currently employed in the Scala libraries.
Currently, every subclass of Iterable refines the result type of the relevant meth-
ods individually, scattering this part of Iterable’s contract over the class hierarchy.
Furthermore, this kind of change is only allowed for result types, as they are in a co-
variant position. More complicated designs quickly transcend these limitations.
As an example of this redundancy, consider the List subclass:
class List[El] extends Iterable[El] {
def map[NewEl](f: El ⇒ NewEl): List[NewEl]
def flatMap[NewEl](f: El ⇒ Iterable[NewEl]): List[NewEl]
def filter(p: El ⇒ Boolean): List[El]
}
We must refine every single method inherited from the Iterable interface to de-
note the fact that these methods actually produce the same container as the one they
were applied to. Worse, almost the same code has to be written over and over again in
the subclasses of Iterable. Our extension not only allows these methods to receive
3 Bruce’s MyType [4] or Eiffel’s type anchors [12] are not viable alternatives, as the result types
are not necessarily the same as the type of the enclosing class: Iterable[El]’s methods
return Iterable[El]’s as well as Iterable[NewEl]’s. Furthermore, our approach fully
supports programming against interfaces, whereas these alternatives do not.
trait Iterable[El] {
def map[NewEl](f: El ⇒ NewEl): Iterable[NewEl]
def flatMap[NewEl](f: El ⇒ Iterable[NewEl]): Iterable[NewEl]
def filter(p: El ⇒ Boolean): Iterable[El]
}
The signatures of Iterable’s methods precisely track the type of the elements
in the containers they manipulate. However, little is known (or enforced) about the
container itself. A container of elements of type T produced by one of these methods
must simply be a subtype of Iterable[T]. Actual implementations of these methods
allow for stricter bounds on their return types: in a subclass of Iterable, the precise
type of the produced container is known, and this information should be exposed to the
clients of these methods (to a certain extent).
The obvious3 way to improve Iterable so that subclasses can precisely specify
which type of container they produce, is to abstract over that type. However, we run into
problems when trying to define a type parameter Container that could later be instan-
tiated in a subclass. We must be able to apply different type arguments to Container
(NewEl and El), but this is not allowed:
trait Iterable[El, Container] {
// error: Container does not take type parameters
def map[NewEl](f: El ⇒ NewEl): Container[NewEl]
def flatMap[NewEl](f: El ⇒ Container[NewEl]): Container[NewEl]
def filter(p: El ⇒ Boolean): Container[El]
}
Our extension adds support for type parameters that take type parameters them-
selves. We will elaborate on this in the rest of the paper. First, we show the ad-hoc
solution currently employed in the Scala libraries.
Currently, every subclass of Iterable refines the result type of the relevant meth-
ods individually, scattering this part of Iterable’s contract over the class hierarchy.
Furthermore, this kind of change is only allowed for result types, as they are in a co-
variant position. More complicated designs quickly transcend these limitations.
As an example of this redundancy, consider the List subclass:
class List[El] extends Iterable[El] {
def map[NewEl](f: El ⇒ NewEl): List[NewEl]
def flatMap[NewEl](f: El ⇒ Iterable[NewEl]): List[NewEl]
def filter(p: El ⇒ Boolean): List[El]
}
We must refine every single method inherited from the Iterable interface to de-
note the fact that these methods actually produce the same container as the one they
were applied to. Worse, almost the same code has to be written over and over again in
the subclasses of Iterable. Our extension not only allows these methods to receive
3 Bruce’s MyType [4] or Eiffel’s type anchors [12] are not viable alternatives, as the result types
are not necessarily the same as the type of the enclosing class: Iterable[El]’s methods
return Iterable[El]’s as well as Iterable[NewEl]’s. Furthermore, our approach fully
supports programming against interfaces, whereas these alternatives do not.
Sign up today - FREE
Mendeley saves you time finding and organizing research. Learn more
- All your research in one place
- Add and import papers easily
- Access it anywhere, anytime
Start using Mendeley in seconds!
Readership Statistics
8 Readers on Mendeley
by Discipline
13% Mathematics
by Academic Status
38% Ph.D. Student
25% Other Professional
13% Post Doc
by Country
38% United States
25% United Kingdom
13% Switzerland



