Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constraining Higher-Kinded Types in Scala

Tags:

types

scala

Say I have a higher kinded type

SuperMap[Key[_],Value[_]]`.  

Suppose now that I had something even more specific that required that the type parameter for Key must match that for Value; that is, something like:

SuperDuperMap[T, Key[T], Value[T]]

Further suppose that I didn't want just any T, but a very specific one where T <: OtherT

SuperDuperPooperMap[T <: OtherT, Key[T], Value[T]]

Can this be done in Scala? Is this just generally a bad idea? Is there an equivalent way of doing this that's easier to read/write/use?

like image 475
duckworthd Avatar asked Feb 08 '12 05:02

duckworthd


People also ask

What languages support higher Kinded types?

You can pass through with a language such as OCaml, Scala, Haskell, PureScript, or one of a few others. However, users of Java, C#, F#, Elm, and many others may proceed no further, and must turn back here.

Why are there high Kinded types?

What Is Higher-Kinded Type? A higher-kinded type is a type that abstracts over some type that, in turn, abstracts over another type. It's a way to generically abstract over entities that take type constructors. They allow us to write modules that can work with a wide range of objects.

Does rust have higher Kinded types?

Rust does not have higher-kinded-types. For example, functor (and thus monad) cannot be written in Rust.

What are higher Kinded types Haskell?

A higher kinded type is a concept that reifies a type constructor as an actual type. A type constructor can be thought of in these analogies: like a function in the type universe. as a type with a "hole" in it. as a container containing type(s)


2 Answers

Your declaration already works as supposed to, i.e. you're restricting the type of T as well as Key and Value. The way you've written it, however, scala will complain if you issue something like

scala> class Foo[T <: OtherT, Key[T], Value[T]]
defined class Foo

scala> new Foo[SpecialOtherT, Key[SpecialOtherT], Value[SpecialOtherT]]
<console>:13: error: Key[SpecialOtherT] takes no type parameters, expected: one
              new Foo[SpecialOtherT, Key[SpecialOtherT], Value[SpecialOtherT]]

because the types of both Key and Value are already given by your former declaration. Hence this will work

scala> new Foo[SpecialOtherT, Key, Value]
res20: Foo[SpecialOtherT,Key,Value] = Foo@3dc6a6fd

which is probably not want you want. You could do it like this

scala> class Foo[T <: OtherT, K <: Key[T], V <: Value[T]]
defined class Foo

scala> new Foo[SpecialOtherT, Key[SpecialOtherT], Value[SpecialOtherT]]
res21: Foo[SpecialOtherT,Key[SpecialOtherT],Value[SpecialOtherT]] = Foo@7110506e

At the bottom line, since the types of Key and Value depend solely on T it is somewhat superfluous to have all that redundant information when working with Foo. So why not use an inner type declaration like so:

class Foo[T <: OtherT] {
  type K = Key[T]
  type V = Value[T]
}

Then you'd have access to types K and V from within the class but wouldn't need to type it everytime you create a new answer:

scala> new Foo[SpecialOtherT]
res23: Foo[SpecialOtherT] = Foo@17055e90

scala> new Foo[Int]
<console>:11: error: ...
like image 156
fotNelton Avatar answered Nov 19 '22 02:11

fotNelton


Can this be done in Scala?

What do you mean? You just did!

Is this just generally a bad idea?

Why would it be? In fact that's a great idea! This is what higher-kinded types are for.

Is there an equivalent way of doing this that's easier to read/write/use?

Reading - reads pretty well to me.

Writing - write/test/compile once, use everywhere.

Using - The compiler will reconstruct (infer) the types "everywhere".

like image 31
agilesteel Avatar answered Nov 19 '22 03:11

agilesteel