Tame Cloud Complex with F# powered DSLs

215
@theburningmonk tame |> (cloud <| complexity) |> with |> (fsharp >> powered >> DSLs)

Transcript of Tame Cloud Complex with F# powered DSLs

Page 1: Tame Cloud Complex with F# powered DSLs

@theburningmonk

tame |> (cloud <| complexity) |> with |> (fsharp >> powered >> DSLs)

Page 2: Tame Cloud Complex with F# powered DSLs
Page 3: Tame Cloud Complex with F# powered DSLs
Page 4: Tame Cloud Complex with F# powered DSLs
Page 5: Tame Cloud Complex with F# powered DSLs
Page 6: Tame Cloud Complex with F# powered DSLs

image by nerovivo license : https://creativecommons.org/licenses/by-sa/2.0/

Page 7: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Under-Abstraction

Page 8: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Oversimplification

Page 9: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Impedance Mismatch

Page 10: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazon DynamoDB

Amazon SimpleWorkflow

Amazon CloudWatch

CASE STUDY

Page 11: Tame Cloud Complex with F# powered DSLs

@theburningmonk

F# DSLs. Awesome!

Page 12: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazon DynamoDB

Amazon SimpleWorkflow

Amazon CloudWatch

CASE STUDY

Page 13: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 14: Tame Cloud Complex with F# powered DSLs

@theburningmonk

managedkey-value store

Page 15: Tame Cloud Complex with F# powered DSLs

@theburningmonk

redundancy9-9s guarantee

Page 16: Tame Cloud Complex with F# powered DSLs

@theburningmonk

great performance

Page 17: Tame Cloud Complex with F# powered DSLs

@theburningmonk

name your throughput

Page 18: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 19: Tame Cloud Complex with F# powered DSLs

@theburningmonk

can be changed on-the-fly

Page 20: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 21: Tame Cloud Complex with F# powered DSLs

@theburningmonk

infinitely scalable(but you still have to pay for it)

Page 22: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 23: Tame Cloud Complex with F# powered DSLs

Hash Key

Range Key

Page 24: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Query:given a hash keyfilter on

range key, orlocal secondary index

Page 25: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 26: Tame Cloud Complex with F# powered DSLs

Hash Key Range Key

Local Secondary Index

Global Secondary Index

Page 27: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Scan:FULL TABLE search(performance + cost concern)

Page 28: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 29: Tame Cloud Complex with F# powered DSLs

Hash Key Range Key

Local Secondary Index

Global Secondary Index

Page 30: Tame Cloud Complex with F# powered DSLs

Hash Key Range Key

Local Secondary Index

Who are the TOP 3 players in “Starship X” with a score of at least 1000?

Global Secondary Index

Page 31: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 32: Tame Cloud Complex with F# powered DSLs

@theburningmonk

select GameTitle, UserId, TopScore from GameScores where GameTitle = “Starship X” and TopScore >= 1000 order desc limit 3 with (NoConsistentRead, Index(GameTitleIndex, true))

Page 33: Tame Cloud Complex with F# powered DSLs

DynamoDB.SQL

github.com/fsprojects/DynamoDb.SQL

Page 34: Tame Cloud Complex with F# powered DSLs

@theburningmonk

GOAL

Disguise complexity

Page 35: Tame Cloud Complex with F# powered DSLs

@theburningmonk

GOAL

SELECT UserId, TopScore FROM GameScore WHERE GameTitle CONTAINS “Zelda” ORDER DESCLIMIT 3 WITH (NoConsistentRead)

Page 36: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Query

AST

Execution

Page 37: Tame Cloud Complex with F# powered DSLs

F# & FParsec*

*www.quanttec.com/fparsec

External DSL via

Page 38: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 39: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

Abstract Syntax Tree (AST)

FParsec

Page 40: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

keyword keyword

* | attribute, attribute, …

table name

Page 41: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

type Attributes = | Asterisk | Attributes of string[]

Page 42: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

type Query = { Attributes : Attributes Table : string }

Page 43: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

Parser for “SELECT” keyword

pSelect

Page 44: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepSelect

let pSelect = skipStringCI "select"

