8. Some new things to learn…
recursion
pure functions
immutable data
partial application
let rec qsort = function
| [] -> []
| hd :: tl ->
let lesser, greater = List.partition ((>=) hd) tl
List.concat [qsort lesser; [hd]; qsort greater]
pattern matching
higher-order functions
type inference
generics by default
'a list -> 'a list when 'a : comparison
9. let rec qsort = function
| [] -> []
| hd :: tl ->
let lesser, greater = List.partition ((>=) hd) tl
List.concat [qsort lesser; [hd]; qsort greater]
gotcha!
10. Some real-world concerns…
•
Good for demos but what about large
programs?
•
Good for academics but what about us?
•
Elegant code but what about performance?
•
Does it work with legacy software?
•
Where do I find functional programmers?
12. Bespoke Enterprise Applications
for the Energy Sector
•
lots of data
•
forecasts
•
•
•
metered data
market data
lots of types
•
units of measure
•
lots of computations
•
schedules
•
contracts
•
analysis
rates
•
•
station parameters
… all changing over time
20. Real-world OO
•
Struggles to be elegant
•
top down designs
•
•
high ceremony
•
•
coarse abstractions
data and behaviour tightly coupled
Mutating state is a powerful and dangerous
technique
•
hard to reason about
•
requires synchronised access
Lots of accidental complexity
•
abstraction event horizon
•
•
•
ORMs, IoCs, Mocks, Design Patterns,
UML …
Hard to find developers who have mastered
all of this
22. Not-Only SQL
•
most applications do not require the
flexibility a relational schema affords
•
•
applications are written in terms of
aggregates not relational schemas
•
•
as-of
store inputs and outputs
•
avoid accidental
complexity: ORM,
normal form
persist aggregates
making aggregates immutable affords
•
•
separate reporting concerns from
application concerns
what-if, easy test and debug
fits well with
functional
programs
23. JSON Documents
Input
• RunID
• Contract Parameters
• Asset Parameters
• Dynamic Parameters
• Market Parameters
JobRequest
• RunID
• Contract
• Interval
Pure
Function
Contract
Evaluation
API
Scheduler
Contract
Evaluation
Job API
Output
• RunID
• Revenue
• Additional Information
Document
Store
“Pure I/0”
Input
Output
25. Adoption: F#
•
Low risk
•
Runs on CLR and mono
•
Open source
•
Inter-op with legacy software and libraries
•
Back-out to C#
26. Adoption: Developers
•
Self taught
•
Hire good .NET developers, not language x
developers
•
.NET developer cutting F# production code in
a week
•
Functional programmer in a month
29. Self-host Web API
let config = new HttpSelfHostConfiguration(baseAddress)
config.MapHttpAttributeRoutes()
config.Formatters.JsonFormatter.SerializerSettings <-
JsonSerializerSettings(
PreserveReferencesHandling = PreserveReferencesHandling.None,
Converters =
[|
Json.TupleConverter()
Json.OptionConverter()
Json.ArrayConverter()
Json.ListConverter()
Json.MapTypeConverter()
Json.UnionTypeConverter()
|])
config.DependencyResolver <- new UnityResolver(container)
F# type JSON
converters
30. Topshelf Windows Service
F# working with an
existing
OO framework
HostFactory.Run(fun hc ->
hc.UseLog4Net("log4net.config")
hc.SetServiceName("Job.Api.Host")
hc.SetDisplayName("E.ON Ancillary Services Job API Host")
hc.SetDescription("An API service for Ancillary Services Jobs.")
hc.RunAsNetworkService() |> ignore
hc.Service<ApiService>(fun (s: ServiceConfigurator<ApiService>) ->
s.ConstructUsing(fun (name: string) -> new ApiService(config)) |> ignore
s.WhenStarted(fun (svc: ApiService) ->
jobRequestQueue.Start()
svc.Start()) |> ignore
s.WhenStopped(fun (svc: ApiService) ->
svc.Stop()
jobRequestQueue.Stop())
|> ignore)
|> ignore)
31. Web API Service
an F# class!!!
type ApiService(config: HttpSelfHostConfiguration) =
!
member val Server =
new HttpSelfHostServer(config) with get, set
!
member this.Start() =
this.Server.OpenAsync().Wait()
member this.Stop() =
if this.Server <> null then
this.Server.CloseAsync().Wait()
this.Server.Dispose()
32. Web API Controller
another F# class!!!
type JobController(log: ILog, jobRequestQueue: JobRequestQueue)
inherit ApiController()
!
[<Route("job/ping")>]
member x.Get() =
log.Debug("ping!!!")
"pong"
[<Route("job")>]
member x.Post(request:JobRequest) =
jobRequestQueue.Add(request)
33. agents: the safe way to manage state
Job Queue
let requests = BlockingQueueAgent<JobRequest>(config.JobRequestQueueLength)
!
let workerName (i: int) = String.Format("worker[{0}]", i)
!
let worker (workerName: string) =
async {
while true do
log.DebugFormat("{0} free", workerName)
let! request = requests.AsyncGet()
log.DebugFormat("{0} busy: job {1}", workerName, request.JobId)
run request
}
async, efficient use of threads
!
for i in 1 .. config.JobRequestWorkers do
Async.Start(workerName i |> worker, CancellationToken.Token)
!
requests.Add(request)
scale workers
github.com/fsprojects/fsharpx/blob/master/src/FSharpx.Core/Agents/
BlockingQueueAgent.fs
34. Execute Job
composition of async computations
async {
let! input = buildRequest dataProvider
let! output = sendToCompute input
let result = buildModel input output
do! store result
}
{
35. Post
dispose of resource when done
async {
use! response =
httpClient.PostAsync(uri, toContent request)
|> Async.AwaitTask
return!
response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()
|> Async.AwaitTask
}
F# async works with TPL Tasks
39. Ubiquitous Language
(Revised)
segment is an event
module Segment =
!
segment holds a value
over an interval
type T<'x,'y> =
| Instantaneous of Point.T<'x,'y>
| Discrete of IntervalType.T * Interval.T<'x> * 'y
| Continuous of Point.T<'x,'y> * Point.T<'x,'y>
segment between two data points
40. module Units =
!
Units of Measure
[<AutoOpen>]
module UnitNames =
/// a unit of time
[<Measure>] type minute
/// a unit of time
[<Measure>] type halfhour
/// a unit of time
[<Measure>] type hour
/// a unit of active power
[<Measure>] type megawatt
/// a unit of energy
[<Measure>] type poundssterling
/// a unit of frequency
[<Measure>] type hertz
[<AutoOpen>]
module UnitSymbols =
/// a synonym for halfhour, a unit of time
[<Measure>] type min = minute
/// a synonym for halfhour, a unit of time
[<Measure>] type hh = halfhour
/// a synonym for hour, a unit of time
[<Measure>] type h = hour
/// a synonym for megawatt, a unit of power
[<Measure>] type MW = megawatt
/// a synonym for pounds sterling, a unit of currency
[<Measure>] type ``£`` = poundssterling
/// a synonym for hertz, a unit of frequency
[<Measure>] type Hz = hertz
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/SI.fs
41. Units of Measure
// Conversion constants
let minutePerHalfhour = 30.0<min>/1.0<hh>
let minutePerHour = 60.0<min>/1.0<h>
let halfhourPerMinute = 1.0<hh>/30.0<min>
let halfhourPerHour = 2.0<hh>/1.0<h>
let hourPerMinute = 1.0<h>/60.0<min>
let hourPerHalfhour = 1.0<h>/2.0<hh>
module Minute =
let toHalfhour (a:float<min>) = a * halfhourPerMinute
let toHour (a:float<min>) = a * hourPerMinute
let inline lift a = LanguagePrimitives.FloatWithMeasure<min>(float a)
let liftTimeSpan (t:TimeSpan) = lift t.TotalMinutes
42. Contract Evaluation
to
p
let run interval initialState parameters
actualFrequencies targetFrequencies marketPrices pdtmLine =
let deloadLine = DeloadLineCalculation.run …
let holdingPayments = holdingPayments …
let referencePrices = ReferencePriceCalculation.run …
responseEnergyPayments …
se
cr
et
… but it involves a fold
43. Testing
// Straight forward implementation
!
let rec reverse = function
| [] -> []
| x::xs -> reverse xs @ [x]
!
// Efficient implementation
!
let rec revAcc xs acc =
match xs with
| [] -> acc
| h::t -> revAcc t (h::acc)
!
let rev xs =
match xs with
| [] -> xs
| [_] -> xs
| h1::h2::t -> revAcc t [h2;h1]
!
// Generate random tests to see if they behave the same
!
Check.Quick(fun (xs:int list) -> reverse xs = rev xs)
github.com/fsharp/FsCheck
44. Testing
nice names
open NUnit.Framework
open FsUnit
!
[<TestFixture; Category("Unit")>]
type ``When I run the deload line calculation`` () =
!
[<Test>]
member x.``with empty MEL line and empty PN line then the deload line is correct`` () =
let melLine = Line.empty
let pnLine = Line.empty
let actual = DeloadLineCalculation.run melLine pnLine
let expected : Line.Time.T<float<MW>> = Line.empty
actual |> should equal expected
structural equality for free
github.com/fsharp/FsUnit
46. C#
F#
Two Implementations of the Same Application
400000
30,801
348,430
21,442
Lines of Code
300000
305,566
200000
16,667
163,276
100000
643
56,929
487
53,270
9,359
42,864
3,630
29,080
0
15
3,011
Braces
Blanks
Null Checks
Comments
Useful Code
App Code
Test Code
Total Code
47. … things aren’t looking good for the old
way of doing things
53. Manifesto for Not Only Object-Oriented Development!
We are uncovering better ways of developing software by doing
it and helping others do it. Through this work we have come to
value:
!
•
•
•
•
•
0b
ov
Functions and Types over classes
0, er
si
gn 00
Purity over mutability
0,
at
Composition over inheritance
or 0 0
0
Higher-order functions over method dispatch
ie
s!
Options over nulls
10
!
That is, while there is value in the items on the right (except for
nulls), we value the items on the left more.
notonlyoo.org