DotNext 2019
The Power Of Composition
@ScottWlaschin
fsharpforfunandprofit.com
The Power Of Composition
1. The philosophy of composition
2. Ideas of functional programming
– Functions and how to compose them
– Types and how to compose them
3. Composition in practice
– Roman Numerals
– FizzBuzz gone carbonated
– Uh oh, monads!
– A web service
THE PHILOSOPHY OF
COMPOSITION
Prerequisites for
understanding composition
• You must have been a child at some point
• You must have played with Lego
• You must have played with toy trains
Lego
Philosophy
Lego Philosophy
1. All pieces are designed to be connected
2. The pieces are reusable in many contexts
3. Connect two pieces together and get
another "piece" that can still be connected
All pieces are designed to be connected
The pieces are reusable in different contexts
Connect two pieces together and
get another "piece" that can still be connected
Make big things from small things in the same way
Wooden Railway Track Philosophy
1. All pieces are designed to be connected
2. The pieces are reusable in many contexts
3. Connect two pieces together and get
another "piece" that can still be connected
All pieces are designed to be connected
The pieces are reusable in different contexts
Connect two pieces together and get
another "piece" that can still be connected
You can keep adding and adding.
Make big things from small things in the same way
If you understand Lego and wooden
railways, then you know
everything about composition!
THE IDEAS OF
FUNCTIONAL PROGRAMMING
Four ideas behind FP
Function
3.Types are not classes
1. Functions are things
2. Build bigger functions using
composition
4. Build bigger types using
composition
FP idea #1:
Functions are things
Function
The Tunnel of
Transformation
Function
apple -> banana
A function is a thing which
transforms inputs to outputs
A function is a standalone thing,
not attached to a class
A function is a standalone thing,
not attached to a class
It can be used for inputs and outputs
of other functions
A function can be an output thing
input
output
A function can be an input thing
input output
A function can be a parameter
input
output
input output
FP idea #2:
Build bigger functions
using composition
Function 1
apple -> banana
Function 2
banana -> cherry
>>
Function 1
apple -> banana
Function 2
banana -> cherry
New Function
apple -> cherry
Can't tell it was built
from smaller functions!
Where did the banana go?
Function composition in F# and C#
using the "piping" approach
int add1(int x) => x + 1;
int times2(int x) => x * 2;
int square(int x) => x * x;
add1(5); // = 6
times2(add1(5)); // = 12
square(times2(add1(5))); // = 144
Nested function calls
can be confusing if too deep
add15 6 times2 12 square 144
5 |> add1 // = 6
5 |> add1 |> times2 // = 12
5 |> add1 |> times2 |> square // = 144
add1 times2 square5 6 12 144
F# example
add1 times2 square5 6 12 144
5.Pipe(add1);
5.Pipe(add1).Pipe(times2);
5.Pipe(add1).Pipe(times2).Pipe(square);
C# example
Building big things from functions
It's compositions all the way up
Low-level operation
ToUpper
stringstring
Low-level operation
Service
AddressValidator
A “Service” is just like a microservice
but without the "micro" in front
Validation
Result
Address
Low-level operation Low-level operation
Service
Use-case
UpdateProfileData
ChangeProfile
Result
ChangeProfile
Request
Service Service
Use-case
Web application
Http
Response
Http
Request
Use-case Use-case
Http
Response
Http
Request
Even for complex applications,
data flows only in one direction
FP idea #3:
Types are not classes
So, what is a type then?
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
Set of
valid inputs
Set of
valid outputs
Function
1
2
3
4
5
6
This is type
"integer"
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
This is type
"string"
"abc"
"but"
"cobol"
"double"
"end"
"float"
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
This is type
"Person"
Donna Roy
Javier Mendoza
Nathan Logan
Shawna Ingram
Abel Ortiz
Lena Robbins
GordonWood
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
This is type
"Fruit"
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
This is a type of
Fruit->Fruit functions
A type is a just a name
for a set of things
FP idea #4:
Types can be composed too
Algebraic type system
Bigger types are built from smaller types by:
Composing with “AND”
Composing with “OR”
FruitSalad =
AND AND
Compose with “AND”
Compose with “AND”
enum AppleVariety { Red, Green }
enum BananaVariety { Yellow, Brown }
enum CherryVariety { Tart, Sweet }
struct FruitSalad
{
AppleVariety Apple;
BananaVariety Banana;
CherryVariety Cherry;
}
C# example
Apple AND
Banana AND
Cherry
Compose with “AND”
type AppleVariety = Red | Green
type BananaVariety = Yellow | Brown
type CherryVariety = Tart | Sweet
type FruitSalad = {
Apple: AppleVariety
Banana: BananaVariety
Cherry: CherryVariety
}
F# example
Snack = OR OR
Compose with “OR”
type Snack =
| Apple of AppleVariety
| Banana of BananaVariety
| Cherry of CherryVariety
A real world example
of composing types
Some requirements:
We accept three forms of payment:
Cash, Paypal, or CreditCard.
For Cash we don't need any extra information
For Paypal we need an email address
For Cards we need a card type and card number
interface IPaymentMethod
{..}
class Cash() : IPaymentMethod
{..}
class Paypal(string emailAddress): IPaymentMethod
{..}
class Card(string cardType, string cardNo) : IPaymentMethod
{..}
In OO design you would probably implement it as an
interface and a set of subclasses, like this:
type EmailAddress = string
type CardNumber = string
In F# you would probably implement by composing
types, like this:
type EmailAddress = ...
type CardNumber = …
type CardType = Visa | Mastercard
type CreditCardInfo = {
CardType : CardType
CardNumber : CardNumber
}
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD | RUB
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD | RUB
type Payment = {
Amount : PaymentAmount
Currency : Currency
Method : PaymentMethod }
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD | RUB
type Payment = {
Amount : PaymentAmount
Currency : Currency
Method : PaymentMethod }
Composable types can be used as
executable documentation
type Deal = Deck -> (Deck * Card)
type PickupCard = (Hand * Card) -> Hand
type Suit = Club | Diamond | Spade | Heart
type Rank = Two | Three | Four | Five | Six | Seven | Eight
| Nine | Ten | Jack | Queen | King | Ace
type Card = { Suit:Suit; Rank:Rank }
type Hand = Card list
type Deck = Card list
type Player = {Name:string; Hand:Hand}
type Game = { Deck:Deck; Players:Player list }
The domain on one screen!
type CardType = Visa | Mastercard
type CardNumber = string
type EmailAddress = string
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
| Bitcoin of BitcoinAddress
A big topic and not enough time  
More on DDD and designing with types at
fsharpforfunandprofit.com/ddd
Composition in practice:
Time for some real examples!
COMPOSITION WITH PIPING
(ROMAN NUMERALS)
Technique #1
To Roman Numerals
• Task: How to convert an arabic integer
to roman numerals?
• 5 => "V"
• 12 => "XII"
• 107 => "CVII"
To Roman Numerals
To Roman Numerals
• Use the "tally" approach
– Start with N copies of "I"
– Replace five "I"s with a "V"
– Replace two "V"s with a "X"
– Replace five "X"s with a "L"
– Replace two "L"s with a "C"
– etc
To Roman Numerals
number
etc
Replicate "I"
Replace_IIIII_V
Replace_VV_X
Replace_XXXXX_L
string ToRomanNumerals(int number)
{
// define a helper function for each step
string replace_IIIII_V(string s) =>
s.Replace("IIIII", "V");
string replace_VV_X(string s) =>
s.Replace("VV", "X");
string replace_XXXXX_L(string s) =>
s.Replace("XXXXX", "L");
string replace_LL_C(string s) =>
s.Replace("LL", "C");
// then combine them using piping
return new string('I', number)
.Pipe(replace_IIIII_V)
.Pipe(replace_VV_X)
.Pipe(replace_XXXXX_L)
.Pipe(replace_LL_C);
}
C# example
let toRomanNumerals number =
// define a helper function for each step
let replace_IIIII_V str =
replace "IIIII" "V" str
let replace_VV_X str =
replace "VV" "X" str
let replace_XXXXX_L str =
replace "XXXXX" "L" str
let replace_LL_C str =
replace "LL" "C" str
// then combine them using piping
String.replicate number "I"
|> replace_IIIII_V
|> replace_VV_X
|> replace_XXXXX_L
|> replace_LL_C
F# example
IT'S NOT ALWAYSTHIS
EASY…
function A function BCompose
function A function B
function A and B
Easy!
... But here is a challenge
function AInput Output
function B
Input 1 Output
Input 2
function AInput Output
function B
Input 1 Output
Input 2
Challenge #1: How can
we compose these?

