diff --git a/README.md b/README.md index fa565f5..79c2901 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ using Java 8 lambda syntax. ```java import scala.concurrent.*; -import static scala.runtime.F.func; +import static scala.runtime.jfunc.JFunc.*; class Test { private static Future futureExample(Future future, ExecutionContext ec) { @@ -17,7 +17,7 @@ class Test { } ``` -[More Examples / Documentation](https://github.com/retronym/java-8-function1/blob/master/src/test/java/scala/runtime/test/Test.java) +[More Examples / Documentation](https://github.com/retronym/java-8-function1/blob/master/src/test/java/scala/runtime/jfunc/Test.java) ### Hacking @@ -38,7 +38,7 @@ class Test { package scala.runtime; @FunctionalInterface -public interface F1$mcII$sp extends F1 { +public interface JFunction1$mcII$sp extends JFunction1 { abstract int apply$mcII$sp(int v1); default Object apply(Object s) { return (Integer) apply$mcII$sp((Integer) s); } diff --git a/build.sbt b/build.sbt index 634ec3e..d0304c2 100644 --- a/build.sbt +++ b/build.sbt @@ -2,16 +2,16 @@ scalaVersion := "2.11.0-RC1" sourceGenerators in Compile <+= sourceManaged in Compile map { dir => def write(name: String, content: String) = { - val f = dir / "scala" / "runtime" / s"${name}.java" + val f = dir / "java" / "scala" / "runtime" / "jfunc" / s"${name}.java" IO.write(f, content) f } - Seq(write("F", CodeGen.factory)) ++ (0 to 22).map(n => write("F" + n, CodeGen.fN(n))) ++ (1 to 22).map(n => write("P" + n, CodeGen.pN(n))) + Seq(write("JFunc", CodeGen.factory)) ++ (0 to 22).map(n => write("JFunction" + n, CodeGen.fN(n))) ++ (1 to 22).map(n => write("JProcedure" + n, CodeGen.pN(n))) } sourceGenerators in Test <+= sourceManaged in Test map { dir => def write(name: String, content: String) = { - val f = dir / "scala" / "runtime" / "test" / s"${name}.java" + val f = dir / "java" / "scala" / "runtime" / "jfunc" / s"${name}.java" IO.write(f, content) f } diff --git a/project/CodeGen.scala b/project/CodeGen.scala index acb2efd..f449e6c 100644 --- a/project/CodeGen.scala +++ b/project/CodeGen.scala @@ -17,84 +17,145 @@ object Type { } object CodeGen { - private val initName = "$init$" - private val function1ImplClass = "scala.Function1$class" - private val copyright = """ -/* - * Copyright (C) 2012-2014 Typesafe Inc. - */""".trim - - private def f0Header = s""" -$copyright - -package scala.runtime; - -@FunctionalInterface -public interface F0 extends scala.Function0 { - default void $initName() { - }; -""" - private def f1Header = s""" -$copyright - -package scala.runtime; - -@FunctionalInterface -public interface F1 extends scala.Function1 { - default void $initName() { - }; - - @Override - default scala.Function1 andThen(scala.Function1 g) { - return $function1ImplClass.andThen(this, g); + def packaging = "package scala.runtime.jfunc;" + case class arity(n: Int) { + val ns = (1 to n).toList + + def csv(f: Int => String): String = ns.map(f).mkString(", ") + + val tparams = csv("T" + _) + + private def f0Header = + s""" + |$copyright + | + |$packaging + | + |@FunctionalInterface + |public interface JFunction0 extends scala.Function0 { + | default void $initName() { + | }; + |""".stripMargin + private def f1Header = + s""" + |$copyright + | + |$packaging + | + |@FunctionalInterface + |public interface JFunction1 extends scala.Function1 { + | default void $initName() { + | }; + | + | @Override + | default scala.Function1 andThen(scala.Function1 g) { + | return $function1ImplClass.andThen(this, g); + | } + | + | @Override + | default scala.Function1 compose(scala.Function1 g) { + | return $function1ImplClass.compose(this, g); + | } + |""".stripMargin + + private def fNHeader = { + + val curriedReturn = (1 to n).reverse.foldLeft("R")((x, y) => s"scala.Function1") + val tupledReturn = s"scala.Function1, R>" + val implClass = s"scala.Function$n" + "$class" + s""" + |$copyright + | + |$packaging + | + |@FunctionalInterface + |public interface JFunction$n<$tparams, R> extends scala.Function$n<$tparams, R> { + | default void $initName() { + | }; + | + | default $curriedReturn curried() { + | return $implClass.curried(this); + | } + | + | default $tupledReturn tupled() { + | return $implClass.tupled(this); + | } + | + |""".stripMargin } - @Override - default scala.Function1 compose(scala.Function1 g) { - return $function1ImplClass.compose(this, g); + def fHeader: String = + n match { + case 0 => f0Header + case 1 => f1Header + case _ => fNHeader + } + + def pN: String = { + val vparams = csv(n => s"T$n t$n") + val vparamRefs = csv(n => s"t$n") + val parent = "JFunction" + n + s""" + |$copyright + | + |$packaging + | + |import scala.runtime.BoxedUnit; + | + |@FunctionalInterface + |public interface JProcedure${n}<${tparams}> extends ${parent}<$tparams, BoxedUnit> { + | default void $initName() { + | } + | + | void applyVoid($vparams); + | + | default BoxedUnit apply($vparams) { + | applyVoid($vparamRefs); + | return BoxedUnit.UNIT; + | } + |} + |""".stripMargin } -""" - - private def f2Header = fNHeader(2) - - def fNHeader(n: Int) = { - require(n > 1, n) - val tparams = (1 to n).map("T" + _).mkString(", ") - val curriedReturn = (1 to n).reverse.foldLeft("R")((x, y) => s"scala.Function1") - val tupledReturn = s"scala.Function1, R>" - val implClass = s"scala.Function$n" + "$class" -s""" -$copyright - -package scala.runtime; - -@FunctionalInterface -public interface F$n<$tparams, R> extends scala.Function$n<$tparams, R> { - default void $initName() { - }; - - default $curriedReturn curried() { - return $implClass.curried(this); + def factory: String = { + s""" + |public static <$tparams, R> scala.Function$n<$tparams, R> func(JFunction$n<$tparams, R> f) { return f; } + |public static <$tparams> scala.Function$n<$tparams, BoxedUnit> proc(JProcedure$n<$tparams> p) { return p; } + |""".stripMargin.trim } - default $tupledReturn tupled() { - return $implClass.tupled(this); + def accept: String = { + val targs = csv(_ => "String") + val vargs = csv("\"" + _ + "\"") + s""" + |static T acceptFunction$n(scala.Function$n<$targs, T> f) { + | return f.apply($vargs); + |} + |static void acceptFunction${n}Unit(scala.Function$n<$targs, scala.runtime.BoxedUnit> f) { + | f.apply($vargs); + |} + |""".stripMargin } + } -""" -} + + private val initName = "$init$" + private val function1ImplClass = "scala.Function1$class" + private val copyright = + """ + |/* + | * Copyright (C) 2012-2014 Typesafe Inc. + | */""".stripMargin.trim private def apply0MethodSpec(r: Type): String = { val name = "apply$mc" + s"${r.code}" + "$sp" val applyCall = s"apply();" def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall" - -s""" -default ${r.prim} $name() { - $body -} -""".trim + s""" + |default ${r.prim} $name() { + | $body + |} + |""".stripMargin.trim } private def apply0SpecMethods = { @@ -108,11 +169,11 @@ default ${r.prim} $name() { val applyCall = s"apply((T1) ((${t1.ref}) v1));" def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall" -s""" -default ${r.prim} $name(${t1.prim} v1) { - $body -} -""".trim + s""" + |default ${r.prim} $name(${t1.prim} v1) { + | $body + |} + |""".stripMargin.trim } private def apply1SpecMethods = { @@ -127,11 +188,11 @@ default ${r.prim} $name(${t1.prim} v1) { val applyCall = s"apply((T1) ((${t1.ref}) v1), (T2) ((${t2.ref}) v2));" def body = if (r == Type.Void) applyCall else s"return (${r.ref}) $applyCall" -s""" -default ${r.prim} $name(${t1.prim} v1, ${t2.prim} v2) { - $body -} -""".trim + s""" + |default ${r.prim} $name(${t1.prim} v1, ${t2.prim} v2) { + | $body + |} + |""".stripMargin.trim } private def apply2SpecMethods = { @@ -141,98 +202,49 @@ default ${r.prim} $name(${t1.prim} v1, ${t2.prim} v2) { methods.map(indent).mkString("\n\n") } - def f0 = f0Header + apply0SpecMethods + "}\n" - - def f1 = f1Header + apply1SpecMethods + "}\n" - - def f2 = f2Header + apply2SpecMethods + "}\n" - - def fN(arity: Int) = arity match { - case 0 => f0 - case 1 => f1 - case 2 => f2 - case x => fNHeader(arity) + "\n}\n" - } - - def pN(arity: Int) = { - def csv(f: Int => String): String = - (1 to arity).map(f).mkString(", ") - val tparams = (1 to arity).map("T" + _).mkString(", ") - val vparams = (1 to arity).map(n => s"T$n t$n").mkString(", ") - val vparamRefs = (1 to arity).map(n => s"t$n").mkString(", ") - val parent = "F" + arity -s""" -$copyright - -package scala.runtime; - -import scala.runtime.BoxedUnit; - -@FunctionalInterface -public interface P${arity}<${tparams}> extends ${parent}<$tparams, BoxedUnit> { - default void $initName() { - } - - void applyVoid($vparams); - default BoxedUnit apply($vparams) { - applyVoid($vparamRefs); - return BoxedUnit.UNIT; - } -} -""" -} - - def factory = { - def factory0(n: Int) = { - val tparams = (1 to n).map("T" + _).mkString(", ") -s""" -public static <$tparams, R> scala.Function$n<$tparams, R> func(F$n<$tparams, R> f) { return f; } -public static <$tparams> scala.Function$n<$tparams, BoxedUnit> proc(P$n<$tparams> p) { return p; } -""".trim + def fN(n: Int) = { + val header = arity(n).fHeader + val applyMethods = n match { + case 0 => apply0SpecMethods + case 1 => apply1SpecMethods + case 2 => apply2SpecMethods + case x => "" } - val ms = (1 to 22).map(factory0).mkString("\n") - -s""" -$copyright - -package scala.runtime; - -import scala.runtime.BoxedUnit; - -public final class F { - private F() {} - public static scala.Function0 f0(F0 f) { return f; } -${indent(ms)} -} - -""" + val trailer = "}\n" + List(header, applyMethods, trailer).mkString } - def accept(n: Int): String = { - val targs = (1 to n).map(_ => "String").mkString(", ") - val vargs = (1 to n).map("\"" + _ + "\"").mkString(", ") -s""" -static T acceptFunction$n(scala.Function$n<$targs, T> f) { - return f.apply($vargs); -} -static void acceptFunction${n}Unit(scala.Function$n<$targs, scala.runtime.BoxedUnit> f) { - f.apply($vargs); -} -""" + def pN(n: Int) = arity(n).pN + + def factory: String = { + val ms = (1 to 22).map(n => arity(n).factory).mkString("\n") + s""" + |$copyright + | + |$packaging + | + |import scala.runtime.BoxedUnit; + | + |public final class JFunc { + | private JFunc() {} + | public static scala.Function0 func(JFunction0 f) { return f; } + |${indent(ms)} + |} + | + |""".stripMargin } - def testApi = { -s""" -$copyright - -package scala.runtime.test; - -final class TestAPI { -${(1 to 22).map(accept).map(indent).mkString("\n\n")} -} -""" + def testApi: String = { + s""" + |$copyright + | + |$packaging + | + |final class TestAPI { + |${(1 to 22).map(n => arity(n).accept).map(indent).mkString("\n\n")} + |} + |""".stripMargin } def indent(s: String) = s.linesIterator.map(" " + _).mkString("\n") } - diff --git a/src/test/java/scala/runtime/test/Test.java b/src/test/java/scala/runtime/jfunc/Test.java similarity index 77% rename from src/test/java/scala/runtime/test/Test.java rename to src/test/java/scala/runtime/jfunc/Test.java index 162a610..c0a2ca2 100644 --- a/src/test/java/scala/runtime/test/Test.java +++ b/src/test/java/scala/runtime/jfunc/Test.java @@ -1,12 +1,11 @@ /* * Copyright (C) 2012-2014 Typesafe Inc. */ -package scala.runtime.test; +package scala.runtime.jfunc; import scala.runtime.*; -import static scala.runtime.test.TestAPI.*; -import static scala.runtime.F.func; -import static scala.runtime.F.proc; +import static scala.runtime.jfunc.TestAPI.*; +import static scala.runtime.jfunc.JFunc.*; public class Test { public static void main(String[] args) { @@ -22,11 +21,11 @@ public static void main(String[] args) { // That's a pity, but we can get pretty close with this library! - // We have to tell javac to use `F1` as the functional interface. - F1 f1 = (String s) -> s; + // We have to tell javac to use `JFunction1` as the functional interface. + JFunction1 f1 = (String s) -> s; // That's more or less equivalent to the old, anonymous class syntax: - new F1() { + new JFunction1() { public String apply(String s) { return s; } }; @@ -41,9 +40,9 @@ public static void main(String[] args) { // F1 is a subclass of Function1: scala.Function1 f2 = f1; - // Factory methods in `F` can reduce the verbosity a little: + // Factory methods in `JFunc` can reduce the verbosity a little: // `func` is actually just an identity method; it only exists to - // trigger lambda creation using the `F1` functional interface. + // trigger lambda creation using the `JFunction1` functional interface. scala.Function1 f3 = func((String s) -> s); // Note that javac's type inference can infer the parameter type here, @@ -54,19 +53,19 @@ public static void main(String[] args) { // Specialized variants of the `apply` method are implenented in the // functional interface - F1 f5 = (i) -> -i; + JFunction1 f5 = (i) -> -i; assert(f5.apply(1) == -1); assert(f5.apply$mcII$sp(1) == -1); // as are `curried`, `tupled`, `compose`, `andThen`. f3.compose(f3).andThen(f3).apply(""); scala.Function2 f6 = func((s1, s2) -> join(s1, s2)); - assert(f6.curried().apply("1").apply("2") == "12"); + assert(f6.curried().apply("1").apply("2").equals("12")); - // Functions returning unit must use the `P1`, ... functional interfaces + // Functions returning unit must use the `JProcedure1`, ... functional interfaces // in order to convert a void lamdba return to Scala's Unit. // - // The easiest way to do this is via `F.proc`, .... + // The easiest way to do this is via `JFunc.proc`, .... // // Note that the lambda has a return type of `void` if the last // statement is a call to a `void` returning method, or if it is @@ -75,21 +74,21 @@ public static void main(String[] args) { scala.Function1 f8 = proc(s -> {s.toUpperCase(); return;}); // Function0 is also available - scala.Function0 f9 = F.f0(() -> "42"); - assert(f9.apply() == "42"); + scala.Function0 f9 = func(() -> "42"); + assert(f9.apply().equals("42")); // You can go up to 22 (the highest arity function defined in the Scala standard library.) - assert(acceptFunction1(func(v1 -> v1.toUpperCase())) == "1"); + assert(acceptFunction1(func(v1 -> v1.toUpperCase())).equals("1")); acceptFunction1Unit(proc(v1 -> sideEffect())); acceptFunction1Unit(proc(v1 -> {v1.toUpperCase(); return;})); - assert(acceptFunction2(func((v1, v2) -> join(v1, v2))) == "12"); + assert(acceptFunction2(func((v1, v2) -> join(v1, v2))).equals("12")); acceptFunction2Unit(proc((v1, v2) -> {v1.toUpperCase(); return;})); - assert(acceptFunction3(func((v1, v2, v3) -> join(v1, v2, v3))) == "123"); + assert(acceptFunction3(func((v1, v2, v3) -> join(v1, v2, v3))).equals("123")); acceptFunction3Unit(proc((v1, v2, v3) -> {v1.toUpperCase(); return;})); - assert(acceptFunction22(func((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -> join(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22))) == "12345678910111213141516171819202122"); + assert(acceptFunction22(func((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -> join(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22))).equals("12345678910111213141516171819202122")); acceptFunction22Unit( proc((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -> {v1.toUpperCase(); return;})); }