Page 45: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepSelect

let pSelect = skipStringCI "select"matches the string “select” (Case

Insensitive) and ignores it

Page 46: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepFrom

let pFrom = skipStringCI "from"

Page 47: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

Parser for a string that represents the table name

pTableName

Page 48: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepTableName

let isTableName = isLetter <||> isDigit let pTableName = many1Satisfy isTableName

Page 49: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepTableName

let isTableName = isLetter <||> isDigit let pTableName = many1Satisfy isTableName

Page 50: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepTableName

let isTableName = isLetter <||> isDigit let pTableName = many1Satisfy isTableName

Page 51: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepTableName

let isTableName = isLetter <||> isDigit let pTableName = many1Satisfy isTableName

Page 52: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepTableName

let isTableName = isLetter <||> isDigit let pTableName = many1Satisfy isTableName

parses a sequence of one or more chars that satisfies the

predicate function

Page 53: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeName

UserId, GameTitle, TopScore, …

Page 54: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeName

UserId, GameTitle, TopScore, …

let pAsterisk = stringCIReturn "*" Asterisk

Page 55: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeName

UserId, GameTitle, TopScore, …

let pAsterisk = stringCIReturn "*" Asteriskmatches the specified string and return the given value

Page 56: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeName

UserId, GameTitle, TopScore, …

let isAttributeName = isLetter <||> isDigit let pAttributeName = many1Satisfy isAttributeName

Page 57: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

UserId, GameTitle, TopScore, …

pAttributeName pCommapAsterisk

*

let pComma = skipStringCI ","

Page 58: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

UserId, GameTitle, TopScore, …

pAttributeName pCommapAsterisk

*

let pAttributeNames = sepBy1 pAttributeName pComma

Page 59: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

UserId, GameTitle, TopScore, …

pAttributeName pCommapAsterisk

*

let pAttributeNames = sepBy1 pAttributeName pComma

parses one or more occurrences of pAttributeName separated by pComma

Page 60: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

UserId, GameTitle, TopScore, …

pAttributeName pComma

sepBy1

pAttributeNames

pAsterisk

*

Page 61: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeNames

UserId, GameTitle, TopScore, …

Page 62: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeNames

UserId, GameTitle, TopScore, …

let pAttribute = pAsterisk <|> pAttributeNames

Page 63: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeNames

UserId, GameTitle, TopScore, …

let pAttribute = pAsterisk <|> pAttributeNames

Page 64: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScore

pAsterisk

*

pAttributeNames

UserId, GameTitle, TopScore, …

choice

pAttribute

Page 65: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute

Page 66: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

Page 67: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

let pQuery = tuple4 pSelect pAttribute pFrom pTableName |>> (fun (_, attributes, _, table) -> { Attributes = attributes Table = table })

Page 68: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

let pQuery = tuple4 pSelect pAttribute pFrom pTableName |>> (fun (_, attributes, _, table) -> { Attributes = attributes Table = table })

Page 69: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

let pQuery = tuple4 pSelect pAttribute pFrom pTableName |>> (fun (_, attributes, _, table) -> { Attributes = attributes Table = table })

Page 70: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

let pQuery = tuple4 pSelect pAttribute pFrom pTableName |>> (fun (_, attributes, _, table) -> { Attributes = attributes Table = table })

Query

Page 71: Tame Cloud Complex with F# powered DSLs

@theburningmonk

SELECT * FROM GameScorepAttribute pTableNamepFrompSelect

tuple4

pQuery

Page 72: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 73: Tame Cloud Complex with F# powered DSLs

@theburningmonk

< 50 lines of code

Page 74: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazing

F# + FParsec =

Page 75: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Recap

Page 76: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 77: Tame Cloud Complex with F# powered DSLs

@theburningmonk

select GameTitle, UserId, TopScore from GameScores where GameTitle = “Starship X” and TopScore >= 1000 order desc limit 3 with (NoConsistentRead, Index(GameTitleIndex, true))

Page 78: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazon DynamoDB

Amazon SimpleWorkflow

Amazon CloudWatch

CASE STUDY

Page 79: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 80: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Decision Worker

Page 81: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Decision WorkerPoll

