Общая функция 'map' для кортежей Scala? - PullRequest
9 голосов
/ 26 октября 2010

Я хотел бы отобразить элементы кортежа Scala (или тройного, ...), используя единственную функцию, возвращающую тип R. Результатом должен быть кортеж (или тройной, ...) с элементами типа R.

ОК, если элементы кортежа относятся к одному и тому же типу, сопоставление не является проблемой:

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)

Но возможно ли сделать это решение универсальным, т. Е. Сопоставить кортежи, содержащие элементы разных типов, одинаковым образом?

Пример:

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

(Sub1, Sub2) map (_.i)

должен вернуть

(1,2): (Int, Int)

Но я не смог найти решение, чтобы функция отображения определяла супертип Sub1 и Sub2. Я пытался использовать границы типов, но моя идея не удалась:

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)) }

Здесь X >: B, кажется, перекрывает X >: A. Разве Scala не поддерживает границы типов для нескольких типов? Если да, то почему?

Ответы [ 5 ]

11 голосов
/ 26 октября 2010

Я думаю, это то, что вы ищете:

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)

Более "функциональный" способ сделать это - две отдельные функции:

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)
4 голосов
/ 26 октября 2010

Я не гений типа скала, но, возможно, это работает:

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)) }
0 голосов
/ 20 июля 2014

Этого легко достичь, используя бесформенный , хотя вам придется сначала определить функцию отображения, прежде чем создавать карту:

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)

(мне пришлось добавить val перед i в определении Super, таким образом: class Super(val i: Int), чтобы к нему можно было получить доступ снаружи)

0 голосов
/ 30 ноября 2013

Для случая, когда две применяемые функции не являются одинаковымиполучить).

0 голосов
/ 26 октября 2010

Более глубокий вопрос здесь: «Почему вы используете для этого кортеж?»

Кортежи по своей конструкции являются гетерогенными и могут содержать ассортимент самых разных типов.Если вы хотите коллекцию связанных вещей, то вы должны использовать ... барабанную дробь ... коллекцию!

A Set или Sequence не окажут влияния напроизводительность, и будет гораздо лучше подходить для такой работы.В конце концов, это то, для чего они предназначены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...