Why is it looking for String?
Given the following code:
abstract class Field {
type T
val data: List[T]
def sum: T = data(0) + data(1)
}
I get an error on the last line - def sum: T = data(0) + data(1)
:
types2.scala:6: error: type mismatch;
found : Field.this.T
required: String
def sum: T = data(0) + data(1)
^
That is, it expects data(1) to be a String
.
I dont understand why... (scala 2.8.1)
Your explanation will be much 开发者_如何学编程appreciated!
Since T
does not support an addition operation, compiler assumes +
to be a string concatenation operation. The following line I tried out at REPL indicates so:
scala> implicitly[Any => {def +(s: String): String}]
res16: (Any) => AnyRef{def +(s: String): String} = <function1>
What you can do is require that T
have a Semigroup
algebra defined. (A type is a semigroup if it supports an associative append operation.)
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> abstract class Field[A : Semigroup] {
| val data: IndexedSeq[A]
| def sum: A = data(0) |+| data(1)
| }
defined class Field
scala> val f = new Field[Int] {
| val data = IndexedSeq(2, 3, 4)
| }
f: Field[Int] = $anon$1@d1fd51
scala> f.sum
res12: Int = 5
I replaced abstract type by a type parameter simply because I do not know how to put a context bound on an abstract type. I also changed type of data from List[A]
to IndexedSeq[A]
because as the name indicates indexed sequences are more suitable for indexed access than lists (which is what you do in your sum
method). And finally, |+|
is the semigroup append operation. For numeric types it will perform addition. For sequences, concatenation etc.
As a complement to @missingfactor's answer, while in principle I would very much favor Semigroup
, there is a trait Numeric
in the standard library which would do the same. And on collections whose content is Numeric
(where a "Numeric
structure" exists for the elements' type), you can simply call collection.sum
(should you want to sum all the elements rather than the two first ones).
I prefer Semigroup
for two reasons. First Numeric
is much more than what is needed here, second, what are the exact properties of a Numeric
structure is not clear. On the other hand, even someone not familiar with basic algebra will have a reasonable understanding of what Numeric means.
So if you are afraid of scalaz and/or semigroups, you can replace Semigroup
with Numeric
and |+|
with +
. You must import Numeric.Implicits._
so that +
is available.
The compiler doesn't know how to invoke +
in your type T
, because it knows nothing about T
. The only solution to compile this +
is then a pimped string concatenation (by means of the implicit Predef.any2stringadd
), which expects a string as second argument — hence the error you're getting.
After a lot of playing with this, I came up with a very simple solution. Here is the full program
package manytypes
abstract class Field {
type T
val data: List[T]
def add (a: T, b: T): T
}
abstract class FieldInt extends Field {
type T = Int
def add (a: T, b: T): T = a + b
}
abstract class FieldDouble extends Field {
type T = Double
def add (a: T, b: T): T = a + b
}
abstract class FieldString extends Field {
type T = String
def add (a: T, b: T): T = a + b
}
object A extends App {
val ints: Field = new FieldInt { val data = List(1, 2, 3)}
val doubles: Field = new FieldDouble { val data = List(1.2, 2.3, 3.4) }
val strings: Field = new FieldString { val data = List("hello ", "this is ", "a list ")}
val fields: List[Field] = List(ints, doubles, strings)
for (field <- fields) println(field.data.reduceLeft(field.add(_, _)))
}
精彩评论