Page 82: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Decision WorkerDecision

Task

Page 83: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Decision WorkerDecide

Page 84: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Activity Worker

Decision Worker

Page 85: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Activity Worker

Decision Worker

Poll

Page 86: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Activity Worker

Decision Worker

Activity Task

Page 87: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Activity Worker

Decision Worker

Complete

Page 88: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Workers can run from anywhere

Page 89: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 90: Tame Cloud Complex with F# powered DSLs

@theburningmonk

input = “Yan”

result = “Hello Yan!”

Start

Finish

Activity

Page 91: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 92: Tame Cloud Complex with F# powered DSLs

image by Ryan Hageman license : https://creativecommons.org/licenses/by-sa/2.0/

Page 93: Tame Cloud Complex with F# powered DSLs

SWF-based Application

Page 94: Tame Cloud Complex with F# powered DSLs

API

Page 95: Tame Cloud Complex with F# powered DSLs

Heartbeats

Error Handling

Polling

API

Page 96: Tame Cloud Complex with F# powered DSLs

Activity Worker

Decision Worker

Heartbeats

Error Handling

Polling

API

Page 97: Tame Cloud Complex with F# powered DSLs

Activity Worker

Decision Worker

Heartbeats

Error Handling

Polling

API

Boilerplate

Page 98: Tame Cloud Complex with F# powered DSLs

– Kris Jordan

“Good simplicity is less with leverage, not less with less. Good simplicity is complexity

disguised, not complexity denied.”

Page 99: Tame Cloud Complex with F# powered DSLs

http://bit.ly/1pOLeKl

Page 100: Tame Cloud Complex with F# powered DSLs
Page 101: Tame Cloud Complex with F# powered DSLs

Start

Finish

Activity?

Page 102: Tame Cloud Complex with F# powered DSLs

@theburningmonk

the workflow is implied by decision

worker logic…

Page 103: Tame Cloud Complex with F# powered DSLs

@theburningmonk

instead..

Page 104: Tame Cloud Complex with F# powered DSLs

@theburningmonk

the workflow should drive decision worker logic

Page 105: Tame Cloud Complex with F# powered DSLs

@theburningmonk

the workflow should driveautomate

decision worker logic

Page 106: Tame Cloud Complex with F# powered DSLs

Amazon.SimpleWorkflow.Extensions

github.com/fsprojects/Amazon.SimpleWorkflow.Extensions

Page 107: Tame Cloud Complex with F# powered DSLs

@theburningmonk

GOAL

Remove boilerplates

Page 108: Tame Cloud Complex with F# powered DSLs

@theburningmonk

GOAL

Code that matches the way you think

Page 109: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Start

Finish

Activity

Page 110: Tame Cloud Complex with F# powered DSLs
Page 111: Tame Cloud Complex with F# powered DSLs
Page 112: Tame Cloud Complex with F# powered DSLs

Start

Finish

Activity

Page 113: Tame Cloud Complex with F# powered DSLs
Page 114: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Workflows can be nested

Page 115: Tame Cloud Complex with F# powered DSLs
Page 116: Tame Cloud Complex with F# powered DSLs
Page 117: Tame Cloud Complex with F# powered DSLs

input

result

Page 118: Tame Cloud Complex with F# powered DSLs
Page 119: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Recap

Page 120: Tame Cloud Complex with F# powered DSLs

Activity Worker

Decision Worker

Heartbeats

Error Handling

Polling

API

Page 121: Tame Cloud Complex with F# powered DSLs
Page 122: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazon DynamoDB

Amazon SimpleWorkflow

Amazon CloudWatch

CASE STUDY

Page 123: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 124: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 125: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 126: Tame Cloud Complex with F# powered DSLs

@theburningmonk

wanna find correlations?

Page 127: Tame Cloud Complex with F# powered DSLs

@theburningmonk

wanna find correlations?

you can DIY it!

;-)

Page 128: Tame Cloud Complex with F# powered DSLs

@theburningmonk

“what latencies spiked at the same time as payment service?”

Page 129: Tame Cloud Complex with F# powered DSLs

Amazon.CloudWatch.Selector