COMPOSITIONWITH CURRYING
(ROMAN NUMERALS)
Technique #2
The Replace function
oldValue outputString
newValue
inputString
Replace
Uh-oh! Composition problem
Replace I / V Replace V / X Replace X / L 
Bad news:
Composition patterns
only work for functions that
have one parameter! 
Good news!
Every function can be turned into
a one parameter function 
Haskell Curry
We named this technique after him
Input A
Uncurried
Function
Input B
Output C
Curried
Function
Input A
Intermediate
Function
Output CInput B
What is currying?
after currying
Function as output
Input A
Uncurried
Function
Input B
Output C
Curried
Function
Input A
Intermediate
Function
Output CInput B
What is currying?
One input One input
Currying means that *every* function can be converted
to a series of one input functions
Replace
Before currying
input.Replace(oldValue, newValue);
string output
Replace
Old New
Old
New
After currying
Func<string,string> replace(string oldVal, string newVal) =>
input => input.Replace(oldVal, newVal);
Replace
Old New
Old
New
After currying
Func<string,string> replace(string oldVal, string newVal) =>
input => input.Replace(oldVal, newVal);
Func<string,string> replace(string oldVal, string newVal) =>
input => input.Replace(oldVal, newVal);
Replace
Old New
Old
New
After currying
This lambda (function) is returned
one-parameter
function
string ToRomanNumerals(int number)
{
// define a general helper function
Func<string,string> replace(
string oldValue, string newValue) =>
input => input.Replace(oldValue, newValue);
// then use piping
return new string('I', number)
.Pipe(replace("IIIII","V"))
.Pipe(replace("VV","X"))
.Pipe(replace("XXXXX","L"))
.Pipe(replace("LL","C"));
}
C# example
let toRomanNumerals number =
// no helper function needed.
// currying occurs automatically in F#
// combine using piping
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
F# example
let toRomanNumerals number =
// no helper function needed.
// currying occurs automatically in F#
// combine using piping
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
Only 2 of the 3
parameters are passed ?
Partial Application
Partial Application
let add x y = x + y
let multiply x y = x * y
5
|> add 2
|> multiply 3
Piping provides the missing argument
partial application
Partial Application
Replace ReplaceOldNew
Old New
Old
New
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
Only 2 parameters
passed in
Piping provides the missing argument
Pipelines
are extensible
let toRomanNumerals number =
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
Composable => extensible
// can easily add new segments to the pipeline
|> replace "VIIII" "IX"
|> replace "IIII" "IV"
|> replace "LXXXX" "XC"
functionInput Output
function
Input 1 Output
Input 2
Challenge #1: How can we compose these?

