简体   繁体   中英

Scala method type-parameter specification for returning various numeric types

I'm trying to understand scala type parameters and having a bit of difficulty figuring out how to do conversions.

Suppose I'm modeling tic-tac-toe:

sealed trait Side {

  def toChar   : Char
  def toInt    : Int
  def toDouble : Double = toInt.toDouble

}//end trait Side

case object X extends Side {
  override val toChar = 'X'
  override val toInt = -1
}//end case object X extends Side

case object O extends Side {
  override val toChar = 'O'
  override val toInt = 1
}//end case object O extends Side

case object EMPTY extends Side {
  override val toChar = '*'
  override val toInt = 0
}

I then model a tic tac board in a way that both naturally represents the game and also can be converted to a numeric array that is useful with machine learning techniques.

class Board(val rows : Int = 3, 
            val cols : Int = 3, 
            val repr : Array[Array[Side]] = Array.ofDim[Side](3,3)) {

  def flatten : Array[Side] = repr.flatten

  /**
   * Converts this board into a 1-D numeric array.  This is likely the 
   * incorrect method signature.
   */
  def flattenNumeric[T] : Array[T] = ???

}

My question is: how do I replace the ??? ?

I would like to be able to make calls of the form

val board = new Board

board.flattenNumeric[Int] //returns Array(0, 0, 0, 0, 0, 0, 0, 0, 0)
board.flattenNumeric[Double] //returns Array(0.0, 0.0, ...)

Thank you in advance for your assistance.

If there are only two type parameters you need this to work with, I'd suggest just writing separate flattenInt and flattenDouble methods—it's much simpler and clearer. That's not an answer to your question, though, so if you really want this to be generic, the standard Scala way would be to use a type class like this:

class Board(val rows: Int = 3, 
            val cols: Int = 3, 
            val repr: Array[Array[Side]] = Array.ofDim[Side](3,3)) {

  def flatten: Array[Side] = repr.flatten

  trait FromSide[A] {
    def apply(side: Side): A
  }

  object FromSide {
    implicit object intFromSide extends FromSide[Int] {
      def apply(side: Side) = side.toInt
    }

    implicit object doubleFromSide extends FromSide[Double] {
      def apply(side: Side) = side.toDouble
    }

    implicit object charFromSide extends FromSide[Char] {
      def apply(side: Side) = side.toChar
    }
  }

  /**
   * Converts this board into a 1-D numeric array.  This is likely the 
   * incorrect method signature.
   */
  def flattenNumeric[T: FromSide: Manifest]: Array[T] =
    flatten.map(implicitly[FromSide[T]].apply)
}

Now eg board.flattenNumeric[Int] will compile, but flattenNumeric[String] won't.

Note that the Manifest part is only necessary because you're using Array —if you switch to Scala's own collection types you wouldn't need it.

It's also possible to do this by using manifests or type tags, but that's a quick way to make your program a horrible mess of runtime errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM