Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

alternative to breakOut that is less verbose

Tags:

scala

breakOut is nice, but too verbose:

List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(breakOut) : Array[(Int, Double, String)]

I don't want to specify the element type. I want something like:

 List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array])

I can write a buildArray easily, but then I'd need a buildSet, buildList, etc. So I want something generic.

Extra points ( ;-) ) if you can make it work for Map (using the same name build, not build2 or buildMap)

like image 558
IttayD Avatar asked Jul 07 '14 07:07

IttayD


2 Answers

This will not work for String or Map. Also this code requires scala.language.higherKinds:

import collection.generic.CanBuildFrom
import collection.breakOut

class Build[To[_]]

def build[To[_]] = new Build[To]

implicit def buildToCbf[From, T, To[_]](b: Build[To])
                                       (implicit cbf: CanBuildFrom[Nothing,T,To[T]]): CanBuildFrom[From,T,To[T]] =
  collection.breakOut

List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array])
//res0: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))
like image 63
senia Avatar answered Nov 12 '22 03:11

senia


Senia's solution is great. Unfortunately as he mentioned this won't work for Map nor String. Here is another alternative (based on his solution), that does:

import collection.generic.CanBuildFrom
import collection.breakOut

class Build[To]

def build[TargetSuperType] = new Build[TargetSuperType]

implicit def buildToCbf[From, T, TargetSuperType, To<:TargetSuperType](b: Build[TargetSuperType])
                                       (implicit cbf: CanBuildFrom[Nothing,T,To]): CanBuildFrom[From,T,To] =
  collection.breakOut

List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array[_]])
//res0: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))

List(1, 2, 3).map{i => (i * 2, i.toString)}(build[Map[_,_]])
//res1: scala.collection.immutable.Map[Int,String] = Map(2 -> 1, 4 -> 2, 6 -> 3)

List('a', 'b', 'c').map(_.toUpper)(build[String])
//res2: String = ABC

This is little more verbose because now you don't just do build[Array], but build[Array[_]]. In exchange you get the ability to specify any target collection you want, irrespective of the number of type arguments (such as Map and String).

Plus you can still be fully explicit (similarly to when using breakOut) if you choose to:

scala> List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array[(Int, Double, String)]])
res3: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))

All that with the same syntax (in other words, using the same name build, as you requested)

like image 38
Régis Jean-Gilles Avatar answered Nov 12 '22 04:11

Régis Jean-Gilles