Skip to content

Commit 6e859c9

Browse files
committed
Lazy For() Comprehension
1 parent 189a2b3 commit 6e859c9

File tree

3 files changed

+2919
-0
lines changed

3 files changed

+2919
-0
lines changed

vavr/generator/Generator.scala

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,133 @@ def generateMainClasses(): Unit = {
743743
"""
744744
}
745745

746+
def genLazyFor(im: ImportManager, packageName: String, className: String): String = {
747+
xs"""
748+
${
749+
monadicTypesFor
750+
.filterNot(_ == "Iterable")
751+
.gen(mtype => (2 to N).gen(i => {
752+
val forClassName = s"ForLazy$i$mtype"
753+
val isComplex = monadicTypesThatNeedParameter.contains(mtype)
754+
val parameterInset = if (isComplex) "L, " else ""
755+
val generics = parameterInset + (1 to i).gen(j => s"T$j")(", ")
756+
757+
val params = (1 to i).gen { j =>
758+
if (j == 1)
759+
s"$mtype<${parameterInset}T1> ts1"
760+
else {
761+
val inputTypes = (1 until j).gen(k => s"T$k")(", ")
762+
s"Function${j - 1}<$inputTypes, $mtype<${parameterInset}T$j>> ts$j"
763+
}
764+
}(", ")
765+
766+
val ctorArgs = (1 to i).gen(j => s"ts$j")(", ")
767+
768+
xs"""
769+
/$javadoc
770+
* Creates a {@code For}-comprehension of ${i.numerus(mtype)}.
771+
${(0 to i).gen(j => if (j == 0) "*" else s"* @param ts$j the ${j.ordinal} $mtype")("\n")}
772+
${if (isComplex) s"* @param <L> left-hand type of all ${mtype}s\n" else ""}
773+
${(1 to i).gen(j => s"* @param <T$j> component type of the ${j.ordinal} $mtype")("\n")}
774+
* @return a new {@code For}-comprehension of arity $i
775+
*/
776+
public static <$generics> $forClassName<$generics> For($params) {
777+
${(1 to i).gen(j => xs"""$Objects.requireNonNull(ts$j, "ts$j is null");""")("\n")}
778+
return new $forClassName<>($ctorArgs);
779+
}
780+
"""
781+
})("\n\n"))("\n\n")
782+
}
783+
784+
${
785+
monadicTypesFor
786+
.filterNot(_ == "Iterable")
787+
.gen(mtype => (2 to N).gen(i => {
788+
val rtype = mtype
789+
val forClassName = s"ForLazy$i$mtype"
790+
val parameterInset = if (monadicTypesThatNeedParameter.contains(mtype)) "L, " else ""
791+
val generics = parameterInset + (1 to i).gen(j => s"T$j")(", ")
792+
val functionType = i match {
793+
case 2 => BiFunctionType
794+
case _ => s"Function$i"
795+
}
796+
val args = (1 to i).gen(j => s"? super T$j")(", ")
797+
798+
val fields = (1 to i).gen { j =>
799+
if (j == 1)
800+
s"private final $mtype<${parameterInset}T1> ts1;"
801+
else {
802+
val inputTypes = (1 until j).gen(k => s"T$k")(", ")
803+
s"private final Function${j - 1}<$inputTypes, $mtype<${parameterInset}T$j>> ts$j;"
804+
}
805+
}("\n")
806+
807+
val ctorParams = (1 to i).gen { j =>
808+
if (j == 1)
809+
s"$mtype<${parameterInset}T1> ts1"
810+
else {
811+
val inputTypes = (1 until j).gen(k => s"T$k")(", ")
812+
s"Function${j - 1}<$inputTypes, $mtype<${parameterInset}T$j>> ts$j"
813+
}
814+
}(", ")
815+
816+
val assignments = (1 to i).gen(j => s"this.ts$j = ts$j;")("\n")
817+
818+
val yieldBody = {
819+
def nestedLambda(j: Int): String = {
820+
val base = " " * 3
821+
val indent = " " * j
822+
if (j == i) {
823+
val argsList = (1 to i).map(k => s"t$k").mkString(", ")
824+
val inputArgs = (1 until i).map(k => s"t$k").mkString(", ")
825+
s"ts$i.apply($inputArgs).map(t$i -> f.apply($argsList))"
826+
} else if (j == 1) {
827+
s"ts1.flatMap(t1 -> {\n" +
828+
s"${base}${indent} return ${nestedLambda(j + 1)};\n" +
829+
s"${base}${indent}})"
830+
} else {
831+
val inputArgs = (1 until j).map(k => s"t$k").mkString(", ")
832+
s"ts$j.apply($inputArgs).flatMap(t$j -> {\n" +
833+
s"${base}${indent} return ${nestedLambda(j + 1)};\n" +
834+
s"${base}${indent}})"
835+
}
836+
}
837+
838+
nestedLambda(1)
839+
}
840+
841+
xs"""
842+
/$javadoc
843+
* For-comprehension with ${i.numerus(mtype)}.
844+
*/
845+
public static class $forClassName<$generics> {
846+
847+
$fields
848+
849+
private $forClassName($ctorParams) {
850+
$assignments
851+
}
852+
853+
/$javadoc
854+
* Yields a result for elements of the cross-product of the underlying ${i.plural(mtype)}.
855+
*
856+
* @param f a function that maps an element of the cross-product to a result
857+
* @param <R> type of the resulting {@code $rtype} elements
858+
* @return an {@code $rtype} of mapped results
859+
*/
860+
public <R> $rtype<${parameterInset}R> yield($functionType<$args, ? extends R> f) {
861+
$Objects.requireNonNull(f, "f is null");
862+
863+
return $yieldBody;
864+
}
865+
}
866+
"""
867+
})("\n\n"))("\n\n")
868+
}
869+
"""
870+
}
871+
872+
746873
def genFor(im: ImportManager, packageName: String, className: String): String = {
747874
xs"""
748875
//
@@ -1440,6 +1567,8 @@ def generateMainClasses(): Unit = {
14401567

14411568
${genFor(im, packageName, className)}
14421569

1570+
${genLazyFor(im, packageName, className)}
1571+
14431572
${genMatch(im, packageName, className)}
14441573
}
14451574
"""

0 commit comments

Comments
 (0)