summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/reflect
diff options
context:
space:
mode:
authorDenys Shabalin <denys.shabalin@typesafe.com>2014-02-19 17:20:35 +0100
committerDenys Shabalin <denys.shabalin@typesafe.com>2014-02-20 13:17:12 +0100
commitd49c09e3f67d780d2757085e02b0a28d333527c4 (patch)
treec4263bd1a011e8d2c955efb550d91446b6c7bffc /src/compiler/scala/tools/reflect
parent3973f29cec9f06724941b68577908f546341c45e (diff)
downloadscala-d49c09e3f67d780d2757085e02b0a28d333527c4.tar.gz
scala-d49c09e3f67d780d2757085e02b0a28d333527c4.tar.bz2
scala-d49c09e3f67d780d2757085e02b0a28d333527c4.zip
Fix quasiquote terminology to be consistent with Scheme
1. Rename cardinality into rank. Shorter word, easier to understand, more appropriate in our context. 2. Previously we called any dollar substitution splicing but this is not consistent with Scheme where splicing is substitution with non-zero rank. So now $foo is unquoting and ..$foo and ...$foo is unquote splicing or just splicing. Correspondingly splicee becomes unquotee. 3. Rename si7980 test into t7980
Diffstat (limited to 'src/compiler/scala/tools/reflect')
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala114
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala10
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala34
4 files changed, 80 insertions, 80 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
index c2f1bf430d..8376fca4ad 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -5,17 +5,17 @@ import scala.collection.{immutable, mutable}
import scala.reflect.internal.Flags._
import scala.reflect.macros.TypecheckException
-class Cardinality private[Cardinality](val value: Int) extends AnyVal {
- def pred = { assert(value - 1 >= 0); new Cardinality(value - 1) }
- def succ = new Cardinality(value + 1)
+class Rank private[Rank](val value: Int) extends AnyVal {
+ def pred = { assert(value - 1 >= 0); new Rank(value - 1) }
+ def succ = new Rank(value + 1)
override def toString = if (value == 0) "no dots" else "." * (value + 1)
}
-object Cardinality {
- val NoDot = new Cardinality(0)
- val DotDot = new Cardinality(1)
- val DotDotDot = new Cardinality(2)
- object Dot { def unapply(card: Cardinality) = card != NoDot }
+object Rank {
+ val NoDot = new Rank(0)
+ val DotDot = new Rank(1)
+ val DotDotDot = new Rank(2)
+ object Dot { def unapply(rank: Rank) = rank != NoDot }
def parseDots(part: String) = {
if (part.endsWith("...")) (part.stripSuffix("..."), DotDotDot)
else if (part.endsWith("..")) (part.stripSuffix(".."), DotDot)
@@ -27,7 +27,7 @@ object Cardinality {
*/
trait Holes { self: Quasiquotes =>
import global._
- import Cardinality._
+ import Rank._
import definitions._
import universeTypes._
@@ -43,43 +43,43 @@ trait Holes { self: Quasiquotes =>
tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe
private def extractIterableTParam(tpe: Type) =
IterableTParam.asSeenFrom(tpe, IterableClass)
- private def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) =
+ private def stripIterable(tpe: Type, limit: Option[Rank] = None): (Rank, Type) =
if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe)
else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe)
else if (isBottomType(tpe)) (NoDot, tpe)
else {
val targ = extractIterableTParam(tpe)
- val (card, innerTpe) = stripIterable(targ, limit.map { _.pred })
- (card.succ, innerTpe)
+ val (rank, innerTpe) = stripIterable(targ, limit.map { _.pred })
+ (rank.succ, innerTpe)
}
- private def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = {
+ private def iterableTypeFromRank(n: Rank, tpe: Type): Type = {
if (n == NoDot) tpe
- else appliedType(IterableClass.toType, List(iterableTypeFromCard(n.pred, tpe)))
+ else appliedType(IterableClass.toType, List(iterableTypeFromRank(n.pred, tpe)))
}
- /** Hole encapsulates information about splices in quasiquotes.
- * It packs together a cardinality of a splice, pre-reified tree
- * representation (possibly preprocessed) and position.
+ /** Hole encapsulates information about unquotees in quasiquotes.
+ * It packs together a rank, pre-reified tree representation
+ * (possibly preprocessed) and position.
*/
abstract class Hole {
val tree: Tree
val pos: Position
- val cardinality: Cardinality
+ val rank: Rank
}
object Hole {
- def apply(card: Cardinality, tree: Tree): Hole =
- if (method != nme.unapply) new ApplyHole(card, tree)
- else new UnapplyHole(card, tree)
- def unapply(hole: Hole): Some[(Tree, Cardinality)] = Some((hole.tree, hole.cardinality))
+ def apply(rank: Rank, tree: Tree): Hole =
+ if (method != nme.unapply) new ApplyHole(rank, tree)
+ else new UnapplyHole(rank, tree)
+ def unapply(hole: Hole): Some[(Tree, Rank)] = Some((hole.tree, hole.rank))
}
- class ApplyHole(card: Cardinality, splicee: Tree) extends Hole {
+ class ApplyHole(annotatedRank: Rank, unquotee: Tree) extends Hole {
val (strippedTpe, tpe): (Type, Type) = {
- val (strippedCard, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card))
+ val (strippedRank, strippedTpe) = stripIterable(unquotee.tpe, limit = Some(annotatedRank))
if (isBottomType(strippedTpe)) cantSplice()
- else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, strippedTpe))
- else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, treeType))
+ else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromRank(annotatedRank, strippedTpe))
+ else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromRank(annotatedRank, treeType))
else cantSplice()
}
@@ -88,28 +88,28 @@ trait Holes { self: Quasiquotes =>
if (isNativeType(itpe)) tree
else if (isLiftableType(itpe)) lifted(itpe)(tree)
else global.abort("unreachable")
- if (card == NoDot) inner(strippedTpe)(splicee)
- else iterated(card, splicee, splicee.tpe)
+ if (annotatedRank == NoDot) inner(strippedTpe)(unquotee)
+ else iterated(annotatedRank, unquotee, unquotee.tpe)
}
- val pos = splicee.pos
+ val pos = unquotee.pos
- val cardinality = stripIterable(tpe)._1
+ val rank = stripIterable(tpe)._1
private def cantSplice(): Nothing = {
- val (iterableCard, iterableType) = stripIterable(splicee.tpe)
- val holeCardMsg = if (card != NoDot) s" with $card" else ""
- val action = "splice " + splicee.tpe + holeCardMsg
- val suggestCard = card != iterableCard || card != NoDot
- val spliceeCardMsg = if (card != iterableCard && iterableCard != NoDot) s"using $iterableCard" else "omitting the dots"
- val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
- val suggestLifting = (card == NoDot || iterableCard != NoDot) && !(iterableType <:< treeType) && !isLiftableType(iterableType)
- val liftedTpe = if (card != NoDot) iterableType else splicee.tpe
+ val (iterableRank, iterableType) = stripIterable(unquotee.tpe)
+ val holeRankMsg = if (annotatedRank != NoDot) s" with $annotatedRank" else ""
+ val action = "unquote " + unquotee.tpe + holeRankMsg
+ val suggestRank = annotatedRank != iterableRank || annotatedRank != NoDot
+ val unquoteeRankMsg = if (annotatedRank != iterableRank && iterableRank != NoDot) s"using $iterableRank" else "omitting the dots"
+ val rankSuggestion = if (suggestRank) unquoteeRankMsg else ""
+ val suggestLifting = (annotatedRank == NoDot || iterableRank != NoDot) && !(iterableType <:< treeType) && !isLiftableType(iterableType)
+ val liftedTpe = if (annotatedRank != NoDot) iterableType else unquotee.tpe
val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
val advice =
if (isBottomType(iterableType)) "bottom type values often indicate programmer mistake"
- else "consider " + List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
- c.abort(splicee.pos, s"Can't $action, $advice")
+ else "consider " + List(rankSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
+ c.abort(unquotee.pos, s"Can't $action, $advice")
}
private def lifted(tpe: Type)(tree: Tree): Tree = {
@@ -149,7 +149,7 @@ trait Holes { self: Quasiquotes =>
else None
}
- /** Map high-cardinality splice onto an expression that eveluates as a list of given cardinality.
+ /** Map high-rank unquotee onto an expression that eveluates as a list of given rank.
*
* All possible combinations of representations are given in the table below:
*
@@ -166,7 +166,7 @@ trait Holes { self: Quasiquotes =>
* x is not just an Iterable[T] but a List[T]. Similarly no mapping is performed if mapping function is
* known to be an identity.
*/
- private def iterated(card: Cardinality, tree: Tree, tpe: Type): Tree = (card, tpe) match {
+ private def iterated(rank: Rank, tree: Tree, tpe: Type): Tree = (rank, tpe) match {
case (DotDot, tpe @ IterableType(LiftedType(lift))) => mapF(toList(tree, tpe), lift)
case (DotDot, LiftedType(lift)) => toStats(lift(tree))
case (DotDotDot, tpe @ IterableType(inner)) => mapF(toList(tree, tpe), t => iterated(DotDot, t, inner))
@@ -175,7 +175,7 @@ trait Holes { self: Quasiquotes =>
}
}
- class UnapplyHole(val cardinality: Cardinality, pat: Tree) extends Hole {
+ class UnapplyHole(val rank: Rank, pat: Tree) extends Hole {
val (placeholderName, pos, tptopt) = pat match {
case Bind(pname, inner @ Bind(_, Typed(Ident(nme.WILDCARD), tpt))) => (pname, inner.pos, Some(tpt))
case Bind(pname, inner @ Typed(Ident(nme.WILDCARD), tpt)) => (pname, inner.pos, Some(tpt))
@@ -188,13 +188,13 @@ trait Holes { self: Quasiquotes =>
try c.typeCheck(TypeDef(NoMods, TypeName("T"), Nil, tpt))
catch { case TypecheckException(pos, msg) => c.abort(pos.asInstanceOf[c.Position], msg) }
val tpe = typedTpt.tpe
- val (iterableCard, _) = stripIterable(tpe)
- if (iterableCard.value < cardinality.value)
- c.abort(pat.pos, s"Can't extract $tpe with $cardinality, consider using $iterableCard")
- val (_, strippedTpe) = stripIterable(tpe, limit = Some(cardinality))
+ val (iterableRank, _) = stripIterable(tpe)
+ if (iterableRank.value < rank.value)
+ c.abort(pat.pos, s"Can't extract $tpe with $rank, consider using $iterableRank")
+ val (_, strippedTpe) = stripIterable(tpe, limit = Some(rank))
if (strippedTpe <:< treeType) treeNoUnlift
else
- unlifters.spawn(strippedTpe, cardinality).map {
+ unlifters.spawn(strippedTpe, rank).map {
Apply(_, treeNoUnlift :: Nil)
}.getOrElse {
c.abort(pat.pos, s"Can't find $unliftableType[$strippedTpe], consider providing it")
@@ -203,7 +203,7 @@ trait Holes { self: Quasiquotes =>
}
/** Full support for unliftable implies that it's possible to interleave
- * deconstruction with higher cardinality and unlifting of the values.
+ * deconstruction with higher rank and unlifting of the values.
* In particular extraction of List[Tree] as List[T: Unliftable] requires
* helper extractors that would do the job: UnliftListElementwise[T]. Similarly
* List[List[Tree]] needs UnliftListOfListsElementwise[T].
@@ -211,24 +211,24 @@ trait Holes { self: Quasiquotes =>
* See also "unlift list" tests in UnapplyProps.scala
*/
object unlifters {
- private var records = List.empty[(Type, Cardinality)]
+ private var records = List.empty[(Type, Rank)]
// Materialize unlift helper that does elementwise
- // unlifting for corresponding cardinality and type.
- def spawn(tpe: Type, card: Cardinality): Option[Tree] = {
+ // unlifting for corresponding rank and type.
+ def spawn(tpe: Type, rank: Rank): Option[Tree] = {
val unlifter = inferUnliftable(tpe)
if (unlifter == EmptyTree) None
- else if (card == NoDot) Some(unlifter)
+ else if (rank == NoDot) Some(unlifter)
else {
- val idx = records.indexWhere { p => p._1 =:= tpe && p._2 == card }
- val resIdx = if (idx != -1) idx else { records +:= (tpe, card); records.length - 1}
+ val idx = records.indexWhere { p => p._1 =:= tpe && p._2 == rank }
+ val resIdx = if (idx != -1) idx else { records +:= (tpe, rank); records.length - 1}
Some(Ident(TermName(nme.QUASIQUOTE_UNLIFT_HELPER + resIdx)))
}
}
// Returns a list of vals that will defined required unlifters
def preamble(): List[Tree] =
- records.zipWithIndex.map { case ((tpe, card), idx) =>
+ records.zipWithIndex.map { case ((tpe, rank), idx) =>
val name = TermName(nme.QUASIQUOTE_UNLIFT_HELPER + idx)
- val helperName = card match {
+ val helperName = rank match {
case DotDot => nme.UnliftListElementwise
case DotDotDot => nme.UnliftListOfListsElementwise
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 301e7051df..3b93a8933d 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -123,7 +123,7 @@ trait Parsers { self: Quasiquotes =>
override def isStatSep(token: Int) = token == EOF || super.isStatSep(token)
override def expectedMsg(token: Int): String =
- if (isHole) expectedMsgTemplate(token2string(token), "splicee")
+ if (isHole) expectedMsgTemplate(token2string(token), "unquotee")
else super.expectedMsg(token)
// $mods def foo
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index e7730f878f..5986758c2b 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -12,7 +12,7 @@ import scala.collection.{immutable, mutable}
*/
trait Placeholders { self: Quasiquotes =>
import global._
- import Cardinality._
+ import Rank._
import universeTypes._
// Step 1: Transform Scala source with holes into vanilla Scala source
@@ -29,13 +29,13 @@ trait Placeholders { self: Quasiquotes =>
posMap += pos -> ((start, end))
}
- def appendHole(tree: Tree, cardinality: Cardinality) = {
+ def appendHole(tree: Tree, rank: Rank) = {
val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
sb.append(placeholderName)
val holeTree =
if (method != nme.unapply) tree
else Bind(placeholderName, tree)
- holeMap(placeholderName) = Hole(cardinality, holeTree)
+ holeMap(placeholderName) = Hole(rank, holeTree)
}
val iargs = method match {
@@ -47,9 +47,9 @@ trait Placeholders { self: Quasiquotes =>
}
foreach2(iargs, parts.init) { case (tree, (p, pos)) =>
- val (part, cardinality) = parseDots(p)
+ val (part, rank) = parseDots(p)
appendPart(part, pos)
- appendHole(tree, cardinality)
+ appendHole(tree, rank)
}
val (p, pos) = parts.last
appendPart(p, pos)
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 339937adc3..61fb22bc73 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -10,7 +10,7 @@ trait Reifiers { self: Quasiquotes =>
import global.build._
import global.treeInfo._
import global.definitions._
- import Cardinality._
+ import Rank._
import universeTypes._
abstract class Reifier(val isReifyingExpressions: Boolean) extends {
@@ -23,7 +23,7 @@ trait Reifiers { self: Quasiquotes =>
lazy val typer = throw new UnsupportedOperationException
def isReifyingPatterns: Boolean = !isReifyingExpressions
- def action = if (isReifyingExpressions) "splice" else "extract"
+ def action = if (isReifyingExpressions) "unquote" else "extract"
def holesHaveTypes = isReifyingExpressions
/** Map that stores freshly generated names linked to the corresponding names in the reified tree.
@@ -129,7 +129,7 @@ trait Reifiers { self: Quasiquotes =>
def reifyTreePlaceholder(tree: Tree): Tree = tree match {
case Placeholder(hole: ApplyHole) if hole.tpe <:< treeType => hole.tree
case Placeholder(Hole(tree, NoDot)) if isReifyingPatterns => tree
- case Placeholder(hole @ Hole(_, card @ Dot())) => c.abort(hole.pos, s"Can't $action with $card here")
+ case Placeholder(hole @ Hole(_, rank @ Dot())) => c.abort(hole.pos, s"Can't $action with $rank here")
case TuplePlaceholder(args) => reifyTuple(args)
case TupleTypePlaceholder(args) => reifyTupleType(args)
case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe)
@@ -236,7 +236,7 @@ trait Reifiers { self: Quasiquotes =>
case List(hole @ Placeholder(Hole(_, NoDot))) => reify(hole)
case List(Placeholder(_)) => reifyBuildCall(nme.SyntacticTuple, args)
// in a case we only have one element tuple without
- // any cardinality annotations this means that this is
+ // any rank annotations this means that this is
// just an expression wrapped in parentheses
case List(other) => reify(other)
case _ => reifyBuildCall(nme.SyntacticTuple, args)
@@ -295,8 +295,8 @@ trait Reifiers { self: Quasiquotes =>
*
* Example:
*
- * reifyMultiCardinalityList(lst) {
- * // first we define patterns that extract high-cardinality holeMap (currently ..)
+ * reifyHighRankList(lst) {
+ * // first we define patterns that extract high-rank holeMap (currently ..)
* case Placeholder(IterableType(_, _)) => tree
* } {
* // in the end we define how single elements are reified, typically with default reify call
@@ -306,10 +306,10 @@ trait Reifiers { self: Quasiquotes =>
* Sample execution of previous concrete list reifier:
*
* > val lst = List(foo, bar, qq$f3948f9s$1)
- * > reifyMultiCardinalityList(lst) { ... } { ... }
+ * > reifyHighRankList(lst) { ... } { ... }
* q"List($foo, $bar) ++ ${holeMap(qq$f3948f9s$1).tree}"
*/
- def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree
+ def reifyHighRankList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree
val fillListHole: PartialFunction[Any, Tree] = {
case Placeholder(Hole(tree, DotDot)) => tree
@@ -331,16 +331,16 @@ trait Reifiers { self: Quasiquotes =>
}
/** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put
- * in the correct position. Fallbacks to regular reification for non-high cardinality
+ * in the correct position. Fallbacks to regular reification for zero rank
* elements.
*/
- override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs)(fillListHole.orElse(fillListOfListsHole))(reify)
+ override def reifyList(xs: List[Any]): Tree = reifyHighRankList(xs)(fillListHole.orElse(fillListOfListsHole))(reify)
- def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
+ def reifyAnnotList(annots: List[Tree]): Tree = reifyHighRankList(annots) {
case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h)
} {
case AnnotPlaceholder(h: ApplyHole) if h.tpe <:< treeType => reifyAnnotation(h)
- case AnnotPlaceholder(h: UnapplyHole) if h.cardinality == NoDot => reifyAnnotation(h)
+ case AnnotPlaceholder(h: UnapplyHole) if h.rank == NoDot => reifyAnnotation(h)
case other => reify(other)
}
@@ -372,7 +372,7 @@ trait Reifiers { self: Quasiquotes =>
}
class ApplyReifier extends Reifier(isReifyingExpressions = true) {
- def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree =
+ def reifyHighRankList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree =
if (xs.isEmpty) mkList(Nil)
else {
def reifyGroup(group: List[Any]): Tree = group match {
@@ -399,12 +399,12 @@ trait Reifiers { self: Quasiquotes =>
}
mods match {
case hole :: Nil =>
- if (flags.nonEmpty) c.abort(flags(0).pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
- if (annots.nonEmpty) c.abort(hole.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
+ if (flags.nonEmpty) c.abort(flags(0).pos, "Can't unquote flags together with modifiers, consider merging flags into modifiers")
+ if (annots.nonEmpty) c.abort(hole.pos, "Can't unquote modifiers together with annotations, consider merging annotations into modifiers")
ensureNoExplicitFlags(m, hole.pos)
hole.tree
case _ :: hole :: Nil =>
- c.abort(hole.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
+ c.abort(hole.pos, "Can't unquote multiple modifiers, consider merging them into a single modifiers instance")
case _ =>
val baseFlags = reifyFlags(m.flags)
val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, hole) => Apply(Select(flag, nme.OR), List(hole.tree)) }
@@ -423,7 +423,7 @@ trait Reifiers { self: Quasiquotes =>
// pq"$lhs :: $rhs"
private def cons(lhs: Tree, rhs: Tree) = Apply(collectionCons, lhs :: rhs :: Nil)
- def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree = {
+ def reifyHighRankList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree = {
val grouped = group(xs) { (a, b) => !fill.isDefinedAt(a) && !fill.isDefinedAt(b) }
def appended(lst: List[Any], init: Tree) = lst.foldLeft(init) { (l, r) => append(l, fallback(r)) }
def prepended(lst: List[Any], init: Tree) = lst.foldRight(init) { (l, r) => cons(fallback(l), r) }