val doStuff : (obj -> obj)
Full name: index.doStuff
val doStuff : f:('a -> 'b) -> g:('b -> 'c) -> a:'a -> 'c
Full name: index.doStuff
val f : ('a -> 'b)
val g : ('a -> 'b)
val a : 'a
val y : 'a
type Trade =
{Product: string;
Volume: float;}
static member Create : product:string * volume:float -> Trade
Full name: index.Trade
Trade.Product: string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Trade.Volume: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
static member Trade.Create : product:string * volume:float -> Trade
Full name: index.Trade.Create
val product : string
val volume : float
static member Trade.Create : product:string * volume:float -> Trade
val trade : Trade
Full name: index.trade
val b : 'b
type ProductRepository =
{Get: string -> obj;
Save: obj -> unit;}
Full name: index.ProductRepository
ProductRepository.Get: string -> obj
ProductRepository.Save: obj -> unit
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
type IProductRepository =
interface
abstract member Get : string -> 'a0
abstract member Save : 'a0 -> unit
end
Full name: index.IProductRepository
abstract member IProductRepository.Get : string -> 'a0
Full name: index.IProductRepository.Get
abstract member IProductRepository.Save : 'a0 -> unit
Full name: index.IProductRepository.Save
val saveTrade : trade:'a -> 'b
Full name: index.DataAccess.saveTrade
val trade : 'a
val doStuff : unit -> 'a
Full name: index.StatefulModule.doStuff
Multiple items
type StatefulObject =
new : myState:obj -> StatefulObject
Full name: index.StatefulObject
--------------------
new : myState:obj -> StatefulObject
val myState : obj
val mutable state : obj
val update : ('a -> 'b -> 'c -> 'd)
val eventHub : 'a
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
val f : 'a
Multiple items
type TradeService =
new : eventHub:obj -> TradeService
member UpdateTrade : trade:Trade * updateFunc:'a0 -> 'a1
Full name: index.TradeService
--------------------
new : eventHub:obj -> TradeService
val eventHub : obj
val x : TradeService
member TradeService.UpdateTrade : trade:Trade * updateFunc:'a0 -> 'a1
Full name: index.TradeService.UpdateTrade
val trade : Trade
val updateFunc : 'a
val newTrade : 'a
val raise : exn:System.Exception -> 'T
Full name: Microsoft.FSharp.Core.Operators.raise
val attemptReadMapProduct : row:'a -> 'b
Full name: index.attemptReadMapProduct
val row : 'a
module String
from Microsoft.FSharp.Core
val attemptReadMapProduct : row:'a -> obj
Full name: index.attemptReadMapProduct
val ma : obj
val prod : 'a
val foo : 'a
Full name: index.foo
val writeLine : 'a
Full name: index.writeLine
Easing F# Adoption
Not about idomatic code
Scaling the F# message
Enterprises aren't software houses
They do not aspire to be best in market with software,
but they do recongise the value of software.
Nobody got fired for hiring {insert-consultancy-here}
An outsourced enterprise
The Stack Overflow survey
But there is a bigger problem
C# Is really quiet good at this stuff
How do we deal with this?
Advertise F# features
- Pattern matching / Active Patters
- Type providers
- Async
- Immutability
- Discriminated Unions
- etc..
Use quantative evidence
Use Gateway drugs
- Build
- Dev ops scripts
- Generate Test data
Consider your environment
How can I apply F# with minimal fuss
Lower mismatch with C#
Composing with let
vs
1:
2:
3:
|
let doStuff f g a =
let y = f a
g y
|
Arrgh! Custom operators
1:
2:
3:
4:
5:
6:
7:
8:
|
type Trade =
{ Product:string; Volume:float }
static member Create (product,volume) =
{ Product = product; Volume = volume }
let inline (!!) = Trade.Create
let trade : Trade = !! ("power", 5000)
|
vs
1:
2:
3:
4:
5:
6:
|
type Trade =
{ Product:string; Volume:float }
static member Create (product,volume) =
{ Product = product; Volume = volume }
let trade = Trade.Create ("power", 5000)
|
Oh yeah! Member constraints
1:
2:
|
let inline (!!) (b : ^b) : ^a =
(^a : (static member Create : ^b -> ^a) (b))
|
Interfaces or Record of functions
1:
2:
3:
4:
|
type ProductRepository = {
Get : string -> Product
Save : Product -> unit
}
|
vs
1:
2:
3:
|
type IProductRepository =
abstract Get : string -> Product
abstract Save : Product -> unit
|
Write interop interfaces in C#
- Carefully consider how you expose FSharp.Core
- Consider using
[<CompiledName>]
attribute
- Try to limit sharing to value types.
Utilise the .NET ecosystem
- Giraffe and ASPNET Core
- Newtonsoft.Json
- PdfSharp
- etc..
Structuring modules
1:
2:
3:
4:
5:
6:
7:
|
module Trade =
let computeVolume trade = ...
module DataAccess =
let saveTrade trade = ...
|
vs
1:
2:
3:
4:
5:
|
module Trade =
let computeVolume trade = ...
let save trade = ...
|
As for the stateful ones
1:
2:
3:
4:
5:
|
module StatefulModule =
let private mutable state = 1
let doStuff() = ..
|
prefer objects
1:
2:
3:
4:
5:
|
type StatefulObject(myState) =
let mutable state = myState
member __.DoStuff() = ..
|
Consider dependencies
1:
2:
3:
|
module Trade =
let update (eventHub:IEventHub option) f trade = ...
|
vs
1:
2:
3:
4:
5:
6:
|
type TradeService(eventHub:IEventHub) =
member x.UpdateTrade(trade:Trade, updateFunc) =
let newTrade = Trade.update updateFunc trade
evntHub.raise (TradeUpdated(trade, newTrade))
newTrade
|
Select your abstractions wisely
These are all super powerful abstractions
Consider the next reader for the best result.
1:
2:
3:
4:
5:
6:
|
let attemptReadMapProduct row =
result {
let! marketArea = read<String> "marketarea" row
let! product = read "product" row
return! tryMapProduct marketArea product
}
|
vs
1:
2:
3:
4:
5:
6:
|
let attemptReadMapProduct row =
read<String> "marketarea" row
>>= (fun ma ->
read<String> "product" row
>>= (fun prod -> tryMapProduct ma prod)
)
|
But do you really need
1:
2:
|
let foo =
asyncReaderStateResult { ... }
|
or
1:
2:
|
let writeLine =
io { .. }
|
Probably not.
Limit to well known abstractions
- Maybe
- Result / Attempt
- Async
In summary
- If you can explain it in a way that scales use it
- If not introduce a stepping stone
- Rinse and repeat
Thanks for listening!!
Twitter: @colinbul