f# - why, how, and for what?...• programming language compiling to .net • functional-first, oo...

43
F# - why, how, and for what? Rune Ibsen SimCorp 11-12-2018

Upload: others

Post on 13-Mar-2021

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F# - why, how, and for what?

Rune Ibsen – SimCorp – 11-12-2018

Page 2: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,
Page 3: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

• Programming language compiling to .NET

• Functional-first, OO second

• Strongly typed

• Source code on GitHub, Don Syme,

Microsoft Research, is BDFL

• Visual Studio, VSCode, Windows, Mac

F#

Story

Page 4: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Micro services

for music

streaming

REST APIs in

ASP.NET, C#

100.000+

simultaneous users

10 services

6 developers

No testers

10.000 unit tests

Continuous

delivery

MålHow we got

started…

Story

Page 5: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

• Code must be maintainable and evolvable for 10+

years

• Code must be continuously deployable

• Code must be of high quality

MålGoals

Story

Page 6: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Code

satisfaction

Story

Time

Satisfaction

Page 7: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Primitive

Obsessions

Primitive Obsession is using primitive data types to

represent domain ideas. For example, we use a String to

represent a message, an Integer to represent an amount of

money, or a Struct/Dictionary/Hash to represent a specific

object.

- Ward Cunningham

Page 8: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Primitive

Obsessions

private bool CompleteTransaction(int businessTransactionId,int mongoDbTransactionId,int sqlDbTransactionId, string user)

{//...;

}

Page 9: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

public class TransactionId{

public int Value { get; set; }}

Primitive

Obsessions

Page 10: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

public class TransactionId{

public TransactionId(int value){

this.Value = value;}

public int Value { get; }}

Primitive

Obsessions

Page 11: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

public class TransactionId{

public TransactionId(int value){

this.Value = value;}public int Value { get; }

}

[Fact]public void ComparisonIsSane(){

TransactionId id1 = new TransactionId(123);TransactionId id2 = new TransactionId(123);

Assert.True(t1.Equals(t2)); //FailsAssert.True(t1 == t2); //FailsAssert.True(t1.Value == t2.Value); //Fails

}

Primitive

Obsessions

Page 12: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

public class TransactionId{

public TransactionId(int value){

this.Value = value;}

public int Value { get; }

public override bool Equals(object obj){

var other = obj as TransactionId;

if (other == null) { return false; }

return this.Value == other.Value;}

}

Primitive

Obsessions

Page 13: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

public class TransactionId{

public TransactionId(int value){

this.Value = value;}public int Value { get; }public override bool Equals(object obj){

var other = obj as TransactionId;if (other == null) { return false; }return this.Value == other.Value;

}

public override int GetHashCode() => this.Value.GetHashCode();public static bool operator ==(TransactionId t1, TransactionId t2) =>

t1.Equals(t2);public static bool operator !=(TransactionId t1, TransactionId t2) =>

!t1.Equals(t2);}

Primitive

Obsessions

Page 14: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

C#”C# is a very nice language – it is just that all the defaults

are wrong”

- Don Syme, creator of F#

Page 15: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

I call it my billion-dollar mistake. [...] But I couldn't resist the

temptation to put in a null reference, simply because it was so

easy to implement. This has led to innumerable errors,

vulnerabilities, and system crashes, which have probably

caused a billion dollars of pain and damage in the last forty

years.

- Tony Hoare

Page 16: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Målnull

• null is the absence of a value

• null is not visible in the type signature

• When is null a valid value?

• Nullable<T> is only for value types

Maybe<T>

Page 17: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

public struct Maybe<T>{

private readonly T _value;public Maybe(T value){

if(value == null){

HasValue = false;_value = default(T);

}else{

_value = value;HasValue = true;

}}public static Maybe<T> Empty() => new Maybe<T>();public bool HasValue { get; }public T Value =>

HasValue? _value: throw new Exception("Cannot access Value when Maybe is empty.");

}

Page 18: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

