Reputation: 1988
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
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