Skip to content

Commit fa5adde

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

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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 makes `PartialFunction` be an extractor, and provides an `unlift` method to convert optional functions to `PartialFunction`s.
27+
28+
## Motivating Examples
29+
30+
{% highlight scala %}
31+
// Define a PartialFunction
32+
val pf: PartialFunction[Int, String] = {
33+
case 1 => "matched by a PartialFunction"
34+
}
35+
36+
// Define an optional function
37+
val of: Int => Option[String] = { i =>
38+
if (i == 2) {
39+
Some("matched by an optional function")
40+
} else {
41+
None
42+
}
43+
}
44+
45+
util.Random.nextInt(4) match {
46+
case pf(m) => // A PartialFunction itself is a pattern
47+
println(m)
48+
case of.unlift(m) => // Convert an optional function to a pattern
49+
println(m)
50+
case _ =>
51+
println("Not matched")
52+
}
53+
{% endhighlight %}
54+
55+
In addition, `elementWise` can be used to create an object with a `unapplySeq` method, which extracts each element of a sequence data.
56+
57+
{% highlight scala %}
58+
val firstChar: String => Option[Char] = _.headOption
59+
60+
Seq("foo", "bar", "baz") match {
61+
case firstChar.unlift.elementWise(c0, c1, c2) =>
62+
println(s"$c0, $c1, $c2") // Output: f, b, b
63+
}
64+
{% endhighlight %}
65+
66+
## Implementation
67+
68+
The idea was originally implemented in a library: [Extractor.scala](https://github.com/ThoughtWorksInc/Extractor.scala), which has been used in [Binding.scala](https://github.com/ThoughtWorksInc/Binding.scala/blob/10.0.x/XmlExtractor/src/main/scala/com/thoughtworks/binding/XmlExtractor.scala#L63) and [sbt-api-mappings](https://github.com/ThoughtWorksInc/sbt-api-mappings/blob/f4e1353/src/main/scala/com/thoughtworks/sbtApiMappings/ApiMappings.scala#L48).
69+
70+
The new implementation aims to become part of core library. The pull request can be found at [#7111][2].
71+
72+
## Drawbacks
73+
74+
Why should we *not* do this. Be honest, these questions will come out during the
75+
process anyway so it's better to get them out up front.
76+
77+
## References
78+
79+
1. [Existing Implementation (Extractor.scala)][1]
80+
2. [Related Pull Request][2]
81+
82+
[1]: https://github.com/ThoughtWorksInc/Extractor.scala "Extractor.scala"
83+
[2]: https://github.com/scala/scala/pull/7111 "#7111"

0 commit comments

Comments
 (0)