问题
trying to understand some scala syntax, and where to find their spec. Bellow i am confused about statefulMapConcat.
The signature is this one:
def statefulMapConcat[T](f: () => Out => immutable.Iterable[T]): Repr[T]
and we have
"be able to restart" in {
Source(List(2, 1, 3, 4, 1))
.statefulMapConcat(() => {
var prev: Option[Int] = None
x => {
if (x % 3 == 0) throw ex
prev match {
case Some(e) =>
prev = Some(x)
(1 to e).map(_ => x)
case None =>
prev = Some(x)
List.empty[Int]
}
}
})
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider))
.runWith(TestSink.probe[Int])
.request(2)
.expectNext(1, 1)
.request(4)
.expectNext(1, 1, 1, 1)
.expectComplete()
}
I do not under well how
{
var prev: Option[Int] = None
x => {
if (x % 3 == 0) throw ex
prev match {
case Some(e) =>
prev = Some(x)
(1 to e).map(_ => x)
case None =>
prev = Some(x)
List.empty[Int]
}
}
correspond to
Out => immutable.Iterable[T]
Can someone decompose it for me please ?
in my mind x
would correspond to Out
, but then we have the declaration of a variable before var prev: Option[Int] = None
I would like to understand and where to find explanation about those scala magic in general
回答1:
Two key concepts to understand here are
- the value of the whole block expression is the value of the last expression in the block, and
- functions are first-class values
Consider the following block expression
{
var prev: Option[Int] = None
42 // I am the last value of the block
}
Because the last expression in the block evaluates to value 42
, the value of the whole block is 42
of type Int
.
Similarly, consider the following block expression
{
var prev: Option[Int] = None
(x: Int) => { x + 1 } // I am also the last *value* of the block
}
Because the last expression in the block evaluates to value (x: Int) => { x + 1 }
, the value of the whole block is value (x: Int) => { x + 1 }
of type Int => Int
.
In both cases, because expression var prev: Option[Int] = None
is not the last expression in the block, it does not affect the type of the value of the whole block.
回答2:
Quick illustration. Actually, no interior braces when you write the single arg as a block:
scala> def f(g: () => String => Int) = g()("42")
f: (g: () => String => Int)Int
scala> f(() => _.toInt)
res0: Int = 42
scala> f { () => s => s.toInt }
res1: Int = 42
scala> f ( () => s => s.toInt )
res2: Int = 42
scala> f { val x = "0" ; () => val y = x + "0" ; s => (s+y).toInt }
res3: Int = 4200
Showing with -Vprint:parser
(-Xprint
on 2.12) that it parses correctly, even though the semicolons might fool the eye:
val res3 = f({
val x = "0";
(() => {
val y = x.$plus("0");
((s) => s.$plus(y).toInt)
})
})
Edit: the magically captured var is on the heap:
scala> { var i = 42 ; (j: Int) => j + i }
res1: Int => Int = $$Lambda$892/0x00000001006ec840@6e981e78
scala> :javap -c -
Compiled from "<console>"
public class $line4.$read$$iw$$iw$ {
public static final $line4.$read$$iw$$iw$ MODULE$;
public static {};
Code:
0: new #2 // class $line4/$read$$iw$$iw$
3: dup
4: invokespecial #25 // Method "<init>":()V
7: putstatic #27 // Field MODULE$:L$line4/$read$$iw$$iw$;
10: bipush 42
12: invokestatic #33 // Method scala/runtime/IntRef.create:(I)Lscala/runtime/IntRef;
15: astore_0
16: aload_0
17: invokedynamic #52, 0 // InvokeDynamic #0:apply$mcII$sp:(Lscala/runtime/IntRef;)Lscala/runtime/java8/JFunction1$mcII$sp;
22: putstatic #54 // Field res1:Lscala/Function1;
25: return
public scala.Function1<java.lang.Object, java.lang.Object> res1();
Code:
0: getstatic #54 // Field res1:Lscala/Function1;
3: areturn
public static final int $anonfun$res1$1(scala.runtime.IntRef, int);
Code:
0: iload_1
1: aload_0
2: getfield #65 // Field scala/runtime/IntRef.elem:I
5: iadd
6: ireturn
public $line4.$read$$iw$$iw$();
Code:
0: aload_0
1: invokespecial #66 // Method java/lang/Object."<init>":()V
4: return
}
来源:https://stackoverflow.com/questions/59462539/confusing-scala-syntax