Download - First-Class Patterns
![Page 1: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/1.jpg)
First-Class PatternsJohn A. De Goes - @jdegoes
Frontier Developers, February 26
![Page 2: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/2.jpg)
Agenda● Intro● Pattern Matching 101● First-Class-ness 101● Magical Patterns● First-Class Patterns● Exercises
![Page 3: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/3.jpg)
IntroPattern Matching
● Divides a (possibly infinite) set of values into a discrete number of cases, where each case can be handled in a uniform way
● “if” on steroids○ Sometimes strictly more powerful (e.g. Haskell)
![Page 4: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/4.jpg)
Intro - Examples-- sign of a number
sign x | x > 0 = 1 | x == 0 = 0 | x < 0 = -1
-- take the first n elements from a list
take 0 _ = []take _ [] = []take n (x:xs) = x : take (n-1) xs
-- generate some javascript
valueToJs :: Options -> ModuleName -> Environment -> Value -> JSvalueToJs _ _ _ (NumericLiteral n) = JSNumericLiteral nvalueToJs _ _ _ (StringLiteral s) = JSStringLiteral svalueToJs _ _ _ (BooleanLiteral b) = JSBooleanLiteral b
...
![Page 5: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/5.jpg)
Intro - Examplessealed trait Level
case object Level1 extends Level
case object Level2 extends Level
sealed trait Title
case object DBAdmin extends Title
case class SWEngineer(level: Level) extends Title
case class Employee(manager: Option[Employee], name: String, title: Title)
val employees = ???
val selfManagedLevel2Engineers = employees.collect {
case Employee(None, name, SWEngineer(Level2)) => name
}
![Page 6: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/6.jpg)
Pattern Matching 101
Filter
Extract
Does it have the structure I want? [Yes/No]
If so, extract out the pieces that are relevant to me.
![Page 7: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/7.jpg)
Pattern Matching 101-- take the first n elements from a list
take 0 _ = []take _ [] = []take n (x:xs) = x : take (n-1) xs
Filter - does it have non-empty list structure?
Extract - Give me ‘head’ and ‘tail’
![Page 8: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/8.jpg)
Pattern Matching 101Products Sumscase class Employee(
manager: Option[Employee],
name: String,
title: Title
)
sealed trait Title
case object DBAdmin extends Title
case class SWEngineer(level: Level)
extends Title
class Account {
...
public Account(BigDecimal balance, User holder) {
...
}
}
interface Shape { }
class Rect extends Shape { … }
class Ellipse extends Shape { … }
class Pentagon extends Shape { … }
terms terms
![Page 9: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/9.jpg)
First-Class-ness 101
data Maybe a = Nothing | Just a deriving (Eq, Ord) class Person {
public Person(String name, int age) {
...
}
}
![Page 10: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/10.jpg)
First-Class-ness 101
Magic interferes with your ability to abstract and compose.
![Page 11: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/11.jpg)
Magical PatternsOrdinary Duplicationsealed trait Provenanceobject Provenance { … case class Either(left: Provenance, right: Provenance) extends Provenance case class Both(left: Provenance, right: Provenance) extends Provenance
def allOf(xs: Seq[Provenance]): Provenance = { if (xs.length == 0) Unknown else if (xs.length == 1) xs.head else xs.reduce(Both.apply) }
def anyOf(xs: Seq[Provenance]): Provenance = { if (xs.length == 0) Unknown else if (xs.length == 1) xs.head else xs.reduce(Either.apply) }}
![Page 12: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/12.jpg)
Magical PatternsFactoringsealed trait Provenanceobject Provenance { case object Unknown extends Provenance case object Value extends Provenance case class Relation(value: SqlRelation) extends Provenance case class Either(left: Provenance, right: Provenance) extends Provenance case class Both(left: Provenance, right: Provenance) extends Provenance
def allOf(xs: Seq[Provenance]): Provenance = reduce(xs)(Both.apply)
def anyOf(xs: Seq[Provenance]): Provenance = reduce(xs)(Either.apply)
private def reduce(xs: Seq[Provenance])(f: (Provenance, Provenance) => Provenance): Provenance = { if (xs.length == 0) Unknown else if (xs.length == 1) xs.head else xs.reduce(f) }}
![Page 13: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/13.jpg)
Magical PatternsFactoringz sealed trait Provenance object Provenance { case object Unknown extends Provenance case object Value extends Provenance case class Relation(value: SqlRelation) extends Provenance case class Either(left: Provenance, right: Provenance) extends Provenance case class Both(left: Provenance, right: Provenance) extends Provenance
private def strict[A, B, C](f: (A, B) => C): (A, => B) => C = (a, b) => f(a, b)
val BothMonoid = Monoid.instance(strict[Provenance, Provenance, Provenance](Both.apply), Unknown) val EitherMonoid = Monoid.instance(strict[Provenance, Provenance, Provenance](Either.apply), Unknown)
def allOf(xs: List[Provenance]): Provenance = Foldable[List].foldMap(xs)(identity)(BothMonoid)
def anyOf(xs: List[Provenance]): Provenance = Foldable[List].foldMap(xs)(identity)(EitherMonoid) }
![Page 14: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/14.jpg)
Magical PatternsToxic Duplication - Abstraction Fail val Add = Mapping("(+)", "Adds two numeric values", NumericDomain,
(partialTyper {
case Type.Const(Data.Number(v1)) :: v2 :: Nil if (v1.signum == 0) => v2
case v1 :: Type.Const(Data.Number(v2)) :: Nil if (v2.signum == 0) => v1
case Type.Const(Data.Int(v1)) :: Type.Const(Data.Int(v2)) :: Nil =>
Type.Const(Data.Int(v1 + v2))
case Type.Const(Data.Number(v1)) :: Type.Const(Data.Number(v2)) :: Nil =>
Type.Const(Data.Dec(v1 + v2))
}) ||| numericWidening
)
![Page 15: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/15.jpg)
Magical PatternsPattern Combinators - Composition Fail
● Product & sum○ PartialFunction.orElse
● Negation● Defaults● Use-case specific
![Page 16: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/16.jpg)
Magical Patterns
Magical patterns limit your ability to abstract over patterns and to
compose them together to create new patterns.
![Page 17: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/17.jpg)
First-Class Patterns
First-class patterns are built using other language features so you can
abstract and compose them.
![Page 18: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/18.jpg)
First-Class PatternsHaskell Exampleex4 :: Either (Int,Int) Int -> Int
ex4 a = match a $
(1+) <$> (left (pair var (cst 4)) ->> id
<|> right var ->> id)
<|> left (pair __ var) ->> id
http://hackage.haskell.org/package/first-class-patterns
![Page 19: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/19.jpg)
First-Class Patterns“For the rest of us”
X match {
case Y => Z
}
type Pattern???
![Page 20: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/20.jpg)
First-Class PatternsStructure
X match {
case Y => Z
}
type Pattern???
Input
Output
Extraction
![Page 21: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/21.jpg)
First-Class PatternsOne ‘Option’
X match {
case Y => Z
}
type Pattern[X, Z] = X => Option[Z]
Input
Output
Extraction
![Page 22: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/22.jpg)
First-Class PatternsBasic Patterns
def some[A, B](p: Pattern[A, B]): Pattern[Option[A], B] = _.flatMap(p)
def none[A, B]: Pattern[A, B] = Function.const( None)
def k[A](v0: A): Pattern[A, A] = v => if (v == v0) Some(v0) else None
def or[A, B](p1: Pattern[A, B], p2: Pattern[A, B]): Pattern[A, B] = v => p1(v).orElse(p2(v))
scala> or(none, some(k( 2)))(Some(2))res3: Option[Int] = Some(2)
https://gist.github.com/jdegoes/9240971
![Page 23: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/23.jpg)
First-Class Patterns - JS
http://jsfiddle.net/AvL4V/
And Now in JavaScript….Because
![Page 24: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/24.jpg)
First-Class PatternsLimitations
● Extractors cannot throw away information, leading to ‘dummy parameters’
● Negation not possible under any circumstances
● No partiality warnings or built-in catch all
![Page 25: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/25.jpg)
Exercises1. Define a Pattern Combinator to Fix: val Add = Mapping("(+)", "Adds two numeric values", NumericDomain,
(partialTyper {
case Type.Const(Data.Number(v1)) :: v2 :: Nil if (v1.signum == 0) => v2
case v1 :: Type.Const(Data.Number(v2)) :: Nil if (v2.signum == 0) => v1
case Type.Const(Data.Int(v1)) :: Type.Const(Data.Int(v2)) :: Nil =>
Type.Const(Data.Int(v1 + v2))
case Type.Const(Data.Number(v1)) :: Type.Const(Data.Number(v2)) :: Nil =>
Type.Const(Data.Dec(v1 + v2))
}) ||| numericWidening
)
EASY
![Page 26: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/26.jpg)
Exercises2. Define ‘Pattern’ and some core patterns in the language of your choice
EASY
![Page 27: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/27.jpg)
Exercises3. Define an ‘And’ pattern that requires both inputs match
EASY
![Page 28: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/28.jpg)
Exercises4. Define an alternate definition of pattern (along with a few core patterns) that permits pattern negation
4.b Optional: Use this to allow catch-alls
MODERATE
![Page 29: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/29.jpg)
Exercises5. Define an alternate definition of pattern (along with a few core patterns) that permits extractors to throw away information
HARD
![Page 30: First-Class Patterns](https://reader034.vdocuments.net/reader034/viewer/2022051616/5538e625550346f02f8b48ea/html5/thumbnails/30.jpg)
THANK YOUFirst-Class Patterns
John A. De Goes - @jdegoesFrontier Developers, February 26