github.com/fsprojects/Amazon.CloudWatch.Selector

Page 130: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Find metrics whose 5 min average exceeded

1 second during last 12 hours

Page 131: Tame Cloud Complex with F# powered DSLs

@theburningmonk

cloudWatch.Select( unitIs “milliseconds” + average (>) 1000.0 @ last 12 hours |> intervalOf 5 minutes)

Page 132: Tame Cloud Complex with F# powered DSLs

@theburningmonk

cloudWatch.Select(“ unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes”)

Page 133: Tame Cloud Complex with F# powered DSLs

@theburningmonk

“did any cache nodes’ CPU spike

yesterday?”

Page 134: Tame Cloud Complex with F# powered DSLs

@theburningmonk

cloudWatch.Select( namespaceLike “elasticache” + nameLike “cpu” + max (>) 80.0 @ last 24 hours |> intervalOf 15 minutes)

Page 135: Tame Cloud Complex with F# powered DSLs

@theburningmonk

cloudWatch.Select( namespaceLike “elasticache” + nameLike “cpu” + max (>) 80.0 @ last 24 hours |> intervalOf 15 minutes)

Regex

Page 136: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 137: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 138: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 139: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 140: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Filters

Page 141: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

TimeFrame

Page 142: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Period

Page 143: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type Query = { Filter : Filter TimeFrame : TimeFrame Period : Period option }

Page 144: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Query

Internal DSL

External DSL

Page 145: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 146: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type MetricTerm = Namespace | Name

type Filter = | MetricFilter of MetricTerm * (string -> bool)

Page 147: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 148: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type MetricTerm = Namespace | Name

type Unit = | Unit

type Filter = | MetricFilter of MetricTerm * (string -> bool) | UnitFilter of Unit * (string -> bool)

Page 149: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 150: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type MetricTerm = Namespace | Name

type Unit = | Unit

type StatsTerm = | Average | Min | Max | Sum | SampleCount

type Filter = | MetricFilter of MetricTerm * (string -> bool) | UnitFilter of Unit * (string -> bool) | StatsFilter of StatsTerm * (float -> bool)

Page 151: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 152: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type MetricTerm = Namespace | Name

type Unit = | Unit

type StatsTerm = | Average | Min | Max | Sum | SampleCount

type Filter = | MetricFilter of MetricTerm * (string -> bool) | UnitFilter of Unit * (string -> bool) | StatsFilter of StatsTerm * (float -> bool) | CompositeFilter of Filter * Filter

Page 153: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 154: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type TimeFrame = | Last of TimeSpan | Since of DateTime | Between of DateTime * DateTime

Page 155: Tame Cloud Complex with F# powered DSLs

@theburningmonk

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes

Page 156: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type Period = | Period of TimeSpan

Page 157: Tame Cloud Complex with F# powered DSLs

@theburningmonk

type Query = { Filter : Filter TimeFrame : TimeFrame Period : Period option }

Page 158: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Active Patternsa primer on

Page 159: Tame Cloud Complex with F# powered DSLs

@theburningmonk

allow patterns to be abstracted away into

named functions

Page 160: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Single-Case Patterns

Page 161: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|) input = match Double.TryParse input with | true, n -> n | _ -> failwithf “not a float [%s]” input

Page 162: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|) input = match Double.TryParse input with | true, n -> n | _ -> failwithf “not a float [%s]” input

Page 163: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|) input = match Double.TryParse input with | true, n -> n | _ -> failwithf “not a float [%s]” input

Float : string -> float

Page 164: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

Page 165: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|) input = match Double.TryParse input with | true, n -> n

match someString with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

Page 166: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|) input = match Double.TryParse input with | true, n -> n

match someString with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

Page 167: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

let (|Float|) input = match Double.TryParse input with | true, n -> n

Page 168: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

let (|Float|) input = match Double.TryParse input with | true, n -> n

Page 169: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match “42” with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

Page 170: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match “boo” with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x

Error!!!

Page 171: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Partial Patterns

Page 172: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|_|) input = match Double.TryParse input with | true, n -> Some n | _ -> None

Page 173: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Float|_|) input = match Double.TryParse input with | true, n -> Some n | _ -> None

