yotommy
yotommy

Reputation: 1496

Type bounds on functions

I am having trouble getting my code with parameterized types to pass the scala compiler. My goal is to be able to express (Predicate, Action) pairs as shown in the MyProg object.

trait ProgBase {
  type Predicate[T] = T => Boolean
  type Action[T]    = T => Unit

  private var prog = List[(Predicate[Any], Action[Any])]()

  final def on[T <: Any](pred: Predicate[T])(action: Action[T]) = {
    prog = (pred, action) :: prog // gives type mismatch
  }

  // remainder of trait elided
}

object MyProg extends ProgBase {
  on[String](s => !s.isEmpty) { s =>
    println(s + " is not empty")
  }

  on[Int](i => i.isValidByte) { i =>
    println(i + " can fit in a byte")
  }
}

By specifying that T has an upper bound of Any, I hoped this would appease the compiler, but clearly I am missing something:

[error] ......ProgBase.scala:8 type mismatch;
[error]  found   : (T => Boolean, T => Unit)
[error]  required: (Any => Boolean, Any => Unit)
[error]     prog = (pred, action) :: prog
[error]                           ^

Upvotes: 1

Views: 119

Answers (1)

DaunnC
DaunnC

Reputation: 1301

First of all, answer to your question, if you write:

private var prog = List[(Predicate[_ <: Any], Action[_ <: Any])]()

It all compiles OK. We should use wildcards, 'cause the type of elements are unknown.

Second, maybe you mistyped, you can't use your on function as you used it, use it something like:

on[String](s => !s.isEmpty)(s => !s.isEmpty)

It caused by type mismatch: type Action[T] = T => Unit but println has type Unit, so as a variant u can simply write: type Action = Unit. Obviously, u can avoid using this type alias at all.

Third, maybe you already know, and I shoudn't tell you, that in fact, you lose all information about predicate types - let's check it using Scala reflection:

import scala.reflect.runtime.{universe => ru}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]

val s: String = "123" 
val i: Int = 123

on[String](s => !s.isEmpty)(s => !s.isEmpty)
on[Int](i => i.isValidByte)(i => i.isValidByte)

getTypeTag((MyProg.prog.head._1)).tpe =:= ru.typeOf[(String) => Boolean] //>false!

So you see the problem.

To deal with it you can use heterogeneous lists. Lists and other various powerful structures you can find in shapeless: https://github.com/milessabin/shapeless

Upvotes: 1

Related Questions