Calling Scala Monads from Java #map

I have an instance of scala.collection.immutable.List and I want to call it map , but from Java.

I need to specify CanBuildFrom .

I noticed that many of the related objects in the scala collection contain implicit CanBuildFrom instances, but I cannot decide which one I need to use.

Here is my Java code:

  Function1<WeatherData, BigDecimal> mapper = new AbstractFunction1<WeatherData, BigDecimal>(){ @Override public BigDecimal apply(WeatherData data) { return data.getTemps().reduce(adder).divide(new BigDecimal(data.getTemps().size())); } }; scala.collection.immutable.List<WeatherData> data = ... data.map(mapper, ???); 

What should I pass as CanBuildFrom (second parameter?)

PS Using scala 2.10-M5

+7
source share
2 answers

In fact, you can get types directly in Java without too much noise:

 import scala.collection.Traversable; import scala.collection.generic.CanBuildFrom; import scala.collection.immutable.List; import scala.collection.mutable.Builder; import scala.runtime.AbstractFunction1; public class ScalaMapTest { public static List<Integer> parseInts(List<String> xs) { final CanBuildFrom<List<?>, Integer, List<Integer>> builder = List.<Integer>canBuildFrom(); return xs.map( new AbstractFunction1<String, Integer>() { public Integer apply(String s) { return Integer.parseInt(s); } }, new CanBuildFrom<Traversable<String>, Integer, List<Integer>>() { public Builder<Integer, List<Integer>> apply() { return builder.apply(); } public Builder<Integer, List<Integer>> apply(Traversable<String> from) { return builder.apply(from.toList()); } } ); } } 

It is still ugly as a sin, but it works. The problem is that you get the CanBuildFrom from the CanBuildFrom method of the CanBuildFrom object, but fortunately you can create your own CanBuildFrom with the correct type.

+8
source

If you want to know what affects the code, ask him.;)

This is possible with scalac -Xprint:typer <file> or the new Reflection API in 2.10:

 scala> import reflect.runtime.universe._ import reflect.runtime.universe._ scala> reify{List(1,2,3).map(_+1)} res0: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]](immutable.this.List.apply(1, 2, 3).map(((x$1) => x$1.$plus(1)))(immutable.this.List.canBuildFrom)) 

So call map with this CanBuildFrom and everything works fine. It's true? No no! The problem is that the Java compiler should foolishly output the arguments expected by map . So what to do? I believe that the only way is to create the necessary values, and they will drop them to death. Finally, add some SuppressWarnings-Annotations and the code should work fine .;)

Here is what I came up with:

 import scala.Function1; import scala.collection.generic.CanBuildFrom; import scala.collection.immutable.List; import scala.collection.immutable.List$; import scala.runtime.AbstractFunction1; public class JTest { @SuppressWarnings({"unchecked", "rawtypes"}) public static void main(final String... args) { final List<Integer> xxx = (List) List$.MODULE$.apply(Predef.wrapIntArray(new int[] {1,2,3})); System.out.println(xxx); System.out.println(Test.sum(1, 2)); final Abc abc = new Abc(); System.out.println(abc.hello("simon")); final List<Integer> xs = (List) Test.xs(); final Function1<Integer, String> mapper = new AbstractFunction1<Integer, String>() { @Override public String apply(final Integer i) { return String.valueOf(i); } }; final CanBuildFrom<List<Integer>, String, List<String>> cbf = (CanBuildFrom) List.<Integer>canBuildFrom(); final List<String> ys = xs.<String, List<String>>map(mapper, cbf); System.out.println(ys); } } 

List:

 object Test { def xs = List(1,2,3) } 

I find it best not to use Scala code in Java. It looks ugly. At the very least, wrap it in some helper classes so that anyone who sees this code does not think about an acid attack on the retina.

By the way, sometimes you have to look at Bytecode to understand how scalac creates values. If you want to create Scala List is Java, you need to decompile the code using javap -c -s -l -verbose -private <classfile> :

  0: aload_0 1: invokespecial #20; //Method java/lang/Object."<init>":()V 4: aload_0 5: putstatic #22; //Field MODULE$:LX$; 8: aload_0 9: getstatic #27; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$; 12: getstatic #32; //Field scala/Predef$.MODULE$:Lscala/Predef$; 15: iconst_3 16: newarray int 18: dup 19: iconst_0 20: iconst_1 21: iastore 22: dup 23: iconst_1 24: iconst_2 25: iastore 26: dup 27: iconst_2 28: iconst_3 29: iastore 30: invokevirtual #38; //Method scala/LowPriorityImplicits.wrapIntArray:([I)Lscala/collection/mutable/WrappedArray; 33: invokevirtual #42; //Method scala/collection/immutable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List; 36: new #44; //class X$$anonfun$1 

Or in a more readable Java code:

 @SuppressWarnings({"unchecked", "rawtypes"}) final List<Integer> xs = (List) List$.MODULE$.apply(Predef.wrapIntArray(new int[] {1,2,3})); 
+4
source

All Articles