Maybe<TransactionId> ParseTransactionId(string s);Transaction GetTransaction(TransactionId id);

Maybe<TransactionId> transactionId = ParseTransactionId(s);if (transactionId.HasValue){

Transaction transaction = GetTransaction(transactionId.Value);

/* Do stuff with transaction */}

Page 19: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

public static Maybe<R> Map<T, R>(this Maybe<T> @this, Func<T, R> mapper) =>

@this.HasValue? new Maybe<R>(mapper(@this.Value)): Maybe<R>.Empty();

Maybe<Transaction> transaction = ParseTransactionId(s).Map(id => GetTransaction(id));

Page 20: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

Maybe<TransactionId> ParseTransactionId(string s);Maybe<Transaction> GetTransaction(TransactionId id);

Maybe<TransactionId> transactionId = ParseTransactionId(s);if (transactionId.HasValue){

Maybe<Transaction> maybeTransaction = GetTransaction(transactionId.Value);if (maybeTransaction.HasValue){

Transaction transaction = maybeTransaction.Value;

/* Do stuff with transaction */}

}

Page 21: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

Maybe<Maybe<Transaction>> transaction = ParseTransactionId(s).Map(id => GetTransaction(id));

Page 22: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

public static Maybe<R> Bind<T, R>(this Maybe<T> @this, Func<T, Maybe<R>> binder) =>

@this.HasValue? binder(@this.Value): Maybe<R>.Empty();

Maybe<Transaction> transaction = ParseTransactionId(s).Bind(id => GetTransaction(id));

Page 23: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

Maybe<Order> order =ParseTransactionId(s).Bind(GetTransaction).Map(transaction => transaction.OrderId).Bind(GetOrder);

Page 24: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

null

Maybe<Order> order =from id in ParseTransactionId(s)from transaction in GetTransaction(id)from o in GetOrder(transaction.OrderId)select o;

Page 25: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

ResultExceptions are not visible in method signatures

Errors can be genuine business scenarios

Result<TSuccess,TFailure>

Page 26: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Result

public struct Result<TSuccess,TFailure>{

public bool IsSuccessful { get; }

public TSuccess Success =>this.IsSuccessful? _successValue: throw new Exception("Cannot access Success on failed Result.");

public TFailure Failure =>this.IsSuccessful? throw new Exception("Cannot access Failure on successful Result."): _failureValue;

}

Implementation details

Page 27: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Result

Result<Order, string> order =ParseTransactionId(s).Bind(GetTransaction).Map(transaction => transaction.OrderId).Bind(GetOrder);

Result<Order, string> order =from id in ParseTransactionId(s)from transaction in GetTransaction(id)from o in GetOrder(transaction.OrderId)select o;

Page 28: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Code

satisfaction

Time

Satisfaction

Page 29: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Result

Page 30: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Code

satisfaction

Time

Satisfaction

Page 31: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

type TransactionId = TransactionId of int

Page 32: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

type TransactionId = TransactionId of int

let t1 = TransactionId(123)let t2 = TransactionId(123)

t1 = t2 //true

Page 33: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

type TransactionId = TransactionId of inttype OrderId = OrderId of inttype Name = Name of stringtype CardNumber = CardNumber of stringtype Amount = Amount of decimaltype PaymentMethod =

| CreditCard of name : Name * number : CardNumber * expiry : DateTime| DebitCard of name : Name * number : CardNumber * expiry : DateTime| Cash of Amount

type CatalogNumber = CatalogNumber of stringtype ISRC = ISRC of stringtype LMID = LMID of stringtype Product =

| Album of label : string * catalogNumber : CatalogNumber| Track of ISRC| Video of LMID

Page 34: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

let processPayment(paymentMethod, product) =match product, paymentMethod with| Album(label, catalogNumber), CreditCard(_)| Album(label, catalogNumber), DebitCard(_) -> Ok (TransactionId 123)| Album(_), Cash(_) -> Error("Cannot pay for an album with cash")| Track(isrc), CreditCard(_, number, _) -> Ok (TransactionId 321)| Track(isrc), DebitCard(_) -> Ok (TransactionId 789)| Track(isrc), Cash(amount) -> Ok (TransactionId 987)

