Skip to content

Commit bcaaa38

Browse files
committed
Add section on implicit function types
1 parent 3092341 commit bcaaa38

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
layout: doc-page
3+
title: "Implicit Function Types"
4+
---
5+
6+
An implicit funciton type describes functions with implicit parameters. Example:
7+
8+
type Contextual[T] = implicit Context => T
9+
10+
A value of implicit function type is applied to implicit arguments, in
11+
the same way a method with implicit parameters is applied. For instance:
12+
13+
implicit ctx: Context = ...
14+
15+
def f(x: Int): Contextual[Int] = ...
16+
17+
f(2) // is expanded to f(2)(ctx)
18+
19+
Conversely, if the expected type of an expression `E` is an implicit
20+
function type `implicit (T_1, ..., T_n) => U` and `E` is not already an
21+
implicit function value, `E` is converted to an implicit function value
22+
by rewriting to
23+
24+
implicit (x_1: T1, ..., x_n: Tn) => E
25+
26+
where the names `x_1`, ..., `x_n` are arbitrary. For example, continuing
27+
with the previous definitions,
28+
29+
def g(arg: Contextual[Int]) = ...
30+
31+
g(22) // is expanded to g { implicit ctx => 22 }
32+
33+
g(f) // is expanded to g { implicit ctx => f(2)(ctx) }
34+
35+
g(implicit ctx => f(22)(ctx)) // is left as it is
36+
37+
Implicit function types have considerable expressive power. For
38+
instance, here is how they can support the "builder pattern", where
39+
the aim is to construct tables like this:
40+
41+
table {
42+
row {
43+
cell("top left")
44+
cell("top right")
45+
}
46+
row {
47+
cell("botttom left")
48+
cell("bottom right")
49+
}
50+
}
51+
52+
The idea is to define classes for `Table` and `Row` that allow
53+
addition of elements via `add`:
54+
55+
class Table {
56+
val rows = new ArrayBuffer[Row]
57+
def add(r: Row): Unit = rows += r
58+
override def toString = rows.mkString("Table(", ", ", ")")
59+
}
60+
61+
class Row {
62+
val cells = new ArrayBuffer[Cell]
63+
def add(c: Cell): Unit = cells += c
64+
override def toString = cells.mkString("Row(", ", ", ")")
65+
}
66+
67+
case class Cell(elem: String)
68+
69+
Then, the `table`, `row` and `cell` constructor methods can be defined
70+
in terms of implicit function types to avoid the plumbing boilerplate
71+
that would otherwise be necessary.
72+
73+
def table(init: implicit Table => Unit) = {
74+
implicit val t = new Table
75+
init
76+
t
77+
}
78+
79+
def row(init: implicit Row => Unit)(implicit t: Table) = {
80+
implicit val r = new Row
81+
init
82+
t.add(r)
83+
}
84+
85+
def cell(str: String)(implicit r: Row) =
86+
r.add(new Cell(str))
87+
88+
With that setup, the table construction code above compiles and expands to:
89+
90+
table { implicit $t: Table =>
91+
row { implicit $r: Row =>
92+
cell("top left")($r)
93+
cell("top right")($r)
94+
}($t)
95+
row { implicit $r: Row =>
96+
cell("botttom left")($r)
97+
cell("bottom right")($r)
98+
}($t)
99+
}

0 commit comments

Comments
 (0)