Scala tuple question - sql

New to Scala, but experienced in C++ I'm trying to implement (possibly misguidedly) a small library on top of the sqlite4java library to allow me to auto-fill tuples of abritrary type from query rows (rows whose column types are each compatible with the respective tuple element type).
In C++ I normally implement this using boost::tuples and compile-time template recursion (terminated using template specialisation as shown below). Boost tuples are implemented very similarly to Haskell HLists. The pattern would be (assuming, for simplicity that the query is returned as vector of strings):
template<typename T1, typename T2>
void populateTuple( boost::tuples::cons<T1, T2>& tupleRec, int index, const std::vector<std::string>& vals )
{
tupleRec.head = boost::lexical_cast<T1>( vals[index] );
populateTuple( tupleRec.tail, index+1, vals );
}
template<typename T>
void populateTuple( boost::tuples::cons<T, boost::tuples::null_type>& tupleRec, int index, const std::vector<std::string>& vals )
{
tupleRec.head = boost::lexical_cast<T>( vals[index] );
}
(Apologies - I've not run the above through a compiler, but am hoping it shows what I mean)
I'd love to be able to do something similar with Scala. I can recurse over a general Tuple type via the Product trait - and get the type of each element at run time using pattern matching (for the small number of column types I support). However I haven't found a way to assign Tuple elements via the product trait. And to be honest, I'm not convinced that this is a particularly nice or idiomatic way to do what I require anyway.
But something like:
val returnedFromQuery = List[String]( "Hello", "4", "6.0" )
val rowAsTuples = interpretListAsTuple[(String, Int, Float)]( returnedFromQuery )
Where rowAsTuples has type (String, Int, Float). Again, please excuse any syntax errors.
Anyone have any thoughts? Or alternative suggestions? In advance - I'm not interested in any higher-level SQL query libraries. I'm happy with sqlite4java but want to wrap it with a simple more abstract interface.

I think you should try using pattern-matching instead of interpretation. First, we need something that will pull out our types from strings using unapply:
object Stringy {
def unapply(s: String) = Some(s)
}
object Inty {
def unapply(s: String) = {
try { Some(s.toInt) }
catch { case nfe: NumberFormatException => None }
}
}
object Floaty {
def unapply(s: String) = {
try { Some(s.toFloat) }
catch { case nfe: NumberFormatException => None }
}
}
Now we can use them in pattern matches:
scala> List("Hello","4","6.0") match {
case Stringy(s) :: Inty(i) :: Floaty(f) :: Nil => Some((s,i,f))
case _ => None
}
res3: Option[(String, Int, Float)] = Some((Hello,4,6.0))
Note that if you do it this way, you (1) don't have to return tuples if you don't want to; (2) have access to all the parsed out variables right away; (3) have automatic error checking built in (with options).

Try HList from the MetaScala library.

I think you got problems with dynamic tuple arity, so you have to implement a method for each tuple arity, something like that:
def interpretListAsTuple2[A,B](s: List[String])(implicit s2a: String => A, s2b: String => B) = {
s.grouped(2).map { case x :: y => (x: A, y.head: B) }
}
def interpretListAsTuple3[A,B,C](s: List[String])(implicit s2a: String => A, s2b: String => B, s2c: String => C) = {
s.grouped(3).map { case x :: y :: z => (x: A, y: B, z.head: C) }
}
implicit def string2String(s: String) = s
implicit def string2Int (s: String) = s.toInt
implicit def string2Float (s: String) = s.toFloat
val returnedFromQuery = List( "Hello", "4", "6.0" )
interpretListAsTuple3[String,Int,Float](returnedFromQuery)
Sorry, this code doesn´t work, because of ambiguity of implicit conversions for String to float in scala´s Predef respectively LowPriorityImplicits. Perhaps someone can help and fix that. But the main idea should be clear, though. You only have to define the implicit conversions once for your data-types, then it works with all tuple-aritys.
EDIT:
You can use the above version to map a list with strings of several tuples. List("Hello", "4", "6.0","Hey","1", "2.3") If you want to handle only one tuple then use this:
def interpretListAsTuple3[A,B,C](s: List[String])(implicit s2a: String => A, s2b: String => B, s2c: String => C): (A,B,C) = {
s.grouped(3).map { case x :: y :: z => (x: A, y: B, z.head: C) }.next
}
Of course, arguments have to be well formed.

AFAIK, you can't get around type parameter arity. For evidence of that, take the fact that function and tuples have one definition for each arity, up to the arbitrary arity of 22.
If you nest type declarations, which is what existing implementations of HList do, then you can do something.

So I was asking this question because I wanted to write a simple wrapper around the sqlite4java sqlite interface. To enable code of the following form where the row type from a query could be specified in the prepared statement (I intend to add type checking to the parameters passed based on a similar method):
test("SQLite wrapper test")
{
val db = new SQLiteWrapper()
db.exec( "BEGIN" )
db.exec( "CREATE TABLE test( number INTEGER, value FLOAT, name TEXT )" )
val insStatement = db.prepare( "INSERT INTO test VALUES( ?, ?, ? )", HNil )
insStatement.exec( 1, 5.0, "Hello1" )
insStatement.exec( 2, 6.0, "Hello2" )
insStatement.exec( 3, 7.0, "Hello3" )
insStatement.exec( 4, 8.0, "Hello4" )
val getStatement = db.prepare( "SELECT * from test", Col[Int]::Col[Double]::Col[String]::HNil )
assert( getStatement.step() === true )
assert( _1(getStatement.row) === Some(1) )
assert( _2(getStatement.row) === Some(5.0) )
assert( _3(getStatement.row) === Some("Hello1") )
getStatement.reset()
db.exec( "ROLLBACK" )
}
And to enable this, using a variety of helpful SO suggestions I've come up with the code below. This is my first attempt at any form of generic programming in Scala - I've only been playing with the language for a week or two. So this is code is unlikely to be considered nice/good by the experienced Scala community. Any suggestions/feedback welcomed....
import java.io.File
import com.almworks.sqlite4java._
object SqliteWrapper
{
trait TypedCol[T]
{
var v : Option[T] = None
def assign( res : SQLiteStatement, index : Int )
}
sealed trait HList
{
def assign( res : SQLiteStatement, index : Int )
}
final case class HCons[H <: TypedCol[_], T <: HList]( var head : H, tail : T ) extends HList
{
def ::[T <: TypedCol[_]](v : T) = HCons(v, this)
def assign( res : SQLiteStatement, index : Int )
{
head.assign( res, index )
tail.assign( res, index+1 )
}
}
final class HNil extends HList
{
def ::[T <: TypedCol[_]](v : T) = HCons(v, this)
def assign( res : SQLiteStatement, index : Int )
{
}
}
type ::[H <: TypedCol[_], T <: HList] = HCons[H, T]
val HNil = new HNil()
final class IntCol extends TypedCol[Int]
{
def assign( res : SQLiteStatement, index : Int ) { v = Some( res.columnInt(index) ) }
}
final class DoubleCol extends TypedCol[Double]
{
def assign( res : SQLiteStatement, index : Int ) { v = Some( res.columnDouble(index) ) }
}
final class StringCol extends TypedCol[String]
{
def assign( res : SQLiteStatement, index : Int ) { v = Some( res.columnString(index) ) }
}
trait TypedColMaker[T]
{
def build() : TypedCol[T]
}
object TypedColMaker
{
implicit object IntColMaker extends TypedColMaker[Int]
{
def build() : TypedCol[Int] = new IntCol()
}
implicit object DoubleColMaker extends TypedColMaker[Double]
{
def build() : TypedCol[Double] = new DoubleCol()
}
implicit object StringColMaker extends TypedColMaker[String]
{
def build() : TypedCol[String] = new StringCol()
}
}
def Col[T : TypedColMaker]() = implicitly[TypedColMaker[T]].build()
// Hideousness. Improve as Scala metaprogramming ability improves
def _1[H <: TypedCol[_], T <: HList]( t : HCons[H, T] ) = t.head.v
def _2[H1 <: TypedCol[_], H2 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, T]] ) = t.tail.head.v
def _3[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, T]]] ) = t.tail.tail.head.v
def _4[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, T]]]] ) = t.tail.tail.tail.head.v
def _5[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, T]]]]] ) = t.tail.tail.tail.tail.head.v
def _6[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, T]]]]]] ) = t.tail.tail.tail.tail.tail.head.v
def _7[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, T]]]]]]] ) = t.tail.tail.tail.tail.tail.tail.head.v
def _8[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], H8 <: TypedCol[_], T <: HList]( t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, HCons[H8, T]]]]]]]] ) = t.tail.tail.tail.tail.tail.tail.tail.head.v
final class DataWrapper[T <: HList]( var row : T )
{
def assign( res : SQLiteStatement ) { row.assign( res, 0 ) }
}
final class SQLiteWrapper( dbFile : File )
{
val conn = new SQLiteConnection( dbFile )
conn.open()
def exec( statement : String )
{
conn.exec( statement )
}
def prepare[T <: HList]( query : String, row : T ) =
{
new PreparedStatement(query, row)
}
// TODO: Parameterise with tuple type
// make applicable to for comprehensions (implement filter, map, flatMap)
final class PreparedStatement[T <: HList]( query : String, var row : T )
{
val statement = conn.prepare( query )
private def bindRec( index : Int, params : List[Any] )
{
println( "Value " + params.head )
// TODO: Does this need a pattern match?
params.head match
{
case v : Int => statement.bind( index, v )
case v : String => statement.bind( index, v )
case v : Double => statement.bind( index, v )
case _ => throw new ClassCastException( "Unsupported type in bind." )
}
if ( params.tail != Nil )
{
bindRec( index+1, params.tail )
}
}
def bind( args : Any* )
{
bindRec( 1, args.toList )
}
def exec( args : Any* )
{
bindRec( 1, args.toList )
step()
reset()
}
def reset()
{
statement.reset()
}
def step() : Boolean =
{
val success = statement.step()
row.assign( statement, 0 )
return success
}
}
}
}

