Reputation: 649
What I am trying to achieve is a way to:
Ideally I would like to type the following expression in the IDE:
val record = types.addValue("test").addValue(123).addValue(new java.util.Date())
having the type of the arguments to addValue()
constrained by the IDE type inference engine.
What follows is a working implementation of the point 1: a specification of record fields types:
case class FieldType[V, T <: FieldType[_, _]](clazz: Class[V], tail: T) {
def addValue(value: V) = FieldValue(value, tail)
}
case class FieldValue[V, T <: FieldType[_, _]](value: V, tail: T)
object emptyFieldType extends FieldType(classOf[Null], null)
and this is an example specification of a record made of String, Int, Date
:
val types = FieldType(
classOf[String], FieldType(
classOf[Int], FieldType(
classOf[java.util.Date], emptyFieldType
)
)
)
By using the addValue
method in the FieldType
type, the compiler recognises the argument types at any depth:
val a = types.addValue("") // here only String allowed
val b = types.tail.addValue(23) // here only Int allowed
val c = types.tail.tail.addValue(new java.util.Date()) // here only Date allowed
But....
I haven't yet found a way to implement a forwarding method on the FieldValue
type, to achieve a fluent interface as in my first example:
val record = types.addValue("test").addValue(123).addValue(new java.util.Date())
This is a pseudo-code to give the idea:
case class FieldValue[V, T <: FieldType[_, _]](value: V, tail: T) {
def addValue(x: What Here??) = tail.addValue(x) // not compiling!
}
I think it must be possible, since the type information for the next argument to addValue
is contained in FieldValue
via the tail
member, as V
. But I could not find a way to make this information available to the addValue
method, to let the compiler validate the type of the argument value, and the IDE to suggest the correct type.
Upvotes: 2
Views: 172
Reputation: 108169
You may want to take a look at shapeless.
Here's a trivial example demonstrating how you can statically specify the type of an heterogeneous list:
type R = String :: Int :: java.util.Date :: HNil
val record: R = "test" :: 123 :: new java.util.Date() :: HNil
// record: R = test :: 123 :: Thu Aug 14 00:21:52 CEST 2014 :: HNil
val record: R = "test" :: "foo" :: new java.util.Date() :: HNil
// error: type mismatch;
found : shapeless.::[String,shapeless.::[String,shapeless.::[java.util.Date,shapeless.HNil]]]
required: R
(which expands to) shapeless.::[String,shapeless.::[Int,shapeless.::[java.util.Date,shapeless.HNil]]]
val record: R = "test" :: "hola" :: new java.util.Date() :: HNil
^
I don't know whether this already fits your needs, but in any case you should definitely check all the features of shapeless, as it provides many facilities for this kind of generic programming
Upvotes: 5