+ All Categories
Home > Technology > Антон Молдован "Type driven development with f#"

Антон Молдован "Type driven development with f#"

Date post: 05-Apr-2017
Category:
Upload: fwdays
View: 173 times
Download: 7 times
Share this document with a friend
56
TDD with F# (since 2003) Anton Moldovan (@antyadev) SBTech Twitter: https://twitter.com/antyaDev Github: https://github.com/antyaDev
Transcript
Page 1: Антон Молдован "Type driven development with f#"

TDD with F# (since 2003)

Anton Moldovan (@antyadev)

SBTech

Twitter: https://twitter.com/antyaDev

Github: https://github.com/antyaDev

Page 2: Антон Молдован "Type driven development with f#"

About me:@AntyaDev

like types*

Page 3: Антон Молдован "Type driven development with f#"
Page 4: Антон Молдован "Type driven development with f#"
Page 5: Антон Молдован "Type driven development with f#"

@ploeh

Page 6: Антон Молдован "Type driven development with f#"

@ploeh

Page 7: Антон Молдован "Type driven development with f#"

@ploeh

Page 8: Антон Молдован "Type driven development with f#"

@ploeh

Page 9: Антон Молдован "Type driven development with f#"

Free monads

Page 10: Антон Молдован "Type driven development with f#"
Page 11: Антон Молдован "Type driven development with f#"
Page 12: Антон Молдован "Type driven development with f#"

In OOP you by default thinking about

abstraction extensibility

Page 13: Антон Молдован "Type driven development with f#"

In FP you by default thinking about

purity composabilitycorrectness

Page 14: Антон Молдован "Type driven development with f#"

In FP you build your ideal, private worldwhere

you know everything

Page 15: Антон Молдован "Type driven development with f#"

Pure Domain

Page 16: Антон Молдован "Type driven development with f#"
Page 17: Антон Молдован "Type driven development with f#"

If you are coming from an object-oriented design background, one of the paradigm shifts involved in "thinking functionally" is to change how you think about types.

A well designed object-oriented program will have:• a strong focus on behavior rather than data, • will use a lot of polymorphism (interfaces),• will try to avoid having explicit knowledge of the actual concrete

classes being passed around.

A well designed functional program, on the other hand, will have a strong focus on data types rather than behavior

Page 18: Антон Молдован "Type driven development with f#"

type UserName = {firstName: stringlastName: string

}

type Shape =| Circle of int| Rectangle of int * int

Page 19: Антон Молдован "Type driven development with f#"

type UserName = {firstName: string;lastName: string

}

let a = { firstName = "a"; lastName = "b" }let b = { firstName = "a"; lastName = "b" }

if a = b then "equals" // true

Page 20: Антон Молдован "Type driven development with f#"

type Money = {amount: decimalcurrency: Currency

}

let a = { amount = 10; currency = USD }let b = { amount = 10; currency = USD }

if a = b then "equals" // true

Page 21: Антон Молдован "Type driven development with f#"

type Shape =| Rectangle = 0| Circle = 1| Prism = 2

Page 22: Антон Молдован "Type driven development with f#"

type Shape =| Rectangle of width:float * length:float| Circle of radius:float| Prism of width:float * float * height:float

let rectangle = Rectangle(width = 6.2, length = 5.5)

Page 23: Антон Молдован "Type driven development with f#"
Page 24: Антон Молдован "Type driven development with f#"

anyone can set this to ‘true’

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.

Rule 3: we have 5 services which works only for verified email and 5 services which works for invalid email.

class EmailContact{

public string EmailAddress { get; set; }public bool IsEmailVerified { get; set; }

}

Page 25: Антон Молдован "Type driven development with f#"

Rule 3: we have - 5 services which works only for verified email and - 5 services which works for invalid email.

if (emailContract.IsEmailVerified)

void SendEmailToApprove(EmailContact emailContract){

if (emailContract.IsEmailVerified)}

void SendEmailToReject(EmailContact emailContract){

if (emailContract.IsEmailVerified)}

void SendEmailToConfirm(EmailContact emailContract){

if (emailContract.IsEmailVerified)}

void SendEmailToLinkedin(EmailContact emailContract){

if (emailContract.IsEmailVerified)}

Page 26: Антон Молдован "Type driven development with f#"

type ValidEmail = { email: string }type InvalidEmail = { email: string }

type Email =| Valid of ValidEmail| Invalid of InvalidEmail

let sendEmailToLinkedin (email: ValidEmail) = ...

You need only one dispatch in one place

Page 27: Антон Молдован "Type driven development with f#"
Page 28: Антон Молдован "Type driven development with f#"

Page 29: Антон Молдован "Type driven development with f#"

