Necesito escribir una función que recibirá una función que transformará un List[T] en un T. Por ejemplo, sumando los elementos de la lista.

Mi primer intento fue usar funciones polimórficas sin forma como:

object sumPoly extends (List ~> Option) = {
  def apply[T: Numeric](list: List[T]): Option = Some(list.sum)
}

Pero obtengo un error porque sin forma espero que la función sea def apply[T](...)

La función que recibe la función anterior se vería así:

def test(list: List[Any], f: (List ~> Option)) = {
  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

¿Hay alguna forma de lograr lo que quiero hacer?

EDITAR

Después de algunas búsquedas, descubrí que es posible hacer lo siguiente:

object sumPoly extends Poly1 {
  implicit def caseListInt = at[List[Int]](x => x.sum)
  implicit def caseListDouble = at[List[Double]](x => x.sum)
  // more type matchings here
}

Y llamar a sumPoly(List(1, 1, 1)) correctamente devuelve 3. Pero si defino la función test como:

def test(list: List[Any], f: Poly1) = {
  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

Y paso la función sumPoly, obtengo errores como este para cada tipo que definí en la función test: could not find implicit value for parameter cse: shapeless.poly.Case[f.type,shapeless.::[List[Int],shapeless.HNil]]

¿Alguna idea?

1
gire 15 ene. 2017 a las 22:52
¿Puede explicar por qué una función List[T] => T implementada en términos de sum, o más generalmente foldLeft/Right no es adecuada para esto?
 – 
Miles Sabin
19 ene. 2017 a las 12:50
Todavía estoy aprendiendo Scala, busqué foldLeft / Right y parece que la función no ayudará si entiendo correctamente su comentario. La función f que recibe test debe realizar una operación de suma, mínimo, máximo, producto, etc. en la lista. El método test sería parte de una clase genérica de la que se derivarían clases concretas, una clase por operación. Básicamente, quiero reducir la repetición del código (casos de coincidencia de tipos) pasando la función f en lugar de repetir la coincidencia de tipos en cada una de las clases concretas
 – 
gire
19 ene. 2017 a las 23:08

1 respuesta

La mejor respuesta

Al mirar el código sin forma, descubrí que puedo hacer lo siguiente:

object sumPoly extends Poly1 {
  implicit def caseList[T: Numeric] = at[List[T]] { x => x.sum }
}

Y luego la función que recibirá el Poly1 debe tener la siguiente definición:

def test(list: List[Any], f: Poly1) 
        (implicit li : f.Case[List[Int]], 
                  ld : f.Case[List[Double]], 
                  ll : f.Case[List[Long]])= {

  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

De esta manera, puedo crear diferentes funciones de Poly1 como sum, prod, min, max que convierten List[T] en un T donde T es numérico.

El diseño puede parecer artificial, pero estoy interoperando con Java. Más específicamente, con las bibliotecas de Hadoop escritas en Java.

0
gire 22 ene. 2017 a las 01:15