Skip to content

Commit 0c08989

Browse files
committed
Add unused params State machine example
1 parent 9498024 commit 0c08989

File tree

3 files changed

+117
-4
lines changed

3 files changed

+117
-4
lines changed

docs/docs/reference/unused-terms.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,62 @@ methodWithUnusedEv(evidence1)
4949
methodWithUnusedEv(unusedEvidence2)
5050
```
5151

52-
Examples
53-
--------
54-
TODO
52+
State machine example
53+
---------------------
54+
The following examples shows an implementation of a simple state machine which can be in a state `On` or `Off`.
55+
The machine can change state from `Off` to `On` with `turnedOn` only if it is currently `Off`,
56+
conversely from `On` to `Off` with `turnedOff` only if it is currently `On`. These last constraint are
57+
captured with the `IsOff[S]` and `IsOn[S]` implicit evidence only exist for `IsOff[Off]` and `InOn[On]`.
58+
For example, not allowing calling `turnedOff` on in an `Off` state as we would require an evidence `IsOn[Off]`
59+
that will not be found.
60+
61+
As the implicit evidences of `turnedOn` and `turnedOff` are not used in the bodies of those functions
62+
we can mark them as `unused`. This will remove the evidence parameters at runtime, but we would still
63+
evaluate the `isOn` and `isOff` implicits that where found as arguments.
64+
As `isOn` and `isOff` are not used except as as `unused` arguments, we can mark them as `unused`, hence
65+
removing the evaluation of the `isOn` and `isOff` evidences.
5566

5667
```scala
57-
68+
import scala.annotation.implicitNotFound
69+
70+
sealed trait State
71+
final class On extends State
72+
final class Off extends State
73+
74+
@implicitNotFound("State is must be Off")
75+
class IsOff[S <: State]
76+
object IsOff {
77+
unused implicit def isOff: IsOff[Off] = new IsOff[Off]
78+
}
79+
80+
@implicitNotFound("State is must be On")
81+
class IsOn[S <: State]
82+
object IsOn {
83+
unused implicit def isOn: IsOn[On] = new IsOn[On]
84+
}
85+
86+
class Machine[S <: State] private {
87+
def turnedOn(implicit unused ev: IsOff[S]): Machine[On] = new Machine[On]
88+
def turnedOff(implicit unused ev: IsOn[S]): Machine[Off] = new Machine[Off]
89+
}
90+
91+
object Machine {
92+
def newMachine(): Machine[Off] = new Machine[Off]
93+
}
94+
95+
object Test {
96+
def main(args: Array[String]): Unit = {
97+
val m = Machine.newMachine()
98+
m.turnedOn
99+
m.turnedOn.turnedOff
100+
101+
// m.turnedOff
102+
// ^
103+
// State is must be On
104+
105+
// m.turnedOn.turnedOn
106+
// ^
107+
// State is must be Off
108+
}
109+
}
58110
```

tests/run/unused-machine-state.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
newMachine
2+
turnedOn
3+
turnedOn
4+
turnedOff

tests/run/unused-machine-state.scala

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import scala.annotation.implicitNotFound
2+
3+
sealed trait State
4+
final class On extends State
5+
final class Off extends State
6+
7+
@implicitNotFound("State is must be Off")
8+
class IsOff[S <: State]
9+
object IsOff {
10+
unused implicit def isOff: IsOff[Off] = {
11+
println("isOff")
12+
new IsOff[Off]
13+
}
14+
}
15+
16+
@implicitNotFound("State is must be On")
17+
class IsOn[S <: State]
18+
object IsOn {
19+
unused implicit def isOn: IsOn[On] = {
20+
println("isOn")
21+
new IsOn[On]
22+
}
23+
}
24+
25+
class Machine[S <: State] private {
26+
def turnedOn(implicit unused s: IsOff[S]): Machine[On] = {
27+
println("turnedOn")
28+
new Machine[On]
29+
}
30+
def turnedOff(implicit unused s: IsOn[S]): Machine[Off] = {
31+
println("turnedOff")
32+
new Machine[Off]
33+
}
34+
}
35+
36+
object Machine {
37+
def newMachine(): Machine[Off] = {
38+
println("newMachine")
39+
new Machine[Off]
40+
}
41+
}
42+
43+
object Test {
44+
def main(args: Array[String]): Unit = {
45+
val m = Machine.newMachine()
46+
m.turnedOn
47+
m.turnedOn.turnedOff
48+
49+
// m.turnedOff
50+
// ^
51+
// State is must be On
52+
53+
// m.turnedOn.turnedOn
54+
// ^
55+
// State is must be Off
56+
}
57+
}

0 commit comments

Comments
 (0)