Float : string -> float option

Page 174: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match “boo” with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x | _ -> “not a float”

Page 175: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match “boo” with | Float 42.0 -> “ftw” | Float 11.0 -> “palprime” | Float x -> sprintf “just %f” x | _ -> “not a float”

Page 176: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Multi-Case Patterns

Page 177: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Prime|NotPrime|NaN|) input = match Double.TryParse input with | true, n when isPrime n -> Prime n | true, n -> NotPrime n | _ -> NaN

Page 178: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Prime|NotPrime|NaN|) input = match Double.TryParse input with | true, n when isPrime n -> Prime n | true, n -> NotPrime n | _ -> NaN

Page 179: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Prime|NotPrime|NaN|) input = match Double.TryParse input with | true, n when isPrime n -> Prime n | true, n -> NotPrime n | _ -> NaN

Page 180: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Prime|NotPrime|NaN|) input = match Double.TryParse input with | true, n when isPrime n -> Prime n | true, n -> NotPrime n | _ -> NaN

Page 181: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|Prime|NotPrime|NaN|) input = match Double.TryParse input with | true, n when isPrime n -> Prime n | true, n -> NotPrime n | _ -> NaN

Page 182: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Prime n -> … | NotPrime n -> … | NaN -> …

Page 183: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Prime n & Float 11.0 -> … | Prime n -> … | Float 42.0 | Float 8.0 -> … | NotPrime n -> … | NaN -> …

Page 184: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Prime n & Float 11.0 -> … | Prime n -> … | Float 42.0 | Float 8.0 -> … | NotPrime n -> … | NaN -> …

Page 185: Tame Cloud Complex with F# powered DSLs

@theburningmonk

match someString with | Prime (IsPalindrome) -> … | Prime _ -> … | _ -> …

Page 186: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Tokenise(string -> string list)

Page 187: Tame Cloud Complex with F# powered DSLs

@theburningmonk

[ “namespaceIs”; “‘JustEat’”; “and”; “nameLike”; “‘cpu’”; “and”; … ]

Page 188: Tame Cloud Complex with F# powered DSLs

@theburningmonk

“namespaceIs” “and” …

F# List = LinkedList

“‘JustEat’”

Page 189: Tame Cloud Complex with F# powered DSLs

@theburningmonk

“namespaceIs” “and” …

F# List = LinkedList

“namespaceIs” “‘JustEat’” “and”:::: :: tail

cons (::) operator to prepend

“‘JustEat’”

Page 190: Tame Cloud Complex with F# powered DSLs

@theburningmonk

“namespaceIs” “and” …

F# List = LinkedList

“namespaceIs” “‘JustEat’” “and”:::: :: tailmatch aList with |

“‘JustEat’”

works as a pattern too!

Page 191: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|StringCI|_|) (str : string) (input : string) = if str.ToLower() = input.ToLower() then Some () else None

let (|Float|_|) input = match System.Double.TryParse input with | true, n -> Some n | _ -> None

Page 192: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|EmptyString|_|) input = if String.IsNullOrWhiteSpace input then Some () else None

let (|StartsWith|_|) char (input : string) = if input.[0] = char then Some () else None

let (|EndsWith|_|) char (input : string) = if input.[input.Length-1] = char then Some () else None

Page 193: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|QuotedString|_|) (input : string) = match input with | EmptyString -> None | str when str.Length < 2 -> None | StartsWith ''' & EndsWith ''' -> Some <| input.Substring(1, input.Length - 2) | _ -> None

Page 194: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|NamespaceIs|_|) = function | StringCI “NamespaceIs" :: QuotedString ns :: tl -> Some (eqFilter MetricFilter Namespace ns, tl) | _ -> None

Page 195: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|NamespaceIs|_|) = function | StringCI “NamespaceIs" :: QuotedString ns :: tl -> Some (eqFilter MetricFilter Namespace ns, tl) | _ -> None

namespaceIs ‘JustEat’ and nameLike ‘cpu’ and …

Page 196: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|NamespaceIs|_|) = function | StringCI “NamespaceIs" :: QuotedString ns :: tl -> Some (eqFilter MetricFilter Namespace ns, tl) | _ -> None