Related

app\src\main\kotlin\KotlinApp.kt: (48, 34): Expression 'f[2]' of type 'Any' cnvoked as a function. The function 'invoke()' is not found

I have been running an algorithm that is designed to take a postfix expression and return the evaluated value.
To carry out the individual functions, I designed an array of lists that basically functions as:
string representation of operation: String, values taken (-1): Integer, function describing what to do: Float
I wrote this as
val functions = arrayOf<List<Any>>(
listOf("+",1,fun (i:List<Float>):Float = i[0] + i[1]),
listOf("-",1,fun (i:List<Float>):Float = i[0] - i[1]),
// etc (rest of them are currently commented out)
)
On compiling this I got the error:
app\src\main\kotlin\KotlinApp.kt: (48, 34): Expression 'f[2]' of type 'Any' cnvoked as a function. The function 'invoke()' is not found
This is referring to how I call f[2](temp:List<Float>) at element f of the array.
I recognise the error here is how as my lists are of type any, the functions inside them aren't assumed to be functions by the compiler. But as I obviously can't cast these to a function, I'm at a loss.
I have tried using lambda expressions instead of anonymous single-expression functions, i.e.
val functions = arrayOf<List<Any>>(
listOf("+",1,i : List<Float> -> i[0] + i[1],
listOf("-",1,i : List<Float> -> i[0] - i[1]),
// etc
)
But as I'm not particularly familiar with lambdas, I don't know whether I did it correctly.
Any help would be appreciated
Full KotlinApp.kt file:
package gradleMain;
import org.shuntingyard.sya
import kotlin.math.*
import java.util.Stack
class KotlinApp {
val functions = arrayOf<List<Any>>(
listOf("+",1,fun (i:List<Float>):Float = i[0] + i[1]),
listOf("-",1,fun (i:List<Float>):Float = i[0] - i[1]),
// listOf("*",1,{ i : List<Float> -> i[0]*i[1]}),
// listOf("/",1,{ i : List<Float> -> i[0]/i[1]}),
// listOf("^",1,{ i : List<Float> -> i[0].pow(i[1])}),
// listOf("sin",0,{ i : List<Float> -> sin(i[0])}),
// listOf("cos",0,{ i : List<Float> -> cos(i[0])}),
// listOf("tan",0,{ i : List<Float> -> tan(i[0])}),
// listOf("asin",0,{ i : List<Float> -> asin(i[0])}),
// listOf("acos",0,{ i : List<Float> -> acos(i[0])}),
// listOf("atan",0,{ i : List<Float> -> atan(i[0])}),
// listOf("sinh",0,{ i : List<Float> -> sinh(i[0])}),
// listOf("cosh",0,{ i : List<Float> -> cosh(i[0])}),
// listOf("tanh",0,{ i : List<Float> -> tanh(i[0])}),
// listOf("fact",0, /* TODO: Need to incorporate gamma function, once established integrals" */ ),
// listOf("int",1,/* TODO: Incorporate integrals */),
// listOf("dif",1,/* TODO: Incorporate differentiation */),
// listOf("log",0,{ i : List<Float> -> log(i[0], 10F)}),
// listOf("ln",0,{ i : List<Float> -> ln(i[0])}),
// listOf("sqrt",0,{ i : List<Float> -> i[0].pow(0.5F)}),
// listOf("sec",0,{ i : List<Float> -> 1/cos(i[0])}),
// listOf("csc",0,{ i : List<Float> -> 1/sin(i[0])}),
// listOf("cot",0,{ i : List<Float> -> 1/tan(i[0])})
)
fun calculate(eq:List<String>):Float{ // takes the rpn of the equation and returns the result
var operand: Stack<String> = Stack()
var temp = listOf<Float>()
for(i in eq){
var n = false
for(f in functions){
if(i==f[0]){
val x: Int = f[1].toString().toInt()
for(j in 0..x){
temp+=operand.pop().toFloat()
}
operand.push(f[2](temp))
n = true
break;
}
}
if(!n){ // when i isnt an operator/function
operand.push(i)
}
}
val result = operand.peek().toFloat()
return result
}
}
fun main(){
val k = KotlinApp()
val e = sya(" 3 + 7 - 2") // runs shunting yard algorithm on equation
val f = k.calculate(e)
println("$f")
}
Note: I didnt look at your logic. Helping just to solve your problem of accessing function.
So here is the thing.
Instead of List of List, you can use Triple object which will help to hold the type of the variable.Since you have only three items in inner list triple might suits you. Because the inner list you are having of multiple types. So you can do typecasting , but it is not recommended.
You can define a function type using TypeAlias for better readability.
Calculate function:
fun calculate(eq:List<String>):Float{ // takes the rpn of the equation and returns the result
var operand: Stack<String> = Stack()
val temp = mutableListOf<Float>()
for (i in eq){
var n = false
for (f in functions){
if (i == f.first){
val x: Int = f.second.toString().toInt()
for(j in 0..x){
temp+=operand.pop().toFloat()
}
operand.push(f.third(temp).toString()) // Float to String
n = true
break
}
}
if(!n){ // when i isnt an operator/function
operand.push(i)
}
}
val result = operand.peek().toFloat()
return result
}
Custom type of the function:
typealias MyFunctionType = (a:List<Float>) -> Float
List of functions:
val functions = arrayListOf<Triple<String,Int,MyFunctionType>>(
Triple("+",1,fun (i:List<Float>):Float = i[0] + i[1]),
Triple("-",1,fun (i:List<Float>):Float = i[0] + i[1])
)

Generic Kotlin factory to DRY constructor parameters

I have the following simple class hierarchy.
abstract class B {
abstract val o : Int
}
class D1 (m: Int, n: Int) : B() {
override val o = m + n
}
class D2 (m: Int, n: Int) : B() {
override val o = m * n
}
I need a "factory function" f that gives me instances of D1 or D2 by calling it as f<D1>() or f<D2>() with hard coded parameters, say 3 and 4. The following doesn't work but illustrates what I need:
// won't compile; just demonstrates what I need
fun < T : B > f () : T {
return T(3, 4) // i. e. return T.constructor(m, n)
}
How to best accomplish this? Any DRY way is fine as long I don't have to repeat 3, 4 all over my code when I instantiate D1 or D2
The only way to do it is via reflection and reified type parameter:
inline fun <reified T : B> f(m: Int = 3, n: Int = 4): T {
val constructor = T::class.constructors.first {
it.parameters.size == 2 &&
it.parameters.all { param -> param.type == Int::class }
}
return constructor.call(m, n)
}
Here's an alternate way without reflection, but you have to manually type out a line for each class you want to handle.
inline fun <reified T : B> f(): T{
val m = 3
val n = 4
return when (T::class) {
D1::class -> D1(m, n)
D2::class -> D2(m, n)
else -> error("Unsupported type ${T::class}")
} as T
}

Writing an addition visitor function in Kotlin

I'm trying to write a visitor function in Kotlin that adds two integers together. I've been working off of some sample code and I can't figure out what these .value or .visit functions are. It doesn't seem to be declared in the sample code, so I'm unsure how to declare it in my code. Whenever I compile the code, I get an error saying that value is an unresolved reference.
Relevant Kotlin code:
package backend
import org.antlr.v4.runtime.*
import grammar.*
abstract class Data
class IntData(val value: Int): Data() {
override fun toString(): String
= "Int($value)"
}
class Context(): HashMap<String, Data>() {
constructor(parent: Context): this() {
this.putAll(parent)
}
}
abstract class Expr {
abstract fun eval(scope: Context): Data
fun run(program: Expr) {
try {
val data = program.eval(Context())
println("=> ${data}")
} catch(e: Exception) {
println("[err] ${e}")
}
}
}
class IntLiteral(val value: Int): Expr() {
override fun eval(scope:Context): Data
= IntData(value)
}
enum class Op {
Add,
Sub,
Mul,
Div
}
class Arithmetic(
val op: Op,
val left: Expr,
val right: Expr): Expr() {
override fun eval(scope: Context): Data {
val x = (left.eval(scope) as IntData).value
val y = (right.eval(scope) as IntData).value
return IntData(
when(op) {
Op.Add -> x + y
Op.Mul -> x * y
Op.Sub -> x - y
Op.Div -> x / y
}
)
}
}
}
class Compiler: PLBaseVisitor<Expr>() {
val scope = mutableMapOf<String, Expr>()
override fun visitAddExpr(ctx: PLParser.AddExprContext): Expr {
val xValue = this.visit(ctx.x)
val yValue = this.visit(ctx.y)
val result = xValue.value + yValue.value
return IntLiteral(result)
}
}
Relevant Antlr Grammar:
expr : x=expr '+' y=expr # addExpr
| x=expr '-' y=expr # subExpr
| x=expr '*' y=expr # mulExpr
| x=expr '/' y=expr # divExpr
;
Code I'm trying to execute:
val test = """
x=1+2
print(x)
"""
fun parse(source: String): PLParser.ProgramContext {
val input = CharStreams.fromString(source)
val lexer = PLLexer(input)
val tokens = CommonTokenStream(lexer)
val parser = PLParser(tokens)
}
val testTree = parse(source1)
val testTree = parse(source1)
fun execute(program: Expr?) {
if(program == null) {
println("Program is null.")
return
}
try {
val data = program.eval(Context())
println("> ${data}")
} catch(e: Exception) {
println("[err] ${e}")
}
}
execute(testProgram)
Code from sample:
data class NodeValue(val value: Int)
val visitor = object: CalcBaseVisitor<NodeValue>() {
override fun visitAddition(ctx: CalcParser.AdditionContext): NodeValue {
val xValue = this.visit(ctx.x)
val yValue = this.visit(ctx.y)
return NodeValue(xValue.value + yValue.value)
}
override fun visitValue(ctx: CalcParser.ValueContext): NodeValue {
val lexeme = ctx.Number().getText()
return NodeValue(lexeme.toInt())
}
}
You don’t show the code for your program.eval() method.
The eval function would need to create an instance of your Visitor. (You’ve done that and called it visitor).
You also have the root expr node in you program variable.
Now you would have your visitor “visit” that node and save the return value:
val nodeVal = visitor.visit(program)
At that point nodeVal.value will have the result of visiting that expression.
note: since you’re doing the evaluation in your visitor, there’s not really any use for your Arithmetic class (unless you refactor your visitor to use it instead of just doing the math, but I don’t see much value in that as the visitor is already pretty easy to read).

