Pergunta

Gostaria de mapear os elementos de uma tupla de Scala (ou triplo, ...) usando uma única função que retornando tipo R. O resultado deve ser uma tupla (ou triplo, ...) com elementos do tipo R.

Ok, se os elementos da tupla são do mesmo tipo, o mapeamento não é um problema:

scala> implicit def t2mapper[A](t: (A,A)) = new { def map[R](f: A => R) = (f(t._1),f(t._2)) }
t2mapper: [A](t: (A, A))java.lang.Object{def map[R](f: (A) => R): (R, R)}

scala> (1,2) map (_ + 1)
res0: (Int, Int) = (2,3)

Mas também é possível tornar essa solução genérica, ou seja, mapear tuplas que contêm elementos de diferentes tipos da mesma maneira?

Exemplo:

class Super(i: Int)
object Sub1 extends Super(1)
object Sub2 extends Super(2)

(Sub1, Sub2) map (_.i)

deve voltar

(1,2): (Int, Int)

Mas não consegui encontrar uma solução para que a função de mapeamento determine o super tipo de sub1 e sub2. Tentei usar os limites do tipo, mas minha ideia falhou:

scala> implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }
<console>:8: error: X is already defined as type X
       implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }
                                                                    ^
<console>:8: error: type mismatch;
 found   : A
 required: X
 Note: implicit method t2mapper is not applicable here because it comes after the application point and it lacks an explicit result type
       implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }

Aqui X >: B parece substituir X >: A. O Scala não suporta limites do tipo em relação a vários tipos? Se sim, por que não?

Foi útil?

Solução

Eu acho que é isso que você está procurando:

implicit def t2mapper[X, A <: X, B <: X](t: (A,B)) = new {
  def map[R](f: X => R) = (f(t._1), f(t._2))
}

scala> (Sub1, Sub2) map (_.i)                             
res6: (Int, Int) = (1,2)

Uma maneira mais "funcional" de fazer isso seria com 2 funções separadas:

implicit def t2mapper[A, B](t: (A, B)) = new { 
  def map[R](f: A => R, g: B => R) = (f(t._1), g(t._2)) 
}       

scala> (1, "hello") map (_ + 1, _.length)                                         
res1: (Int, Int) = (2,5)

Outras dicas

Eu não sou um gênio do tipo Scala, mas talvez isso funcione:

implicit def t2mapper[X, A<:X, B<:X](t: (A,B)) = new { def map[A, B, R](f: X => R) = (f(t._1),f(t._2)) }

A pergunta mais profunda aqui é "Por que você está usando uma tupla para isso?"

As tuplas são hetrogênicas por design e podem conter uma variedade de tipos muito diferentes. Se você quer uma coleção de coisas relacionadas, então você deve usar ... rolo de tambor ... uma coleção!

UMA Set ou Sequence Não terá impacto no desempenho e seria muito melhor para esse tipo de trabalho. Afinal, é para isso que eles foram projetados.

Para o caso em que as duas funções a serem aplicadas não são as mesmas

scala> Some((1, "hello")).map((((_: Int) + 1 -> (_: String).length)).tupled).get
res112: (Int, Int) = (2,5)

A principal razão pela qual forneci esta resposta é que funciona para listas de tuplas (basta alterar alguns para listar e remover o GET).

Isso pode ser facilmente alcançado usando Fale, embora você precise definir a função de mapeamento antes de fazer o mapa:

object fun extends Poly1 {
  implicit def value[S <: Super] = at[S](_.i) 
}

(Sub1, Sub2) map fun // typed as (Int, Int), and indeed equal to (1, 2)

(Eu tive que adicionar um val à frente de i na definição de Super, Por aqui: class Super(val i: Int), para que possa ser acessado fora)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top