“namespaceIs” “‘JustEat’” :::: …

Page 197: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|NamespaceIs|_|) = function | StringCI “NamespaceIs" :: QuotedString ns :: tl -> Some (eqFilter MetricFilter Namespace ns, tl) | _ -> None

type MetricTerm = Namespace | Name

type Filter = | MetricFilter of MetricTerm * (string -> bool)

Page 198: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let (|NamespaceIs|_|) = function | StringCI “NamespaceIs" :: QuotedString ns :: tl -> Some (eqFilter MetricFilter Namespace ns, tl) | _ -> None

“and” :: “nameLike” :: “‘cpu’” :: “and” :: …

Page 199: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let rec loop acc = function | NamespaceIs (filter, tl) | NamespaceLike (filter, tl) | NameIs (filter, tl) | NameLike (filter, tl) | UnitIs (filter, tl) | Average (filter, tl) | Sum (filter, tl) | Min (filter, tl) | Max (filter, tl) | SampleCount (filter, tl) -> match tl with | And tl -> loop (filter::acc) tl | _ -> flatten (filter::acc), tl | _ -> failwith “No filters?!?!?”

Page 200: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let rec loop acc = function | NamespaceIs (filter, tl) | NamespaceLike (filter, tl) | NameIs (filter, tl) | NameLike (filter, tl) | UnitIs (filter, tl) | Average (filter, tl) | Sum (filter, tl) | Min (filter, tl) | Max (filter, tl) | SampleCount (filter, tl) -> match tl with | And tl -> loop (filter::acc) tl | _ -> flatten (filter::acc), tl | _ -> failwith “No filters?!?!?”

Page 201: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let rec loop acc = function | NamespaceIs (filter, tl) | NamespaceLike (filter, tl) | NameIs (filter, tl) | NameLike (filter, tl) | UnitIs (filter, tl) | Average (filter, tl) | Sum (filter, tl) | Min (filter, tl) | Max (filter, tl) | SampleCount (filter, tl) -> match tl with | And tl -> loop (filter::acc) tl | _ -> flatten (filter::acc), tl | _ -> failwith “No filters?!?!?”

Page 202: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let rec loop acc = function | NamespaceIs (filter, tl) | NamespaceLike (filter, tl) | NameIs (filter, tl) | NameLike (filter, tl) | UnitIs (filter, tl) | Average (filter, tl) | Sum (filter, tl) | Min (filter, tl) | Max (filter, tl) | SampleCount (filter, tl) -> match tl with | And tl -> loop (filter::acc) tl | _ -> flatten (filter::acc), tl | _ -> failwith “No filters?!?!?”

Page 203: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let rec loop acc = function | NamespaceIs (filter, tl) | NamespaceLike (filter, tl) | NameIs (filter, tl) | NameLike (filter, tl) | UnitIs (filter, tl) | Average (filter, tl) | Sum (filter, tl) | Min (filter, tl) | Max (filter, tl) | SampleCount (filter, tl) -> match tl with | And tl -> loop (filter::acc) tl | _ -> flatten (filter::acc), tl | _ -> failwith “No filters?!?!?”

Page 204: Tame Cloud Complex with F# powered DSLs

@theburningmonk

let parse (input : string) = input |> tokenize |> parseFilter |> parseTimeFrame |> parsePeriod

Page 205: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazing

F# =

Page 206: Tame Cloud Complex with F# powered DSLs

@theburningmonk

usable from anywhere you can run F# code

e.g. F# REPL, executable, ..

Internal DSL

Page 207: Tame Cloud Complex with F# powered DSLs

@theburningmonk

useful for building tools e.g. CLI, …

External DSL

Page 208: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 209: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 210: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 211: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Recap

Page 212: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 213: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Page 214: Tame Cloud Complex with F# powered DSLs

@theburningmonk

Amazon DynamoDB

Amazon SimpleWorkflow

Amazon CloudWatch

CASE STUDY

Page 215: Tame Cloud Complex with F# powered DSLs

@theburningmonktheburningmonk.comgithub.com/theburningmonk