public class NaiveShoppingCart<TItem>{

private List<TItem> items;private decimal paidAmount;

public NaiveShoppingCart(){

this.items = new List<TItem>();this.paidAmount = 0;

}

/// Is cart paid for?public bool IsPaidFor => this.paidAmount > 0;

public IEnumerable<TItem> Items => this.items;

public void AddItem(TItem item){

if (!this.IsPaidFor) this.items.Add(item); }

/// remove item only if not paid for

Page 30: Антон Молдован "Type driven development with f#"

if (!this.IsPaidFor) { do something }

Page 31: Антон Молдован "Type driven development with f#"

public class NaiveShoppingCart<TItem>{

private List<TItem> items;public bool IsPaidFor => this.paidAmount > 0;

public bool IsPaidFor => this.paidAmount > 0;public bool IsConfirmedByUser => _isConfirmedByUser;public bool IsApproved => IsPaidFor && _isValid;public bool IsCanceledByUser => _isCanceled && _user != null;public bool IsCanceledAuto => _isCanceled || _user == null && _system != null;public bool IsCanceledAdmin => _isCanceled || _user == null && _system == null && _admin != null;

public Status GetStatus(){

if (_isCanceled && items.Count > 0)return Status.Invalid;

else if (items.Count > 0)return Status.Active;

return Status.Empty;}

public void Approve(){

if (_isCanceled) throw new Exception();

if (items.Count > 0)}

what about recently added IsCanceledByAdmin?

Page 32: Антон Молдован "Type driven development with f#"
Page 33: Антон Молдован "Type driven development with f#"

Page 34: Антон Молдован "Type driven development with f#"

type CartItem = string // placeholder for a more complicated type

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal}

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

Page 35: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

// =============================// operations on empty state// =============================

let addToEmptyState (item: CartItem) : Cart.Active =Cart.Active { unpaidItems = [item] } // a new Active Cart

Page 36: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

// =============================// operation on empty state// =============================

let addToEmptyState item ={ unpaidItems = [item] } // returns a new Active Cart

Page 37: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

// =============================// operation on active state// =============================

let addToActiveState (state: ActiveState, itemToAdd: CartItem) =let newList = itemToAdd :: state.unpaidItemsCart.Active { state with unpaidItems = newList }

Page 38: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

// =============================// operation on active state// =============================

let addToActiveState state itemToAdd =let newList = itemToAdd :: state.unpaidItems{ state with unpaidItems = newList }

Page 39: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

let removeFromActiveState state itemToRemove =let newList = state.unpaidItems

|> List.filter (fun i -> i <> itemToRemove)

match newList with| [] -> Cart.Empty NoItems| _ -> Cart.Active { state with unpaidItems = newList }

Page 40: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal }

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

let payForActiveState state amount =Cart.PaidFor { paidItems = state.unpaidItems

payment = amount }

Page 41: Антон Молдован "Type driven development with f#"

type EmptyState = NoItems

type ActiveState = { unpaidItems: CartItem list; }

type PaidForState = { paidItems: CartItem list; payment: decimal}

type Cart =| Empty of EmptyState| Active of ActiveState| PaidFor of PaidForState

let item = “test_product”let activeState = addToEmptyState(item)let paidState = payForActiveState(activeState, 10)

// compile error, your state is not active anymorelet activeState = addToActiveState(paidState, item)

Page 42: Антон Молдован "Type driven development with f#"

errors

Page 43: Антон Молдован "Type driven development with f#"

let failingFunc num =let x = raise (new System.Exception("fail!"))try

let y = 42 + 5 + numx + y

withe -> 43

Page 44: Антон Молдован "Type driven development with f#"

/// Represents the result of a computationtype Result<'ok, 'msg> =

| Ok of 'ok * 'msg list| Fail of 'msg list

Page 45: Антон Молдован "Type driven development with f#"

type Request = { name: string; email: string }

let validateInput input =if input.name = ""

then Fail("Name must not be blank")elif input.email = ""

then Fail("Email must not be blank")else Ok(input)

Page 46: Антон Молдован "Type driven development with f#"

type Request = { name: string; email: string }

let validateInput input =if input.name = ""

then fail "Name must not be blank"elif input.email = ""

then fail "Email must not be blank"else ok input

Page 47: Антон Молдован "Type driven development with f#"

let validate1 input =if input.name = "" then fail "Name must not be blank“else ok input

let validate2 input =if input.name.Length > 50 then fail "Name must not be longer than 50 chars"else ok input

let validate3 input =if input.email = "" then fail "Email must not be blank"else ok input

let validRequest = validate1 >>= validate2 >>= validate3 >>= validate4

Page 48: Антон Молдован "Type driven development with f#"
Page 49: Антон Молдован "Type driven development with f#"

In functional programming we strive to write side-effect free applications. In other words, all the functions of the application should be pure. However, completely side-effect free applications are mostly useless, so the next best thing is to minimize the amount of side-effects, make them explicit and push them as close to the boundaries of the application as possible.

Page 50: Антон Молдован "Type driven development with f#"

Let’s see an example in invoicing domain. When changing a due date of an invoice we want to check that the new due date is in the future. We could implement it like this:

let changeDueDate (newDueDate:DateTime, invoice) =

if newDueDate > System.DateTime.Todaythen ok { invoice with dueDate = newDueDate }

else fail "Due date must be in future."

Page 51: Антон Молдован "Type driven development with f#"

let changeDueDate (newDueDate:DateTime,currentDate:DateTime, invoice) =

if newDueDate > currentDatethen ok { invoice with dueDate = newDueDate }

else fail "Due date must be in future."

Page 52: Антон Молдован "Type driven development with f#"

type PastDate = PastDate of DateTimetype CurrentDate = CurrentDate of DateTimetype FutureDate = FutureDate of DateTime

type Date =| Past of PastDate| Current of CurrentDate| Future of FutureDate

let changeDueDate (newDueDate:FutureDate, invoice) ={ invoice with DueDate = Date newDueDate }

Page 53: Антон Молдован "Type driven development with f#"

Problem: Language do not integrate information

- We need to bring information into the language…

Page 54: Антон Молдован "Type driven development with f#"
Page 55: Антон Молдован "Type driven development with f#"
Page 56: Антон Молдован "Type driven development with f#"

Recommended