tags:

views:

96

answers:

4

I'm pretty sure this is very simple to do in Scala, but I can't seem to figure out what hints the type system needs to make this work.

I want to have an abstract Printable class and then implicitly convert other classes to it. More specifically I want to implicitly convert Byte to Printable and Array[Byte] to Printable.

So I've done this:

abstract class Printable{
    def print():String
}
class PrintableByte(b:Byte) extends Printable{
    def print() = "" /*return something*/
}
implicit def printableByte(b:Byte) = new PrintableByte(b)

class PrintableArray(a:Array[Printable]) extends Printable{
    def print() = {
        for(i <- 0 until a.length) a(i).print()   // no problems here
        "" /*return something*/
    }
}
implicit def printableArray(a:Array[Printable]) = new PrintableArray(a)

However:

val b:Byte = 0
b.print()   //no problem here
val a= new Array[Byte](1024)
a.print()   //error: value print() is not a member of Array[Byte]

I expected that the type system would be able to understand that Array[Byte] is implicitly an Array[Printable] and implicitly a Printable.

What am I missing?

+1  A: 

For a.print() to type check, two implicit conversions would be required, one from Byte to PrintableByte and another from Array[PrintableByte] to PrintableArray, but implicits will not combine in this manner. Only one implicit conversion will be applied in pursuit of making a non-type-checking expression properly type check.

Randall Schulz
A: 

Randall has already given the reason why your code does not work the way it is. In your case, you can just change your PrintableArray as follows, and then it works as you described (using scala 2.8.0.RC7):


abstract class Printable{
    def print():String
}
class PrintableByte(b:Byte) extends Printable{
    def print() = b.toString
}
implicit def printableByte(b:Byte) = new PrintableByte(b)

class PrintableArray(a:Array[Byte]) extends Printable {
    def print() = {
        var s:String = ""
        for(i <- 0 until a.length) s += a(i).print()
        s
    }
}
implicit def printableArray(a:Array[Byte]) = new PrintableArray(a)

scala> val a = new Array[Byte](10)
a: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

scala> a print
res0: String = 0000000000
Arjan Blokzijl
That works, but it's not generic. I want to convert any Array of Printable objects to a Printable.
hbatista
+3  A: 

Aswering my own question here, what I was needing was a view bound (new to me, still learning) on the Array type, modifying the code like this:

class PrintableArray[T <% Printable](a:Array[T]) {...}
implicit def printableArray[T <% Printable](a:Array[T]) {...}

This states that there is an implicit conversion (a view) from type T to Printable, and is the same as doing this:

class PrintableArray[T](a:Array[T])(implicit t2printable: T => Printable) {...}
implicit def printableArray[T](a:Array[T])(implicit t2printable: T => Printable) {...}

Not sure if this is the best way to do this, any suggestions for improvement are welcome!

hbatista
+5  A: 

Just my two cents:

abstract class Printable{
    def print: String
}

class PrintableArray[T <% Printable](a: Array[T]) extends Printable{
    def print = (for(x <- a) yield x.print) mkString
}
implicit def printableArray[T <% Printable](a: Array[T]) = new PrintableArray(a)

class PrintableByte(b: Byte) extends Printable{
    def print = "%#02x" format b
}
implicit def printableByte(b:Byte) = new PrintableByte(b)

class PrintableInt(i: Int) extends Printable{
    def print = "%#08x" format i
}
implicit def printableInt(i: Int) = new PrintableInt(i)

And then:

scala> val b: Byte = -1
b: Byte = -1

scala> val i: Int = 100
i: Int = 100

scala> val bArr = Array[Byte](11,22,33,44)
bArr: Array[Byte] = Array(11, 22, 33, 44)

scala> val iArr = Array[Int](111111,222222,333333,444444)
iArr: Array[Int] = Array(111111, 222222, 333333, 444444)

scala> b print
res0: String = 0xff

scala> i print
res1: String = 0x000064

scala> bArr print
res2: String = 0xb0x160x210x2c

scala> iArr print
res3: String = 0x01b2070x03640e0x0516150x06c81c

scala>
Eastsun
Eventually found out the *view bound* thing by myself, but accepting your answer! Thanks!
hbatista