diff --git a/_sips/sips/2018-08-20-converters-among-optional-functions-partialfunctions-and-extractor-objects.md b/_sips/sips/2018-08-20-converters-among-optional-functions-partialfunctions-and-extractor-objects.md new file mode 100644 index 0000000000..0e2b8e9ca5 --- /dev/null +++ b/_sips/sips/2018-08-20-converters-among-optional-functions-partialfunctions-and-extractor-objects.md @@ -0,0 +1,147 @@ +--- +layout: sips +discourse: true +title: SIP-NN - Converters among optional Functions, PartialFunctions and extractor objects +--- + +**By: Yang Bo** + + +## History + +| Date | Version | +|---------------|---------------| +| Aug 20th 2018 | Initial Draft | + +## Motivation + +There are three types in Scala to represent a function that accept some of the parameters: + +1. optional functions: `A => Option[B]` +2. extracter objects: `{ def unapply(a: A): Option[B] }` and `{ def unapplySeq(a: A): Option[Seq[B]] }` +3. partial fucntions: `PartialFunction[A, B]` + +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. + +This proposal makes `PartialFunction` be an extractor, and provides an `unlift` method to convert optional functions to `PartialFunction`s. + +## Motivating Examples + +{% highlight scala %} +// Define a PartialFunction +val pf: PartialFunction[Int, String] = { + case 1 => "matched by a PartialFunction" +} + +// Define an optional function +val of: Int => Option[String] = { i => + if (i == 2) { + Some("matched by an optional function") + } else { + None + } +} + +util.Random.nextInt(4) match { + case pf(m) => // A PartialFunction itself is a pattern + println(m) + case of.unlift(m) => // Convert an optional function to a pattern + println(m) + case _ => + println("Not matched") +} +{% endhighlight %} + +In addition, `elementWise` can be used to create an object with a `unapplySeq` method, which extracts each element of a sequence data. + +{% highlight scala %} +val firstChar: String => Option[Char] = _.headOption + +Seq("foo", "bar", "baz") match { + case firstChar.unlift.elementWise(c0, c1, c2) => + println(s"$c0, $c1, $c2") // Output: f, b, b +} +{% endhighlight %} + +## Cheat sheet + +This proposal allows converting among optional Functions, PartialFunctions and extractor objects as shown in the following table. + +
+ How to convert ... + | ++ to a partial function + | ++ to an optional function + | ++ to an extractor + | +
---|---|---|---|
+ from a partial function + | +
+ partialFunction
+ |
+
+ partialFunction.lift
+ |
+
+ partialFunction
+ |
+
+ from an optional function + | +
+ optionalFunction.unlift or Function.unlift(optionalFunction)
+ |
+
+ optionalFunction
+ |
+
+ optionalFunction.unlift
+ |
+
+ from an extractor + | +
+ { case extractor(x) => x }
+ |
+
+ extractor.unapply _
+ |
+
+ extractor
+ |
+