Here is another challenge
function AInput Output
function BInput
Output 1
Output 2
function AInput Output
function BInput
Output 1
Output 2
Challenge #2: How can we compose these?

COMPOSITION WITH BIND
(FIZZBUZZ)
Technique #3
FizzBuzz definition
• Write a program that prints the numbers
from 1 to 100
• But:
– For multiples of three print "Fizz" instead
– For multiples of five print "Buzz" instead
– For multiples of both three and five print
"FizzBuzz" instead.
let fizzBuzz max =
for n in [1..max] do
if (isDivisibleBy n 15) then
printfn "FizzBuzz"
else if (isDivisibleBy n 3) then
printfn "Fizz"
else if (isDivisibleBy n 5) then
printfn "Buzz"
else
printfn "%i" n
let isDivisibleBy n divisor =
(n % divisor) = 0 // helper function
A simple F# implementation
let fizzBuzz max =
for n in [1..max] do
if (isDivisibleBy n 15) then
printfn "FizzBuzz"
else if (isDivisibleBy n 3) then
printfn "Fizz"
else if (isDivisibleBy n 5) then
printfn "Buzz"
else
printfn "%i" n
let isDivisibleBy n divisor =
(n % divisor) = 0 // helper function
A simple F# implementation
Pipeline implementation
number Handle 15 case
Pipeline implementation
Handle 3 case
number Handle 15 case
Pipeline implementation
Handle 3 case
Handle 5 case
number Handle 15 case
Pipeline implementation
Handle 3 case
Handle 5 case
number
Answer
Handle 15 case
Last step
number Handle case
Carbonated
(e.g. "Fizz", "Buzz")
Uncarbonated
(e.g. 2, 7, 13)
Uncarbonated
Carbonated
Input ->
or
Uncarbonated
Carbonated
Input ->
type CarbonationResult =
| Uncarbonated of int // unprocessed
| Carbonated of string // "Fizz", Buzz", etc
Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html
or
type CarbonationResult =
| Uncarbonated of int // unprocessed
| Carbonated of string // "Fizz", Buzz", etc
let carbonate divisor label n =
if (isDivisibleBy n divisor) then
Carbonated label
else
Uncarbonated n
Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html
type CarbonationResult =
| Uncarbonated of int // unprocessed
| Carbonated of string // "Fizz", Buzz", etc
let carbonate divisor label n =
if (isDivisibleBy n divisor) then
Carbonated label
else
Uncarbonated n
12 |> carbonate 3 "Fizz" // Carbonated "Fizz"
10 |> carbonate 3 "Fizz" // Uncarbonated 10
10 |> carbonate 5 "Buzz" // Carbonated "Buzz"
carbonate 5 "Buzz"
let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
let result5 = n |> carbonate 5 "Buzz"
match result5 with
| Carbonated str ->
str
| Uncarbonated n ->
string n // convert to string
First implementation attempt
let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
let result5 = n |> carbonate 5 "Buzz"
match result5 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
// ...
// ...
let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
// ...
// ...
if Carbonated then
// return the string
if Uncarbonated then
// do something with the number
If Uncarbonated
If Carbonated
Bypass and
return the string
let ifUncarbonatedDo f result =
match result with
| Carbonated str ->
Carbonated str
| Uncarbonated n ->
f n
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
let lastStep result =
match result with
| Carbonated str ->
str
| Uncarbonated n ->
string(n) // still not fizzy, so
// convert to string
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> lastStep
Composable => easy to extend
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> ifUncarbonatedDo (carbonate 7 "Baz")
|> lastStep
Composable => easy to extend
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> ifUncarbonatedDo (carbonate 7 "Baz")
|> ifUncarbonatedDo (carbonate 11 "Pozz")
|> lastStep
Composable => easy to extend
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> ifUncarbonatedDo (carbonate 7 "Baz")
|> ifUncarbonatedDo (carbonate 11 "Pozz")
|> ifUncarbonatedDo (carbonate 13 "Tazz")
|> lastStep
Composable => easy to extend
Another example:
Chaining tasks
When task
completesWait Wait
a.k.a "promise", "future"
let taskExample input =
let taskX = startTask input
taskX.WhenFinished (fun x ->
let taskY = startAnotherTask x
taskY.WhenFinished (fun y ->
let taskZ = startThirdTask y
taskZ.WhenFinished (fun z ->
etc
let taskExample input =
let taskX = startTask input
taskX.WhenFinished (fun x ->
let taskY = startAnotherTask x
taskY.WhenFinished (fun y ->
let taskZ = startThirdTask y
taskZ.WhenFinished (fun z ->
do something
let taskExample input =
let taskX = startTask input
taskX.WhenFinished (fun x ->
let taskY = startAnotherTask x
taskY.WhenFinished (fun y ->
do something
let taskExample input =
let taskX = startTask input
taskX.WhenFinished (fun x ->
do something
let whenFinishedDo f task =
task.WhenFinished (fun taskResult ->
f taskResult)
let taskExample input =
startTask input
|> whenFinishedDo startAnotherTask
|> whenFinishedDo startThirdTask
|> whenFinishedDo ...
Parameterize the next step
MONADS!
Is there a general solution to
handling functions like this?
Yes! “Bind” is the answer!
Bind all the things!
How do we compose these?
>> >>
Composing one-track functions is fine...
>> >>
... and composing two-track functions is fine...
 
... but composing points/switches is not allowed!
Two-track input Two-track output
One-track input Two-track output


Two-track input Two-track output
Two-track input Two-track output
A function transformer
let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
FP terminology
• A monad is
– A data type
– With an associated "bind" function
– (and some other stuff)
• A monadic function is
– A switch/points function
– "bind" is used to compose them
type CarbonationResult =
| Uncarbonated of int
| Carbonated of string
function AInput Output
function BInput
Output 1
Output 2
Challenge #2: How can we compose these?

KLEISLI COMPOSITION
(WEB SERVICE)
Technique #4
=
compose
with
The result is the
same kind of thing
Kleisli Composition
Async<HttpContext option>HttpContext
A HttpHandler "WebPart"
Async<HttpContext option>HttpContext
A HttpHandler "WebPart"
=>=>
The result is another
HttpHandler so you can
keep adding and adding
Composition of HttpHandlers
Kleisli composition symbol
path "/hello"
Checks request path
(might fail)
matches path
doesn't match
OK "Hello"
Sets response
200 OK
path "/hello" >=> OK "Hello"
Checks request path
(might fail)
Sets response
>=>
A new WebPart
choose [
]
Picks first HttpHandler
that succeeds
choose [
path "/hello" >=> OK "Hello"
path "/goodbye" >=> OK "Goodbye"
]
Pick first path
that succeeds
GET
Only succeeds if
request is a GET
GET >=> choose [
path "/hello" >=> OK "Hello"
path "/goodbye" >=> OK "Goodbye"
]
let app = choose [
]
startWebServer defaultConfig app
A complete web app
GET >=> choose [
path "/hello" >=> OK "Hello"
path "/goodbye" >=> OK "Goodbye"
]
POST >=> choose [
path "/hello" >=> OK "Hello POST"
path "/goodbye" >=> OK "Goodbye POST"
]
Http
Response
Http
Request
No classes, no inheritance, one-directional data flow!
Review
• The philosophy of composition
– Connectable, reusable parts
• FP principles:
– Composable functions
– Composable types
Review
A taste of various composition techniques:
– Piping with "|>"
– Currying/partial application
– Composition using "bind" (monads!)
– Kleisli composition using ">=>"
Don't worry about understanding it all,
but hopefully it's not so scary now!
Why bother?
Benefits of composition:
• Reusable parts – no strings attached
• Testable – parts can be tested in isolation
• Understandable – data flows in one direction
• Maintainable – all dependencies are explicit
• Extendable – can add new parts without touching
old code
• A different way of thinking – it's good for
your brain to learn new things!
Slides and video here
fsharpforfunandprofit.com/composition
Thank you, DotNext!
@ScottWlaschin Me on twitter
My book Ask me anything
about railways!

The Power Of Composition (DotNext 2019)

  • 1.
    DotNext 2019 The PowerOf Composition @ScottWlaschin fsharpforfunandprofit.com
  • 2.
    The Power OfComposition 1. The philosophy of composition 2. Ideas of functional programming – Functions and how to compose them – Types and how to compose them 3. Composition in practice – Roman Numerals – FizzBuzz gone carbonated – Uh oh, monads! – A web service
  • 3.
  • 4.
    Prerequisites for understanding composition •You must have been a child at some point • You must have played with Lego • You must have played with toy trains
  • 5.
  • 6.
    Lego Philosophy 1. Allpieces are designed to be connected 2. The pieces are reusable in many contexts 3. Connect two pieces together and get another "piece" that can still be connected
  • 7.
    All pieces aredesigned to be connected
  • 8.
    The pieces arereusable in different contexts
  • 9.
    Connect two piecestogether and get another "piece" that can still be connected
  • 10.
    Make big thingsfrom small things in the same way
  • 12.
    Wooden Railway TrackPhilosophy 1. All pieces are designed to be connected 2. The pieces are reusable in many contexts 3. Connect two pieces together and get another "piece" that can still be connected
  • 13.
    All pieces aredesigned to be connected
  • 14.
    The pieces arereusable in different contexts
  • 15.
    Connect two piecestogether and get another "piece" that can still be connected You can keep adding and adding.
  • 16.
    Make big thingsfrom small things in the same way
  • 17.
    If you understandLego and wooden railways, then you know everything about composition!
  • 18.
  • 19.
    Four ideas behindFP Function 3.Types are not classes 1. Functions are things 2. Build bigger functions using composition 4. Build bigger types using composition
  • 20.
    FP idea #1: Functionsare things Function
  • 21.
    The Tunnel of Transformation Function apple-> banana A function is a thing which transforms inputs to outputs
  • 22.
    A function isa standalone thing, not attached to a class
  • 23.
    A function isa standalone thing, not attached to a class It can be used for inputs and outputs of other functions
  • 24.
    A function canbe an output thing input
  • 25.
    output A function canbe an input thing
  • 26.
    input output A functioncan be a parameter
  • 27.
  • 28.
    FP idea #2: Buildbigger functions using composition
  • 29.
    Function 1 apple ->banana Function 2 banana -> cherry
  • 30.
    >> Function 1 apple ->banana Function 2 banana -> cherry
  • 31.
    New Function apple ->cherry Can't tell it was built from smaller functions! Where did the banana go?
  • 32.
    Function composition inF# and C# using the "piping" approach
  • 33.
    int add1(int x)=> x + 1; int times2(int x) => x * 2; int square(int x) => x * x; add1(5); // = 6 times2(add1(5)); // = 12 square(times2(add1(5))); // = 144 Nested function calls can be confusing if too deep
  • 34.
    add15 6 times212 square 144
  • 35.
    5 |> add1// = 6 5 |> add1 |> times2 // = 12 5 |> add1 |> times2 |> square // = 144 add1 times2 square5 6 12 144 F# example
  • 36.
    add1 times2 square56 12 144 5.Pipe(add1); 5.Pipe(add1).Pipe(times2); 5.Pipe(add1).Pipe(times2).Pipe(square); C# example
  • 37.
    Building big thingsfrom functions It's compositions all the way up
  • 38.
  • 39.
    Low-level operation Service AddressValidator A “Service”is just like a microservice but without the "micro" in front Validation Result Address Low-level operation Low-level operation
  • 40.
  • 41.
  • 42.
    Http Response Http Request Even for complexapplications, data flows only in one direction
  • 45.
    FP idea #3: Typesare not classes
  • 46.
    So, what isa type then? A type is a just a name for a set of things Set of valid inputs Set of valid outputs Function
  • 47.
    Set of valid inputs Setof valid outputs Function 1 2 3 4 5 6 This is type "integer" A type is a just a name for a set of things
  • 48.
    Set of valid inputs Setof valid outputs Function This is type "string" "abc" "but" "cobol" "double" "end" "float" A type is a just a name for a set of things
  • 49.
    Set of valid inputs Setof valid outputs Function This is type "Person" Donna Roy Javier Mendoza Nathan Logan Shawna Ingram Abel Ortiz Lena Robbins GordonWood A type is a just a name for a set of things
  • 50.
    Set of valid inputs Setof valid outputs Function This is type "Fruit" A type is a just a name for a set of things
  • 51.
    Set of valid inputs Setof valid outputs Function This is a type of Fruit->Fruit functions A type is a just a name for a set of things
  • 52.
    FP idea #4: Typescan be composed too
  • 53.
  • 54.
    Bigger types arebuilt from smaller types by: Composing with “AND” Composing with “OR”
  • 55.
  • 56.
    Compose with “AND” enumAppleVariety { Red, Green } enum BananaVariety { Yellow, Brown } enum CherryVariety { Tart, Sweet } struct FruitSalad { AppleVariety Apple; BananaVariety Banana; CherryVariety Cherry; } C# example Apple AND Banana AND Cherry
  • 57.
    Compose with “AND” typeAppleVariety = Red | Green type BananaVariety = Yellow | Brown type CherryVariety = Tart | Sweet type FruitSalad = { Apple: AppleVariety Banana: BananaVariety Cherry: CherryVariety } F# example
  • 58.
    Snack = OROR Compose with “OR” type Snack = | Apple of AppleVariety | Banana of BananaVariety | Cherry of CherryVariety
  • 59.
    A real worldexample of composing types
  • 60.
    Some requirements: We acceptthree forms of payment: Cash, Paypal, or CreditCard. For Cash we don't need any extra information For Paypal we need an email address For Cards we need a card type and card number
  • 61.
    interface IPaymentMethod {..} class Cash(): IPaymentMethod {..} class Paypal(string emailAddress): IPaymentMethod {..} class Card(string cardType, string cardNo) : IPaymentMethod {..} In OO design you would probably implement it as an interface and a set of subclasses, like this:
  • 62.
    type EmailAddress =string type CardNumber = string In F# you would probably implement by composing types, like this:
  • 63.
    type EmailAddress =... type CardNumber = … type CardType = Visa | Mastercard type CreditCardInfo = { CardType : CardType CardNumber : CardNumber }
  • 64.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo
  • 65.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB
  • 66.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }
  • 67.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }
  • 69.
    Composable types canbe used as executable documentation
  • 70.
    type Deal =Deck -> (Deck * Card) type PickupCard = (Hand * Card) -> Hand type Suit = Club | Diamond | Spade | Heart type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace type Card = { Suit:Suit; Rank:Rank } type Hand = Card list type Deck = Card list type Player = {Name:string; Hand:Hand} type Game = { Deck:Deck; Players:Player list } The domain on one screen!
  • 71.
    type CardType =Visa | Mastercard type CardNumber = string type EmailAddress = string type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo | Bitcoin of BitcoinAddress
  • 72.
    A big topicand not enough time   More on DDD and designing with types at fsharpforfunandprofit.com/ddd
  • 73.
    Composition in practice: Timefor some real examples!
  • 74.
    COMPOSITION WITH PIPING (ROMANNUMERALS) Technique #1
  • 75.
    To Roman Numerals •Task: How to convert an arabic integer to roman numerals? • 5 => "V" • 12 => "XII" • 107 => "CVII"
  • 76.
  • 77.
    To Roman Numerals •Use the "tally" approach – Start with N copies of "I" – Replace five "I"s with a "V" – Replace two "V"s with a "X" – Replace five "X"s with a "L" – Replace two "L"s with a "C" – etc
  • 78.
    To Roman Numerals number etc Replicate"I" Replace_IIIII_V Replace_VV_X Replace_XXXXX_L
  • 79.
    string ToRomanNumerals(int number) { //define a helper function for each step string replace_IIIII_V(string s) => s.Replace("IIIII", "V"); string replace_VV_X(string s) => s.Replace("VV", "X"); string replace_XXXXX_L(string s) => s.Replace("XXXXX", "L"); string replace_LL_C(string s) => s.Replace("LL", "C"); // then combine them using piping return new string('I', number) .Pipe(replace_IIIII_V) .Pipe(replace_VV_X) .Pipe(replace_XXXXX_L) .Pipe(replace_LL_C); } C# example
  • 80.
    let toRomanNumerals number= // define a helper function for each step let replace_IIIII_V str = replace "IIIII" "V" str let replace_VV_X str = replace "VV" "X" str let replace_XXXXX_L str = replace "XXXXX" "L" str let replace_LL_C str = replace "LL" "C" str // then combine them using piping String.replicate number "I" |> replace_IIIII_V |> replace_VV_X |> replace_XXXXX_L |> replace_LL_C F# example
  • 81.
  • 82.
  • 83.
  • 84.
    function A andB Easy!
  • 85.
    ... But hereis a challenge
  • 86.
    function AInput Output functionB Input 1 Output Input 2
  • 87.
    function AInput Output functionB Input 1 Output Input 2 Challenge #1: How can we compose these? 
  • 88.
  • 89.
    The Replace function oldValueoutputString newValue inputString Replace
  • 90.
    Uh-oh! Composition problem ReplaceI / V Replace V / X Replace X / L 
  • 91.
    Bad news: Composition patterns onlywork for functions that have one parameter! 
  • 92.
    Good news! Every functioncan be turned into a one parameter function 
  • 93.
    Haskell Curry We namedthis technique after him
  • 94.
    Input A Uncurried Function Input B OutputC Curried Function Input A Intermediate Function Output CInput B What is currying? after currying Function as output
  • 95.
    Input A Uncurried Function Input B OutputC Curried Function Input A Intermediate Function Output CInput B What is currying? One input One input Currying means that *every* function can be converted to a series of one input functions
  • 96.
  • 97.
    Replace Old New Old New After currying Func<string,string>replace(string oldVal, string newVal) => input => input.Replace(oldVal, newVal);
  • 98.
    Replace Old New Old New After currying Func<string,string>replace(string oldVal, string newVal) => input => input.Replace(oldVal, newVal);
  • 99.
    Func<string,string> replace(string oldVal,string newVal) => input => input.Replace(oldVal, newVal); Replace Old New Old New After currying This lambda (function) is returned one-parameter function
  • 100.
    string ToRomanNumerals(int number) { //define a general helper function Func<string,string> replace( string oldValue, string newValue) => input => input.Replace(oldValue, newValue); // then use piping return new string('I', number) .Pipe(replace("IIIII","V")) .Pipe(replace("VV","X")) .Pipe(replace("XXXXX","L")) .Pipe(replace("LL","C")); } C# example
  • 101.
    let toRomanNumerals number= // no helper function needed. // currying occurs automatically in F# // combine using piping String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" F# example
  • 102.
    let toRomanNumerals number= // no helper function needed. // currying occurs automatically in F# // combine using piping String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Only 2 of the 3 parameters are passed ?
  • 103.
  • 104.
    Partial Application let addx y = x + y let multiply x y = x * y 5 |> add 2 |> multiply 3 Piping provides the missing argument partial application
  • 105.
    Partial Application Replace ReplaceOldNew OldNew Old New String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Only 2 parameters passed in Piping provides the missing argument
  • 106.
  • 107.
    let toRomanNumerals number= String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Composable => extensible // can easily add new segments to the pipeline |> replace "VIIII" "IX" |> replace "IIII" "IV" |> replace "LXXXX" "XC"
  • 108.
    functionInput Output function Input 1Output Input 2 Challenge #1: How can we compose these? 
  • 109.
    Here is anotherchallenge
  • 110.
    function AInput Output functionBInput Output 1 Output 2
  • 111.
    function AInput Output functionBInput Output 1 Output 2 Challenge #2: How can we compose these? 
  • 112.
  • 113.
    FizzBuzz definition • Writea program that prints the numbers from 1 to 100 • But: – For multiples of three print "Fizz" instead – For multiples of five print "Buzz" instead – For multiples of both three and five print "FizzBuzz" instead.
  • 114.
    let fizzBuzz max= for n in [1..max] do if (isDivisibleBy n 15) then printfn "FizzBuzz" else if (isDivisibleBy n 3) then printfn "Fizz" else if (isDivisibleBy n 5) then printfn "Buzz" else printfn "%i" n let isDivisibleBy n divisor = (n % divisor) = 0 // helper function A simple F# implementation
  • 115.
    let fizzBuzz max= for n in [1..max] do if (isDivisibleBy n 15) then printfn "FizzBuzz" else if (isDivisibleBy n 3) then printfn "Fizz" else if (isDivisibleBy n 5) then printfn "Buzz" else printfn "%i" n let isDivisibleBy n divisor = (n % divisor) = 0 // helper function A simple F# implementation
  • 116.
  • 117.
    Pipeline implementation Handle 3case number Handle 15 case
  • 118.
    Pipeline implementation Handle 3case Handle 5 case number Handle 15 case
  • 119.
    Pipeline implementation Handle 3case Handle 5 case number Answer Handle 15 case Last step
  • 120.
    number Handle case Carbonated (e.g."Fizz", "Buzz") Uncarbonated (e.g. 2, 7, 13)
  • 121.
  • 122.
    Uncarbonated Carbonated Input -> type CarbonationResult= | Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html or
  • 123.
    type CarbonationResult = |Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc let carbonate divisor label n = if (isDivisibleBy n divisor) then Carbonated label else Uncarbonated n Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html
  • 124.
    type CarbonationResult = |Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc let carbonate divisor label n = if (isDivisibleBy n divisor) then Carbonated label else Uncarbonated n
  • 125.
    12 |> carbonate3 "Fizz" // Carbonated "Fizz" 10 |> carbonate 3 "Fizz" // Uncarbonated 10 10 |> carbonate 5 "Buzz" // Carbonated "Buzz" carbonate 5 "Buzz"
  • 126.
    let fizzbuzz n= let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> let result3 = n |> carbonate 3 "Fizz" match result3 with | Carbonated str -> str | Uncarbonated n -> let result5 = n |> carbonate 5 "Buzz" match result5 with | Carbonated str -> str | Uncarbonated n -> string n // convert to string First implementation attempt
  • 127.
    let fizzbuzz n= let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> let result3 = n |> carbonate 3 "Fizz" match result3 with | Carbonated str -> str | Uncarbonated n -> let result5 = n |> carbonate 5 "Buzz" match result5 with | Carbonated str -> str | Uncarbonated n -> // do something with Uncarbonated value
  • 128.
    let fizzbuzz n= let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> let result3 = n |> carbonate 3 "Fizz" match result3 with | Carbonated str -> str | Uncarbonated n -> // do something with Uncarbonated value // ... // ...
  • 129.
    let fizzbuzz n= let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> // do something with Uncarbonated value // ... // ...
  • 130.
    if Carbonated then //return the string if Uncarbonated then // do something with the number
  • 131.
  • 132.
    let ifUncarbonatedDo fresult = match result with | Carbonated str -> Carbonated str | Uncarbonated n -> f n
  • 133.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  • 134.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  • 135.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  • 136.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  • 137.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep let lastStep result = match result with | Carbonated str -> str | Uncarbonated n -> string(n) // still not fizzy, so // convert to string
  • 138.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep Composable => easy to extend
  • 139.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> lastStep Composable => easy to extend
  • 140.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> ifUncarbonatedDo (carbonate 11 "Pozz") |> lastStep Composable => easy to extend
  • 141.
    let fizzbuzz n= n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> ifUncarbonatedDo (carbonate 11 "Pozz") |> ifUncarbonatedDo (carbonate 13 "Tazz") |> lastStep Composable => easy to extend
  • 142.
  • 143.
  • 144.
    let taskExample input= let taskX = startTask input taskX.WhenFinished (fun x -> let taskY = startAnotherTask x taskY.WhenFinished (fun y -> let taskZ = startThirdTask y taskZ.WhenFinished (fun z -> etc
  • 145.
    let taskExample input= let taskX = startTask input taskX.WhenFinished (fun x -> let taskY = startAnotherTask x taskY.WhenFinished (fun y -> let taskZ = startThirdTask y taskZ.WhenFinished (fun z -> do something
  • 146.
    let taskExample input= let taskX = startTask input taskX.WhenFinished (fun x -> let taskY = startAnotherTask x taskY.WhenFinished (fun y -> do something
  • 147.
    let taskExample input= let taskX = startTask input taskX.WhenFinished (fun x -> do something
  • 148.
    let whenFinishedDo ftask = task.WhenFinished (fun taskResult -> f taskResult) let taskExample input = startTask input |> whenFinishedDo startAnotherTask |> whenFinishedDo startThirdTask |> whenFinishedDo ... Parameterize the next step
  • 149.
  • 150.
    Is there ageneral solution to handling functions like this?
  • 151.
    Yes! “Bind” isthe answer! Bind all the things!
  • 152.
    How do wecompose these?
  • 154.
    >> >> Composing one-trackfunctions is fine...
  • 155.
    >> >> ... andcomposing two-track functions is fine...
  • 156.
      ... butcomposing points/switches is not allowed!
  • 157.
    Two-track input Two-trackoutput One-track input Two-track output  
  • 158.
  • 159.
    Two-track input Two-trackoutput A function transformer
  • 160.
    let bind nextFunctionresult = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  • 161.
    let bind nextFunctionresult = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  • 162.
    let bind nextFunctionresult = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  • 163.
    let bind nextFunctionresult = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  • 164.
    let bind nextFunctionresult = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  • 165.
    FP terminology • Amonad is – A data type – With an associated "bind" function – (and some other stuff) • A monadic function is – A switch/points function – "bind" is used to compose them type CarbonationResult = | Uncarbonated of int | Carbonated of string
  • 166.
    function AInput Output functionBInput Output 1 Output 2 Challenge #2: How can we compose these? 
  • 167.
  • 168.
    = compose with The result isthe same kind of thing Kleisli Composition
  • 169.
  • 170.
  • 171.
    =>=> The result isanother HttpHandler so you can keep adding and adding Composition of HttpHandlers Kleisli composition symbol
  • 172.
    path "/hello" Checks requestpath (might fail) matches path doesn't match
  • 173.
  • 174.
    path "/hello" >=>OK "Hello" Checks request path (might fail) Sets response >=> A new WebPart
  • 175.
    choose [ ] Picks firstHttpHandler that succeeds
  • 176.
    choose [ path "/hello">=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] Pick first path that succeeds
  • 177.
  • 178.
    GET >=> choose[ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ]
  • 179.
    let app =choose [ ] startWebServer defaultConfig app A complete web app GET >=> choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] POST >=> choose [ path "/hello" >=> OK "Hello POST" path "/goodbye" >=> OK "Goodbye POST" ]
  • 180.
    Http Response Http Request No classes, noinheritance, one-directional data flow!
  • 181.
    Review • The philosophyof composition – Connectable, reusable parts • FP principles: – Composable functions – Composable types
  • 182.
    Review A taste ofvarious composition techniques: – Piping with "|>" – Currying/partial application – Composition using "bind" (monads!) – Kleisli composition using ">=>" Don't worry about understanding it all, but hopefully it's not so scary now!
  • 183.
    Why bother? Benefits ofcomposition: • Reusable parts – no strings attached • Testable – parts can be tested in isolation • Understandable – data flows in one direction • Maintainable – all dependencies are explicit • Extendable – can add new parts without touching old code • A different way of thinking – it's good for your brain to learn new things!
  • 184.
    Slides and videohere fsharpforfunandprofit.com/composition Thank you, DotNext! @ScottWlaschin Me on twitter My book Ask me anything about railways!