7

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?

3 Answers 3

11

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: ... 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Very informative. My only answer to "So why not use an inner type declaration" is that I want to have those types for K and V inferred on instantiation.
I'm not sure I understand because the type actually is inferred. Depending on your use-case, you can still use the type from the "outside", e.g. class Foo[T]; class Bar[T] { type Wee = Foo[T] }; def doSomething[T](b: Bar[T])(implicit mf: Manifest[Bar[T]#Wee]) { Console println mf }, and then doSomething(new Bar[Double]). Agreed, it's a dirty example.
3

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

Comments

2

You probably don't need anything more complicated than a couple of type aliases,

type SuperDuperMap[T, Key[_], Value[_]] = SuperMap[Key, Value] type SuperDuperPooperMap[T <: OtherT, Key[_], Value[_]] = SuperMap[Key, Value] 

Sample REPL session,

scala> new SuperDuperMap[Int, Option, List] {} res0: java.lang.Object with SuperDuperMap[Int,Option,List] = ... scala> new SuperDuperPooperMap[OtherT, Option, List] {} res1: java.lang.Object with SuperDuperPooperMap[OtherT,Option,List] = ... 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.