开发者

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(_, _)))
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