jetbrains exposed - select based on nullable reference column

I'm trying to select the rows from a table based on a nullable referenced column.
If you replace the reference with just a standard integer column and keep it nullable, it handles the eq just fine. I've also tried replacing the reference with optReference, but that didn't make a difference.
The error is given by the compiler.
None of the following functions can be called with the arguments supplied.
Expression<in EntityID<Int>?>.eq(Expression<in EntityID<Int>?>)   where T = EntityID<Int>?, S1 = EntityID<Int>?, S2 = EntityID<Int>? for   infix fun <T, S1 : T?, S2 : T?> Expression<in S1>.eq(other: Expression<in S2>): Op<Boolean> defined in org.jetbrains.exposed.sql.SqlExpressionBuilder
ExpressionWithColumnType<T>.eq(T)   where T cannot be inferred for   infix fun <T> ExpressionWithColumnType<T>.eq(t: T): Op<Boolean> defined in org.jetbrains.exposed.sql.SqlExpressionBuilder
ExpressionWithColumnType<EntityID<Int>>.eq(Int?)   where T = Int for   infix fun <T : Comparable<T>> ExpressionWithColumnType<EntityID<T>>.eq(t: T?): Op<Boolean> defined in org.jetbrains.exposed.sql.SqlExpressionBuilder
A basic working example showing the error given by Intellij.
object A : IntIdTable("a") {
val n = varchar("n", 255)
val x = reference("x", B).nullable()
}
object B : IntIdTable("b") {
val i = varchar("m", 255)
val y = integer("y")
}
fun main() {
connectToDatabase()
transaction {
SchemaUtils.createMissingTablesAndColumns(A, B)
A.select { A.x eq 1 }
}
}
The equivalent sql I'm want it to run is:
select * from test.a as a where a.x = 1;
A.x is not a Column<Int>, its type is actually Column<EntityID<Int>>.
It looks like you need to write the query as
A.select { A.x eq EntityID(1, B) }

