Skip to content

Commit 1360686

Browse files
committed
Create 2018-08-20-converters-among-optional-functions-partialfunctions-and-extractor-objects.md
1 parent 7455f07 commit 1360686

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
layout: sips
3+
discourse: true
4+
title: SIP-NN - Converters among optional Functions, PartialFunctions and extractor objects
5+
---
6+
7+
**By: Yang Bo**
8+
9+
10+
## History
11+
12+
| Date | Version |
13+
|---------------|---------------|
14+
| Aug 20th 2018 | Initial Draft |
15+
16+
## Motivation
17+
18+
There are three types in Scala to represent a function that accept some of the parameters:
19+
20+
1. optional functions: `A => Option[B]`
21+
2. extracter objects: `{ def unapply(a: A): Option[B] }` and `{ def unapplySeq(a: A): Option[Seq[B]] }`
22+
3. partial fucntions: `PartialFunction[A, B]`
23+
24+
Optional functions and partial functions can be converted to each other via `PartialFunction.lift` and `Function.unlift`. However, there is no simple approach to convert a partial function to an extractor object. As a result, partial functions are not composable. You cannot create a partial function then use it as a pattern in another partial function.
25+
26+
This proposal provide an `extract` method to convert all these representation to extractor objects.
27+
28+
## Motivating Examples
29+
30+
{% highlight scala %}
31+
// Define a PartialFunction
32+
val pf: PartialFunction[Int, String] = {
33+
case 1 => "matched by PartialFunction"
34+
}
35+
36+
// Define an optional function
37+
val f: Int => Option[String] = { i =>
38+
if (i == 2) {
39+
Some("matched by optional function")
40+
} else {
41+
None
42+
}
43+
}
44+
45+
// Convert an optional function to a PartialFunction
46+
val pf2: PartialFunction[Int, String] = Function.unlift(f)
47+
48+
util.Random.nextInt(4) match {
49+
case pf.extract(m) => // Convert a PartialFunction to a pattern
50+
println(m)
51+
case f.extract(m) => // Convert an optional function to a pattern
52+
println(m)
53+
case pf2.extract(m) => // Convert a PartialFunction to a pattern
54+
throw new AssertionError("This case should never occur because it has the same condition as `f.extract`.")
55+
case _ =>
56+
println("Not matched")
57+
}
58+
{% endhighlight %}
59+
60+
Alternatively, `extract.seq` can be used to create an object with a `unapplySeq` method, which extracts each element of a sequence data.
61+
62+
{% highlight scala %}
63+
val csvRow: String => Option[Seq[String]] = { line =>
64+
Some(line.split(','))
65+
}
66+
67+
"foo,bar,baz" match {
68+
case csvRow.extract.seq(cell0, cell1, cell2) =>
69+
println(s"cell1=$cell1") // Output: cell1=bar
70+
}
71+
{% endhighlight %}
72+
73+
## Implementation
74+
75+
The `extract` method has been implemented in a library: [Extractor.scala](https://github.com/ThoughtWorksInc/Extractor.scala), which has been used in Binding.scala in order to [extract patterns of XML literals](https://github.com/ThoughtWorksInc/Binding.scala/blob/10.0.x/XmlExtractor/src/main/scala/com/thoughtworks/binding/XmlExtractor.scala#L63).
76+
77+
## Drawbacks
78+
79+
Why should we *not* do this. Be honest, these questions will come out during the
80+
process anyway so it's better to get them out up front.
81+
82+
## References
83+
84+
1. [Existing Implementation (Extractor.scala)][1]
85+
86+
[1]: https://github.com/ThoughtWorksInc/Extractor.scala "Extractor.scala"

0 commit comments

Comments
 (0)