Use functional combinators on Scala Tuples?

ぃ、小莉子 提交于 2020-01-09 02:13:05

问题


'map' preserves the number of elements, so using it on a Tuple seems sensible.

My attempts so far:

scala> (3,4).map(_*2)    
error: value map is not a member of (Int, Int)
       (3,4).map(_*2)
             ^
scala> (3,4).productIterator.map(_*2)
error: value * is not a member of Any
       (3,4).productIterator.map(_*2)
                                  ^
scala> (3,4).productIterator.map(_.asInstanceOf[Int]*2)
res4: Iterator[Int] = non-empty iterator

scala> (3,4).productIterator.map(_.asInstanceOf[Int]*2).toList
res5: List[Int] = List(6, 8)

It looks quite painful... And I haven't even begun to try to convert it back to a tuple.
Am I doing it wrong? Could the library be improved?


回答1:


shapeless Supports mapping and folding over tuples via an intermediary HList representation,

Sample REPL session,

scala> import shapeless._ ; import Tuples._
import shapeless._
import Tuples._

scala> object double extends (Int -> Int) (_*2)
defined module double

scala> (3, 4).hlisted.map(double).tupled
res0: (Int, Int) = (6,8)

Where the elements of the tuple are of different types you can map with a polymorphic function with type-specific cases,

scala> object frob extends Poly1 {
     |   implicit def caseInt     = at[Int](_*2)
     |   implicit def caseString  = at[String]("!"+_+"!")
     |   implicit def caseBoolean = at[Boolean](!_)
     | }
defined module frob

scala> (23, "foo", false, "bar", 13).hlisted.map(frob).tupled
res1: (Int, String, Boolean, String, Int) = (46,!foo!,true,!bar!,26)

Update

As of shapeless 2.0.0-M1 mapping over tuples is supported directly. The above examples now look like this,

scala> import shapeless._, poly._, syntax.std.tuple._
import shapeless._
import poly._
import syntax.std.tuple._

scala> object double extends (Int -> Int) (_*2)
defined module double

scala> (3, 4) map double
res0: (Int, Int) = (6,8)

scala> object frob extends Poly1 {
     |   implicit def caseInt     = at[Int](_*2)
     |   implicit def caseString  = at[String]("!"+_+"!")
     |   implicit def caseBoolean = at[Boolean](!_)
     | }
defined module frob

scala> (23, "foo", false, "bar", 13) map frob
res1: (Int, String, Boolean, String, Int) = (46,!foo!,true,!bar!,26)



回答2:


In general, the element types of a tuple aren't the same, so map doesn't make sense. You can define a function to handle the special case, though:

scala> def map[A, B](as: (A, A))(f: A => B) = 
     as match { case (a1, a2) => (f(a1), f(a2)) } 
map: [A,B](as: (A, A))(f: (A) => B)(B, B)

scala> val p = (1, 2)    
p: (Int, Int) = (1,2)

scala> map(p){ _ * 2 }
res1: (Int, Int) = (2,4)

You could use the Pimp My Library pattern to call this as p.map(_ * 2).

UPDATE

Even when the types of the elements are not the same, Tuple2[A, B] is a Bifunctor, which can be mapped with the bimap operation.

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val f = (_: Int) * 2
f: (Int) => Int = <function1>

scala> val g = (_: String) * 2
g: (String) => String = <function1>

scala> f <-: (1, "1") :-> g
res12: (Int, String) = (2,11)

UPDATE 2

http://gist.github.com/454818




回答3:


map function gets an A => B and returns F[B].

def map[A, B](f: A => B) : F[B]

As retronym wrote Tuple2[A, B] is a Bifunctor, so you can look for the bimap function in scalaz or cats.
bimap is a function that maps both sides of the tuple:

def bimap[A, B, C, D](fa: A => C, fb: B => D): Tuple2[C, D]

Because Tuple[A, B] holds 2 values and only one value can be mapped (by convention the right value), you can just return the same value for the left side and use the right function to map over the right value of the tuple.

(3, 4).bimap(identity, _ * 2)


来源:https://stackoverflow.com/questions/2339863/use-functional-combinators-on-scala-tuples

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!