This is easy once you realize that Object-Oriented Constructor Injection corresponds very closely to Functional Partial Function Application.
First, I'd write Dings
as a record type:
type Dings = { Lol : string; Rofl : string }
In F#, the IGetStuff
interface can be reduced to a single function with the signature
Guid -> seq<Dings>
A client using this function would take it as a parameter:
let Client getStuff =
getStuff(Guid("055E7FF1-2919-4246-876E-1DA71980BE9C")) |> Seq.toList
The signature for the Client
function is:
(Guid -> #seq<'b>) -> 'b list
As you can see, it takes a function of the target signature as input, and returns a list.
Generator
The generator function is easy to write:
let GenerateDingse id =
seq {
yield { Lol = "Ha!"; Rofl = "Ha ha ha!" }
yield { Lol = "Ho!"; Rofl = "Ho ho ho!" }
yield { Lol = "asd"; Rofl = "ASD" } }
The GenerateDingse
function has this signature:
'a -> seq<Dings>
This is actually more generic than Guid -> seq<Dings>
, but that's not a problem. If you only want to compose the Client
with GenerateDingse
, you could simply use it like this:
let result = Client GenerateDingse
Which would return all three Ding
values from GenerateDingse
.
Decorator
The original Decorator is a little bit more difficult, but not much. In general, instead of adding the Decorated (inner) type as a constructor argument, you just add it as a parameter value to a function:
let AdsFilteredDingse id s = s |> Seq.filter (fun d -> d.Lol = "asd")
This function has this signature:
'a -> seq<Dings> -> seq<Dings>
That's not quite what we want, but it's easy to compose it with GenerateDingse
:
let composed id = GenerateDingse id |> AdsFilteredDingse id
The composed
function has the signature
'a -> seq<Dings>
Just what we're looking for!
You can now use Client
with composed
like this:
let result = Client composed
which will return only [{Lol = "asd"; Rofl = "ASD";}]
.
You don't have to define the composed
function first; you can also compose it on the spot:
let result = Client (fun id -> GenerateDingse id |> AdsFilteredDingse id)
This also returns [{Lol = "asd"; Rofl = "ASD";}]
.
Alternative Decorator
The previous example works well, but doesn't really Decorate a similar function. Here's an alternative:
let AdsFilteredDingse id f = f id |> Seq.filter (fun d -> d.Lol = "asd")
This function has the signature:
'a -> ('a -> #seq<Dings>) -> seq<Dings>
As you can see, the f
argument is another function with the same signature, so it more closely resembles the Decorator pattern. You can compose it like this:
let composed id = GenerateDingse |> AdsFilteredDingse id
Again, you can use Client
with composed
like this:
let result = Client composed
or inline like this:
let result = Client (fun id -> GenerateDingse |> AdsFilteredDingse id)
For more examples and principles for composing entire applications with F#, see my on-line course on Functional architecture with F#.
For more about Object-Oriented Principles and how they map to Functional Programming, see my blog post on the SOLID principles and how they apply to FP.