@@ -8,27 +8,29 @@ _Context functions_ are functions with (only) context parameters.
88Their types are _ context function types_ . Here is an example of a context function type:
99
1010``` scala
11+ import scala .concurrent .ExecutionContext
12+
1113type Executable [T ] = ExecutionContext ?=> T
1214```
1315Context functions are written using ` ?=> ` as the "arrow" sign.
1416They are applied to synthesized arguments, in
1517the same way methods with context parameters are applied. For instance:
1618``` scala
17- given ec : ExecutionContext = ...
19+ given ec : ExecutionContext = ...
1820
19- def f (x : Int ): ExecutionContext ?=> Int = ...
21+ def f (x : Int ): ExecutionContext ?=> Int = ...
2022
21- // could be written as follows with the type alias from above
22- // def f(x: Int): Executable[Int] = ...
23+ // could be written as follows with the type alias from above
24+ // def f(x: Int): Executable[Int] = ...
2325
24- f(2 )(using ec) // explicit argument
25- f(2 ) // argument is inferred
26+ f(2 )(using ec) // explicit argument
27+ f(2 ) // argument is inferred
2628```
2729Conversely, if the expected type of an expression ` E ` is a context function type
2830` (T_1, ..., T_n) ?=> U ` and ` E ` is not already an
2931context function literal, ` E ` is converted to a context function literal by rewriting it to
3032``` scala
31- (x_1 : T1 , ..., x_n : Tn ) ?=> E
33+ (x_1 : T1 , ..., x_n : Tn ) ?=> E
3234```
3335where the names ` x_1 ` , ..., ` x_n ` are arbitrary. This expansion is performed
3436before the expression ` E ` is typechecked, which means that ` x_1 ` , ..., ` x_n `
@@ -38,14 +40,14 @@ Like their types, context function literals are written using `?=>` as the arrow
3840
3941For example, continuing with the previous definitions,
4042``` scala
41- def g (arg : Executable [Int ]) = ...
43+ def g (arg : Executable [Int ]) = ...
4244
43- g(22 ) // is expanded to g((ev: ExecutionContext) ?=> 22)
45+ g(22 ) // is expanded to g((ev: ExecutionContext) ?=> 22)
4446
45- g(f(2 )) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev))
47+ g(f(2 )) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev))
4648
47- g((ctx : ExecutionContext ) ?=> f(3 )) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx))
48- g((ctx : ExecutionContext ) ?=> f(3 )(using ctx)) // is left as it is
49+ g((ctx : ExecutionContext ) ?=> f(3 )) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx))
50+ g((ctx : ExecutionContext ) ?=> f(3 )(using ctx)) // is left as it is
4951```
5052
5153## Example: Builder Pattern
@@ -54,63 +56,65 @@ Context function types have considerable expressive power. For
5456instance, here is how they can support the "builder pattern", where
5557the aim is to construct tables like this:
5658``` scala
57- table {
58- row {
59- cell(" top left" )
60- cell(" top right" )
61- }
62- row {
63- cell(" bottom left" )
64- cell(" bottom right" )
65- }
59+ table {
60+ row {
61+ cell(" top left" )
62+ cell(" top right" )
63+ }
64+ row {
65+ cell(" bottom left" )
66+ cell(" bottom right" )
6667 }
68+ }
6769```
6870The idea is to define classes for ` Table ` and ` Row ` that allow the
6971addition of elements via ` add ` :
7072``` scala
71- class Table :
72- val rows = new ArrayBuffer [Row ]
73- def add (r : Row ): Unit = rows += r
74- override def toString = rows.mkString(" Table(" , " , " , " )" )
73+ import scala .collection .mutable .ArrayBuffer
74+
75+ class Table :
76+ val rows = new ArrayBuffer [Row ]
77+ def add (r : Row ): Unit = rows += r
78+ override def toString = rows.mkString(" Table(" , " , " , " )" )
7579
76- class Row :
77- val cells = new ArrayBuffer [Cell ]
78- def add (c : Cell ): Unit = cells += c
79- override def toString = cells.mkString(" Row(" , " , " , " )" )
80+ class Row :
81+ val cells = new ArrayBuffer [Cell ]
82+ def add (c : Cell ): Unit = cells += c
83+ override def toString = cells.mkString(" Row(" , " , " , " )" )
8084
81- case class Cell (elem : String )
85+ case class Cell (elem : String )
8286```
8387Then, the ` table ` , ` row ` and ` cell ` constructor methods can be defined
8488with context function types as parameters to avoid the plumbing boilerplate
8589that would otherwise be necessary.
8690``` scala
87- def table (init : Table ?=> Unit ) =
88- given t : Table = Table ()
89- init
90- t
91-
92- def row (init : Row ?=> Unit )(using t : Table ) =
93- given r : Row = Row ()
94- init
95- t.add(r)
96-
97- def cell (str : String )(using r : Row ) =
98- r.add(new Cell (str))
91+ def table (init : Table ?=> Unit ) =
92+ given t : Table = Table ()
93+ init
94+ t
95+
96+ def row (init : Row ?=> Unit )(using t : Table ) =
97+ given r : Row = Row ()
98+ init
99+ t.add(r)
100+
101+ def cell (str : String )(using r : Row ) =
102+ r.add(new Cell (str))
99103```
100104With that setup, the table construction code above compiles and expands to:
101105``` scala
102- table { ($t : Table ) ?=>
103-
104- row { ($r : Row ) ?=>
105- cell(" top left" )(using $r)
106- cell(" top right" )(using $r)
107- }(using $t)
108-
109- row { ($r : Row ) ?=>
110- cell(" bottom left" )(using $r)
111- cell(" bottom right" )(using $r)
112- }(using $t)
113- }
106+ table { ($t : Table ) ?=>
107+
108+ row { ($r : Row ) ?=>
109+ cell(" top left" )(using $r)
110+ cell(" top right" )(using $r)
111+ }(using $t)
112+
113+ row { ($r : Row ) ?=>
114+ cell(" bottom left" )(using $r)
115+ cell(" bottom right" )(using $r)
116+ }(using $t)
117+ }
114118```
115119## Example: Postconditions
116120
@@ -131,12 +135,18 @@ import PostConditions.{ensuring, result}
131135
132136val s = List (1 , 2 , 3 ).sum.ensuring(result == 6 )
133137```
134- ** Explanations** : We use a context function type ` WrappedResult[T] ?=> Boolean `
138+ ### Explanation
139+
140+ We use a context function type ` WrappedResult[T] ?=> Boolean `
135141as the type of the condition of ` ensuring ` . An argument to ` ensuring ` such as
136142` (result == 6) ` will therefore have a given of type ` WrappedResult[T] ` in
137- scope to pass along to the ` result ` method. ` WrappedResult ` is a fresh type, to make sure
143+ scope to pass along to the ` result ` method.
144+
145+ ` WrappedResult ` is a fresh type, to make sure
138146that we do not get unwanted givens in scope (this is good practice in all cases
139- where context parameters are involved). Since ` WrappedResult ` is an opaque type alias, its
147+ where context parameters are involved).
148+
149+ Since ` WrappedResult ` is an opaque type alias, its
140150values need not be boxed, and since ` ensuring ` is added as an extension method, its argument
141151does not need boxing either. Hence, the implementation of ` ensuring ` is close in efficiency to the best possible code one could write by hand:
142152
0 commit comments