Vikas Pandya
Vikas Pandya

Reputation: 1988

heterogenous mapping, dependent types at compile time

Trying to use this trick, by Miles Sabin, to create a function which accepts a parameter of only a set of predefined types.

val bool =  Boolean
val timestamp = new Timestamp(date.getTime())
val str = "My String"

and then following should pass at compile time

takeValue(bool)
takeValue(timestamp)
takeValue(str)

but

where takeValue should fail for takeValue(someIntValue) if implicit for type Int isn't defined.And this failure will be at compile time.

trait MyConv[K] { type V; def convert: AnyRef => V }

def iMakeConv[V0](con: AnyRef => V0) = new MyConv[con.type] {
  override type V = V0
  val convert = con
}

  def takeValue(value:AnyRef)(implicit conv :MyConv[value.type]) : \/[Throwable,conv.V] = \/.fromTryCatch(conv.convert(value))

And then

implicit val strAny = iMakeConv((x:Any) => x.toString)

Then I would want takeValue(str) to work at compile time but takeValue(someIntValue) to fail at compile time since there isn't any appropriate implicit defined for it. Basically want to limit(at compile time) type of types takeValue can take and fail for others.

Certainly I am doing something wrong here because when calling

takeValue("string")

it throws following at compile time

 could not find implicit value for parameter conv: 
 MyConv[String("string")]
- not enough arguments for method takeValue: (implicit conv: 
 MyConv[String("string")])scalaz.\/[Throwable,conv.V]. 
 Unspecified value parameter conv.

Upvotes: 3

Views: 97

Answers (1)

wingedsubmariner
wingedsubmariner

Reputation: 13667

The meaning of .type is often misunderstood. The type value.type is a type with only a single value - value. Even if value is known to be String, value.type is not String, but specifically only one String - value.

As a result, the code tries to find a MyConv[value.type], of which none exist, even if there is a MyConv[String] available.

Have takeValue use a type parameter instead:

def takeValue[T](value: T)(implicit conv: MyConv[T]) : \/[Throwable, conv.V] = \/.fromTryCatch(conv.convert(value))

Or alternatively make the type parameter of MyConv contravariant:

trait MyConv[-K] { type V; def convert: AnyRef => V }

Which would allow a MyConv[T] to be used if value.type is a subtype of T.

You also need a different MyConv and iMakeConv:

trait MyConv[K] { type V; def convert: K => V }

def iMakeConv[K, V0](con: K => V0) = new MyConv[K] {
  override type V = V0
  val convert = con
}

Upvotes: 5

Related Questions