Incomplete pattern matches on this expression.

For example, the value '(Video(_),_)' may indicate a case not covered by the pattern(s)

Page 35: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

let processPayment(paymentMethod, product) =match product, paymentMethod with| Album(label, catalogNumber), CreditCard(_)| Album(label, catalogNumber), DebitCard(_) -> Ok (TransactionId 123)| Album(_), Cash(_) -> Error("Cannot pay for an album with cash")| Track(isrc), CreditCard(_, number, _) -> Ok (TransactionId 321)| Track(isrc), DebitCard(_) -> Ok (TransactionId 789)| Track(isrc), Cash(amount) -> Ok (TransactionId 987)

let myCreditCard = CreditCard(Name("Rune Ibsen“), CardNumber("12345678"), DateTime(1, 1, 2020))

let backInBlack = Album("Epic", CatalogNumber("5099751076520"))

let result = processPayment(myCreditCard, backInBlack)

let transactionId =match result with| Ok(transactionId) -> transactionId| Error(msg) -> failwith msg

Page 36: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F# / C#

var myCreditCard =PaymentMethod.NewCreditCard(

Name.NewName("Rune Ibsen"),CardNumber.NewCardNumber("12345678"),new DateTime(1, 1, 2020));

var backInBlack = Product.NewAlbum("Epic", CatalogNumber.NewCatalogNumber("5099751076520"));

var result = processPayment(myCreditCard, backInBlack);

if (result.IsOk){

int transactionId = result.ResultValue.Item;/* Do stuff */

}else{

throw new Exception(result.ErrorValue);}

Page 37: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

type Maybe<'T> = | Some of 'T| None

val parseTransactionId : string -> Maybe<TransactionId>val getTransaction : TransactionId -> Maybe<Transaction>val getOrder : OrderId -> Maybe<Order>

let tryGetOrder (s : string) : Maybe<Order> =maybe {

let! transactionId = parseTransactionId(s)let! transaction = getTransaction(transactionId)let orderId = transaction.OrderIdlet! order = getOrder(orderId)return order

}

Page 38: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

type Result<'TSuccess, 'TFailure> = | Success of 'TSuccess| Failure of 'TFailure

val parseTransactionId : string -> Result<TransactionId, string>val getTransaction : TransactionId -> Result<Transaction, string>val getOrder : OrderId -> Result<Order, string>

let tryGetOrder (s : string) : Result<Order, string> =result {

let! transactionId = parseTransactionId(s)let! transaction = getTransaction(transactionId)let orderId = transaction.OrderIdlet! order = getOrder(orderId)return order

}

Page 39: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

F#

val parseTransactionId : string -> Async<Result<TransactionId, string>>val getTransaction : TransactionId -> Async<Result<Transaction, string>>val getOrder : OrderId -> Async<Result<Order, string>>

let tryGetOrder (s : string) : Async<Result<Order, string>> =asyncResult {

let! transactionId = parseTransactionId(s)let! transaction = getTransaction(transactionId)let orderId = transaction.OrderIdlet! order = getOrder(orderId)return order

}

Page 40: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Code

satisfaction

Time

Satisfaction

Page 41: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Getting

started

• Continuous transition from C# to F#

• Start with one F# project in a larger solution

• Critical mass of enthusiastic developers

• Peer review proces

Page 42: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Conclusion”C# is a very nice language – it is just that all the defaults

are wrong”

- Don Syme, creator of F#

Page 43: F# - why, how, and for what?...• Programming language compiling to .NET • Functional-first, OO second • Strongly typed • Source code on GitHub, Don Syme, Microsoft Research,

Thank you

• F# can be adopted gradually

• F# is well suited for modelling

• F# makes certain prevalent concepts explicit

• Make the world a better place

• copenhagensoftware.com/simcorp@cphsoft