11

Is it possible to use the context bounds syntax shortcut with higher kinded-types?

trait One { def test[W   : ClassManifest]: Unit } // first-order ok
trait Two { def test[W[_]: ClassManifest]: Unit } // not possible??
trait Six { def test[W[_]](implicit m: ClassManifest[W[_]]): Unit } // hmm...
0__
  • 66,707
  • 21
  • 171
  • 266

2 Answers2

12

Yes, it is, but your context bound type must have a higher kinded type parameter (which ClassManifest doesn't).

scala> trait HKTypeClass[CC[_]]
defined trait HKTypeClass

scala> implicit def listHKTC = new HKTypeClass[List] {}
listHKTC: java.lang.Object with HKTypeClass[List]

scala> def frob[CC[_] : HKTypeClass] = implicitly[HKTypeClass[CC]]
frob: [CC[_]](implicit evidence$1: HKTypeClass[CC])HKTypeClass[CC]

scala> frob[List]
res0: HKTypeClass[List] = $anon$1@13e02ed

Update

It's possible to use a type alias to allow a higher-kinded type parameter to be bounded by a first-order context bound type. We use the type alias as a type-level function to make a higher-kinded type out of the first-order type. For ClassManifest it could go like this,

scala> type HKClassManifest[CC[_]] = ClassManifest[CC[_]]
defined type alias HKClassManifest

scala> def frob[CC[_] : HKClassManifest] = implicitly[HKClassManifest[CC]]         
test: [CC[_]](implicit evidence$1: HKClassManifest[CC])HKClassManifest[CC]

scala> frob[List]                                                       
res1: HKClassManifest[List] = scala.collection.immutable.List[Any]

Note that on the right hand side of the type alias CC[_] is a first-order type ... the underscore here is the wildcard. Consequently it can be used as the type argument for ClassManifest.

Update

For completeness I should note that the type alias can be inlined using a type lambda,

scala> def frob[CC[_] : ({ type λ[X[_]] = ClassManifest[X[_]] })#λ] = implicitly[ClassManifest[CC[_]]]     
frob: [CC[_]](implicit evidence$1: scala.reflect.ClassManifest[CC[_]])scala.reflect.ClassManifest[CC[_]]

scala> frob[List]
res0: scala.reflect.ClassManifest[List[_]] = scala.collection.immutable.List[Any]
Miles Sabin
  • 23,015
  • 6
  • 61
  • 95
  • But `ClassManifest` doesn't need to know about that its type parameter itself is higher kinded than it expects, as long as they higher types are bound (even with a wildcard), e.g. why isn't `def test[ A, B[ A ] : ClassManifest ] {}` allowed, although `def test[ A, B[ A ]]( implicit m: ClassManifest[ B[ A ]]) {}` is? – 0__ Apr 04 '11 at 18:44
  • ClassManifest does require that it's type argument is first order as you can see if you try implicitly[ClassManifest[List]] in the REPL. – Miles Sabin Apr 04 '11 at 22:41
  • That's what I'm saying: it doesn't matter whether the type arg is first order or higher order, as long as it is reduced to first order: `implicitly[ClassManifest[List[_]]]` --> ok! So I was just wondering why I can't get the shortcut for a context bound in this case – 0__ Apr 04 '11 at 22:49
  • No, it really does matter. List is higher order, and List[_] is first order thanks to the existential. ClassManifest will only accept first order type arguments. – Miles Sabin Apr 04 '11 at 23:38
  • Miles, I'm giving you an accept here for your persistence :) but I really think we are missing each other here - I just wanna know how to do `def test[B[_] : ClassManifest] {}` – 0__ Apr 04 '11 at 23:43
  • I think I've answered the question you asked. The thing you have to understand is that by making B higher-kinded you've ruled out ClassManifest as a possible context bound for it, because ClassManifest's type argument isn't higher-kinded. – Miles Sabin Apr 05 '11 at 08:04
  • Example: `object Ref { def init[A : ClassManifest](a: A) : Ref[A] = println("ja")}; trait Ref[A]` ; then: `def test[W[_]](w: W[_])(implicit m: ClassManifest[W[_]]) = Ref.init[W[_]](w)` ; and then: `test(List(1, 2, 3))` ; works fine... – 0__ Apr 05 '11 at 12:38
  • Of course. In this case the type argument to ClassManifest is first-order: in that context W[_] means W[T] forSome { type T } which isn't a higher-order type. You've been misled by the multipurpose _. – Miles Sabin Apr 05 '11 at 14:59
  • I haven't been mislead. I exactly understand what's going on. Why can't I express what I want? I just want to be able to use the syntactic shortcut for the context bound in this case. It doesn't make sense that it doesn't work here. All I want is `[ A : B ]` – 0__ Apr 05 '11 at 15:32
  • P.S. I think we can reproduce this thread at a humorous occasion. Like: "Conversations about Scala" – 0__ Apr 05 '11 at 15:33
5

Note that implicitly[ClassManifest[List[_]]] is short for implicitly[ClassManifest[List[T] forSome {type T}]].

That's why it works: ClassManifest expects a proper type argument, and List[T] forSome {type T} is a proper type, but List is a type constructor. (Please see What is a higher kinded type in Scala? for a definition of "proper" etc.)

To make both ClassManifest[List[String]] and ClassManifest[List] work, we'd need to overload ClassManifest somehow with versions that take type parameters of varying kinds, something like:

class ClassManifest[T] // proper type
class ClassManifest[T[_]] // type constructor with one type parameter
class ClassManifest[T[_, _]] // type constructor with two type parameters
// ... ad nauseam

(On an academic note, the "proper" way to do this, would be to allow abstracting over kinds:

    class ClassManifest[T : K][K]

    implicitly[ClassManifest[String]] // --> compiler infers ClassManifest[String][*]
    implicitly[ClassManifest[List]] // --> compiler infers ClassManifest[List][* -> *]

)

Community
  • 1
  • 1
Adriaan Moors
  • 4,256
  • 1
  • 18
  • 10