scala trait or abstract type or parameter type or what?

I wish to use several classes interchangeably, all of them implementing a method "add", so I start to write a trait:
trait CanAdd{
def add(that: CanAdd): CanAdd
}
then implement my classes and hit a problem: I cannot add any "CanAdd" with any other, they need to be of the same class. I workaround the problem with some ugly code relying on "isInstanceOf":
class A(val value: Int) extends CanAdd{
def add(that: CanAdd): CanAdd = {
if(!that.isInstanceOf[A]) sys.error("")
val thatA: A = that.asInstanceOf[A]
new A(value + thatA.value)
}
}
class B(val value: Boolean) extends CanAdd{
def add(that: CanAdd): CanAdd = {
if(!that.isInstanceOf[B]) sys.error("")
val thatB: B = that.asInstanceOf[B]
new B(value ^ thatB.value)
}
}
Finally, I use the classes as follow:
class User(val stuff: Array[CanAdd]) {
def add(that: User): User = {
assume(stuff.length==that.stuff.length)
val out = new Array[CanAdd](stuff.length)
for( i <- 0 until stuff.length) out(i) = stuff(i).add(that.stuff(i))
new User(out)
}
}
val u1=new User(Array(new A(0)))
val u2=new User(Array(new B(false)))
val u3 = u1.add(u1)
val u4 = u1.add(u2) //should fail, ideally, should not even compile
I don't like it because first it is a burden to write the boiler plate code with "isInstanceOf" and second because it fails at run time instead of compile time.
My question: how would you do that, having in mind some plan to implement many more methods than just "add" and maybe to implement few other classes with very different internal representations ?
trait CanAdd[T <: CanAdd[T]] {
def add(a: T): T
}
class A(val value:Int) extends CanAdd[A] {
def add(that:A) = new A(value+that.value)
}
class B(val value:Boolean) extends CanAdd[B] {
def add(that:B) = new B(value ^ that.value)
}
class User[X <: CanAdd[X] : Manifest](val stuff:Array[X]) extends CanAdd[User[X]]{
def add(that:User[X]):User[X] = {
assume(stuff.length==that.stuff.length)
new User(stuff.zip(that.stuff).map(t => t._1.add(t._2)) toArray)
}
}
val u1 = new User(Array(new A(0)))
val u2 = new User(Array(new B(false)))
val u3 = u1.add(u1)
val u4 = u1.add(u2) // compile error: type mismatch