Slide 1

Slide 1 text

ML 21: Experience Report: Domain Modeling with F# @ScottWlaschin fsharpforfunandprofit.com

Slide 2

Slide 2 text

Languages with Mathematical focus Languages with Business focus Proof assistants FP languages COBOL Java/C# F#

Slide 3

Slide 3 text

Business-focused development • Understanding the domain is critical – “Correctness” is a lesser goal • Responding to feedback – Need to adjust domain model quickly and safely

Slide 4

Slide 4 text

F# for business-focused development • Readable by non-developers – And easy to collaborate with them • Composable types for domain modeling – And type checking makes it safe to refactor • Eliminating errors through design – “Make illegal states unrepresentable”

Slide 5

Slide 5 text

Collaborating with non-developers (Domain-driven design)

Slide 6

Slide 6 text

 Waterfall methodology

Slide 7

Slide 7 text

Agile methodology

Slide 8

Slide 8 text

Agile methodology

Slide 9

Slide 9 text

Domain-Driven Design

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Can you really make code represent the domain?

Slide 13

Slide 13 text

What non-developers think source code looks like

Slide 14

Slide 14 text

Shared language What DDD source code should look like module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand

Slide 15

Slide 15 text

module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand

Slide 16

Slide 16 text

module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand Non-programmers can provide useful feedback

Slide 17

Slide 17 text

Rapid feedback during the design stage

Slide 18

Slide 18 text

Creating the domain model is an interactive process Not just for developers. Everyone participates

Slide 19

Slide 19 text

... type Deck = Card list type Deal = Deck –› (Deck * Card)

Slide 20

Slide 20 text

... type Deck = Card list type Deal = ShuffledDeck –› (ShuffledDeck * Card)

Slide 21

Slide 21 text

... type Deck = Card list type Deal = ShuffledDeck –› (ShuffledDeck * Card) type ShuffledDeck = Card list

Slide 22

Slide 22 text

... type Deck = Card list type Deal = ShuffledDeck –› (ShuffledDeck * Card) type ShuffledDeck = Card list type Shuffle = Deck –› ShuffledDeck

Slide 23

Slide 23 text

... type Deck = Card list type Deal = ShuffledDeck –› (ShuffledDeck * Card) type ShuffledDeck = Card list type Shuffle = Deck –› ShuffledDeck

Slide 24

Slide 24 text

Domain modeling with an algebraic type system

Slide 25

Slide 25 text

I explain that new types are built from smaller types by: • Composing with AND • Composing with OR (“choices”)

Slide 26

Slide 26 text

Example of requirements: We accept three forms of payment: Cash, PayPal, or Card. For Cash we don't need any extra information For PayPal we need a email address For Cards we need a card type and card number

Slide 27

Slide 27 text

type EmailAddress = string type CardNumber = string Implement by composing types, like this:

Slide 28

Slide 28 text

type EmailAddress = ... type CardNumber = … type CardType = Visa | Mastercard type CreditCardInfo = { CardType : CardType CardNumber : CardNumber }

Slide 29

Slide 29 text

type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo

Slide 30

Slide 30 text

type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo

Slide 31

Slide 31 text

type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD

Slide 32

Slide 32 text

type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }

Slide 33

Slide 33 text

type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }

Slide 34

Slide 34 text

Eliminating errors through design “Make illegal states unrepresentable”

Slide 35

Slide 35 text

Business Rules: • Rule 1: If the email is changed, the verified flag must be reset to false. • Rule 2: The verified flag can only be set by a special verification service type EmailContactInfo = { EmailAddress : string IsEmailVerified : bool }

Slide 36

Slide 36 text

type EmailAddress = EmailAddress of string // with associated smart constructor type VerifiedEmail = VerifiedEmail of EmailAddress type VerificationService = (EmailAddress * VerificationHash) –› VerifiedEmail option type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmail Here’s the refactored model

Slide 37

Slide 37 text

type EmailAddress = EmailAddress of string // with associated smart constructor type VerifiedEmail = VerifiedEmail of EmailAddress type VerificationService = (EmailAddress * VerificationHash) –› VerifiedEmail option type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmail

Slide 38

Slide 38 text

type EmailAddress = EmailAddress of string // with associated smart constructor type VerifiedEmail = VerifiedEmail of EmailAddress type VerificationService = (EmailAddress * VerificationHash) –› VerifiedEmail option type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmail

Slide 39

Slide 39 text

type EmailAddress = EmailAddress of string // with associated smart constructor type VerifiedEmail = VerifiedEmail of EmailAddress type VerificationService = (EmailAddress * VerificationHash) –› VerifiedEmail option type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmail Those business rules are automatically enforced by the design!

Slide 40

Slide 40 text

Nice-to-have language improvements • Zero-cost wrappers (e.g. newtype) • An easier way to do “smart constructors” that perform validation • An easier way to enforce constraints (refinement types?) • A more fine-grained way of controlling access to constructors (e.g. the VerifiedEmail constructor)

Slide 41

Slide 41 text

Summary • Algebraic types are excellent for business-focused domain modeling • Non-developers can read and contribute • Lots of potential for ML languages here! Thanks!