I am trying to use a Computation Expression to build a list of actions. I need to bind to a value that gets returned from the getFood action so that I can register a later step to consume it.
type Food =
| Chicken
| Rice
type Step =
| GetFood of Food
| Eat of Food
| Sleep of duration:int
type Plan = Plan of Step list
type PlanBuilder () =
member this.Bind (plan:Plan, f) =
f plan
member this.Yield _ = Plan []
member this.Run (Plan x) = Plan (List.rev x)
[<CustomOperation("eat")>]
member this.Eat (Plan p, food) =
printfn "Eat"
Plan ((Eat food)::p)
[<CustomOperation("sleep")>]
member this.Sleep (Plan p, duration) =
printfn "Sleep"
Plan ((Sleep duration)::p)
let plan = PlanBuilder()
let rng = System.Random(123)
let getFood (Plan p) =
printfn "GetFood"
let randomFood =
if rng.NextDouble() > 0.5 then
Food.Chicken
else
Food.Rice
(Plan ((GetFood randomFood)::p)), randomFood
let testPlan =
plan {
let! food = getFood // <-- This is what I am trying to get to work
sleep 10
eat food
}
I believe the problem is with the Bind but I can't figure out what it is.
(*
Example result
testPlan =
(GetFood Chicken,(
(Sleep 10,(
EatFood Chicken
))
))
*)
To make something like this work, you will probably need a type that has a more monadic structure and lets you store any result, rather than just the plan. I would use something like this:
type Step =
| GetFood of Food
| Eat of Food
| Sleep of duration:int
type Plan<'T> = Plan of Step list * 'T
Now, Plan<'T> represents a computation that produces a value of type 'T and collects a plan along the way. GetFood can produce a plan, but also return the food:
let getFood () =
printfn "GetFood"
let randomFood =
if rng.NextDouble() > 0.5 then Food.Chicken
else Food.Rice
Plan([GetFood randomFood], randomFood)
Implementing a computation builder is a bit of a black art, but you can now define Bind and your custom operations. To be able to access variables in the argument, it needs to be a function as in the where or select operations:
type PlanBuilder () =
member this.For (Plan(steps1, res):Plan<'T>, f:'T -> Plan<'R>) : Plan<'R> =
let (Plan(steps2, res2)) = f res
Plan(steps1 # steps2, res2)
member this.Bind (Plan(steps1, res):Plan<'T>, f:'T -> Plan<'R>) : Plan<'R> =
let (Plan(steps2, res2)) = f res
Plan(steps1 # steps2, res2)
member this.Yield x = Plan([], x)
member this.Return x = Plan([], x)
member this.Run (Plan(p,r)) = Plan(List.rev p, r)
[<CustomOperation("eat", MaintainsVariableSpace=true)>]
member this.Eat (Plan(p, r), [<ProjectionParameter>] food) =
Plan((Eat (food r))::p, r)
[<CustomOperation("sleep", MaintainsVariableSpace=true)>]
member this.Sleep (Plan(p, r), [<ProjectionParameter>] duration) =
Plan ((Sleep (duration r))::p, r)
let plan = PlanBuilder()
This actually lets you implement your test plan:
let testPlan =
plan {
let! food = getFood ()
sleep 10
eat food
return ()
}
That said, in practice, I'm not sure I would actually want to use this. I would probably just us a seq { .. } computation that uses yield to accumulat the steps of the plan.
Related
Is there a step-by-step process for converting several pipelines into a functional composition?
Side note:
Did I even use the term "functional composition" in the right context?
I have the following code:
let purchase qty inventory =
buyFrom inventory qty |> fromInventory
|> reduceBy qty
I wanted to refactor this code to support functional composition.
So I thought I could do this:
let purchase qty inventory =
buyFrom inventory >> fromInventory >> reduceBy qty
This code compiles. However, it appears that I am missing an argument when attempting to invoke the purchase function within the debugger.
Here's my test for the function:
[<Test>]
let ``buy last product``() =
[SomeProduct] |> purchase 1
|> should equal []
NOTE:
I'm still struggling with FP fundamentals and still do not understand the process of converting a pipeline work flow into a functional composition.
I think I understand that functional composition relies on partial application which I think I have a handle on now. Thus, I have reason to believe that my refactored function is missing an argument.
Any guidance on a workflow I can use to get better at functional composition?
Appendix:
type Product =
| SomeProduct
type TransactionResult = { Price:decimal; Inventory:Product list }
(* Functions *)
let priceOf qty product =
match product with
| SomeProduct -> match qty with
| 3 -> decimal (qty - 1) * 1.99m
| _ -> decimal (qty) * 1.99m
let rec reduceBy count list =
match list with
| first::remaining when count = 1 -> remaining
| first::second::remaining when count > 1 -> second::remaining |> reduceBy (count - 1)
| remaining when count = 0 -> remaining
| _ -> []
let buyFrom inventory qty =
{ Price=priceOf qty (List.head inventory); Inventory=inventory }
let fromInventory transactionResult =
transactionResult.Inventory
The direct translation to a composed form would be either:
Using "forward" function composition:
let purchase qty inventory = (buyFrom inventory >> fromInventory >> reduceBy qty) qty
Using "backward" function composition (Personally, I'm not sure I like the term "backward" because this is function composition in the traditional mathemetical sense of the term, i.e. f << g = f ∘ g):
let purchase' qty inventory = reduceBy qty << fromInventory << buyFrom inventory <| qty
As you can perhaps see from the above examples, it's really most valuable when it lets you skip the last argument entirely. In this example, you can't do that because you depend on qty twice so I'd recommend you stick with piping here.
In general, if you have a function f(x) and a function g(x) you can write a composed function h(x) = (f ∘ g)(x) in the following ways:
let h x = f (g (x))
let h x = x |> g |> f
let h x = f << g <| x
let h x = (g >> f) x
In the second two cases, using function composition, you can omit the x entirely.
let h = f << g
let h = g >> f
Usually, if you can massage your pipeline expression to have the form
let f x y value = value |> f1 x |> f2 y |> f3
you can refactor it to
let f x y = f1 x >> f2 y >> f3
That's not quite the case here, as your expression begins with a function call (buyFrom inventory qty), the value of which is then piped to fromInventory.
In this case, I think you can refactor to
let purchase' inventory qty =
qty |> buyFrom inventory |> fromInventory |> reduceBy qty
but then you can't really get any further because qty is also needed as an argument to reduceBy.
I think you might be able to eliminate both occurrences of qty by using the Reader or State monad, but it's hardly worth the effort.
While the code I have does what I need, I feel I am missing some coding technique to implement this kind of stuff in a more concise way.
The goal is to compose items and give them an Id value along the way.
Here the code which I feel can be simplified and improved in many ways. If only I knew, how...
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let makeA ids =
A(Seq.head ids), Seq.skip 1 ids
let makeB ids =
B(Seq.head ids), Seq.skip 1 ids
let makeC ids =
C(Seq.head ids), Seq.skip 1 ids
let config = [makeA; makeA; makeC; makeB]
let create (ids : seq<int>) (cfg : (seq<int> -> Foo * seq<int>) list) : Foo list * seq<int> =
let rec cre ids1 acc cl =
match cl with
| [] -> (acc,ids1)
| x::xs ->
let v,ids2 = x ids1
cre ids2 (acc # [v]) xs
cre ids [] cfg
let result : Foo list * seq<int> = create (ids 0) config
Which results in the very simple:
val result : Foo list * seq = ([A 0; A 1; C 2; B 3], )
Somehow I feel there should be an easier way to accomplish the same.
In fact, I know one way to make it simpler but this would involve mutable state and memoization (and would thusly be probably considered worse):
let idgen startvalue =
let v = ref startvalue
fun () ->
let result = !v
v := !v + 1
result
With the thusly received generator function I could get rid of all those tuples, at least and I could also get rid of create function and simply write:
let ids = idgen 0
let result =
[
A(ids())
A(ids())
C(ids())
B(ids())
]
But there should also exist a "functional" way to get it done more simply.
It seems that what you want is to take two sequences, one of functions, the other of arguments, and produce new sequence by applying functions to corresponding arguments, where in your particular case arguments are successive integer numbers and functions are union case constructors. Would that be a correct assessment?
If so, here's what I would do:
let create args funs =
Seq.zip args funs
|> Seq.map (fun (arg, fn) -> fn arg)
|> List.ofSeq
let result = create (ids 0) [A; A; C; B]
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let config = [A; A; C; B]
let create ids i cfg =
let ids' = ids i
let nextI = i + List.length cfg
(Seq.map2 id cfg ids'), nextI
let result, nextI = create ids 0 config
let result2, nextI2 = create ids nextI config
There are several considerations here:
A, B, and C are constructors of the type int -> Foo. You may directly use them as functions and eliminate repetitive wrapper functions;
Seq.map2 is able to process sequences of different length and ignore the remaining elements of the longer one;
id is a shortcut for fun f number -> f number, please use the longer one if you find id unclear
You may also refactor ids function into Seq.initInfinite id |> Seq.skip startvalue.
At the end, you may convert a sequence into a List if you really need.
Updated to receive an keep i and nextI.
As Fyodor pointed out in his answer, you really just want to apply your constructors to consecutive integers. You can use the built-in mapi for that, writing your entire program as:
type Foo = | A of int | B of int | C of int
let config = [A; A; B; C]
let create offset = List.mapi (fun i f -> f (offset + i))
create 78 config
// val it : Foo list = [A 78; A 79; B 80; C 81]
I have a computation expression builder that builds up a value as you go, and has many custom operations. However, it does not allow for standard F# language constructs, and I'm having a lot of trouble figuring out how to add this support.
To give a stand-alone example, here's a dead-simple and fairly pointless computation expression that builds F# lists:
type Items<'a> = Items of 'a list
type ListBuilder() =
member x.Yield(()) = Items []
[<CustomOperation("add")>]
member x.Add(Items current, item:'a) =
Items [ yield! current; yield item ]
[<CustomOperation("addMany")>]
member x.AddMany(Items current, items: seq<'a>) =
Items [ yield! current; yield! items ]
let listBuilder = ListBuilder()
let build (Items items) = items
I can use this to build lists just fine:
let stuff =
listBuilder {
add 1
add 5
add 7
addMany [ 1..10 ]
add 42
}
|> build
However, this is a compiler error:
listBuilder {
let x = 5 * 39
add x
}
// This expression was expected to have type unit, but
// here has type int.
And so is this:
listBuilder {
for x = 1 to 50 do
add x
}
// This control construct may only be used if the computation expression builder
// defines a For method.
I've read all the documentation and examples I can find, but there's something I'm just not getting. Every .Bind() or .For() method signature I try just leads to more and more confusing compiler errors. Most of the examples I can find either build up a value as you go along, or allow for regular F# language constructs, but I haven't been able to find one that does both.
If someone could point me in the right direction by showing me how to take this example and add support in the builder for let bindings and for loops (at minimum - using, while and try/catch would be great, but I can probably figure those out if someone gets me started) then I'll be able to gratefully apply the lesson to my actual problem.
The best place to look is the spec. For example,
b {
let x = e
op x
}
gets translated to
T(let x = e in op x, [], fun v -> v, true)
=> T(op x, {x}, fun v -> let x = e in v, true)
=> [| op x, let x = e in b.Yield(x) |]{x}
=> b.Op(let x = e in in b.Yield(x), x)
So this shows where things have gone wrong, though it doesn't present an obvious solution. Clearly, Yield needs to be generalized since it needs to take arbitrary tuples (based on how many variables are in scope). Perhaps more subtly, it also shows that x is not in scope in the call to add (see that unbound x as the second argument to b.Op?). To allow your custom operators to use bound variables, their arguments need to have the [<ProjectionParameter>] attribute (and take functions from arbitrary variables as arguments), and you'll also need to set MaintainsVariableSpace to true if you want bound variables to be available to later operators. This will change the final translation to:
b.Op(let x = e in b.Yield(x), fun x -> x)
Building up from this, it seems that there's no way to avoid passing the set of bound values along to and from each operation (though I'd love to be proven wrong) - this will require you to add a Run method to strip those values back off at the end. Putting it all together, you'll get a builder which looks like this:
type ListBuilder() =
member x.Yield(vars) = Items [],vars
[<CustomOperation("add",MaintainsVariableSpace=true)>]
member x.Add((Items current,vars), [<ProjectionParameter>]f) =
Items (current # [f vars]),vars
[<CustomOperation("addMany",MaintainsVariableSpace=true)>]
member x.AddMany((Items current, vars), [<ProjectionParameter>]f) =
Items (current # f vars),vars
member x.Run(l,_) = l
The most complete examples I've seen are in §6.3.10 of the spec, especially this one:
/// Computations that can cooperatively yield by returning a continuation
type Eventually<'T> =
| Done of 'T
| NotYetDone of (unit -> Eventually<'T>)
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Eventually =
/// The bind for the computations. Stitch 'k' on to the end of the computation.
/// Note combinators like this are usually written in the reverse way,
/// for example,
/// e |> bind k
let rec bind k e =
match e with
| Done x -> NotYetDone (fun () -> k x)
| NotYetDone work -> NotYetDone (fun () -> bind k (work()))
/// The return for the computations.
let result x = Done x
type OkOrException<'T> =
| Ok of 'T
| Exception of System.Exception
/// The catch for the computations. Stitch try/with throughout
/// the computation and return the overall result as an OkOrException.
let rec catch e =
match e with
| Done x -> result (Ok x)
| NotYetDone work ->
NotYetDone (fun () ->
let res = try Ok(work()) with | e -> Exception e
match res with
| Ok cont -> catch cont // note, a tailcall
| Exception e -> result (Exception e))
/// The delay operator.
let delay f = NotYetDone (fun () -> f())
/// The stepping action for the computations.
let step c =
match c with
| Done _ -> c
| NotYetDone f -> f ()
// The rest of the operations are boilerplate.
/// The tryFinally operator.
/// This is boilerplate in terms of "result", "catch" and "bind".
let tryFinally e compensation =
catch (e)
|> bind (fun res -> compensation();
match res with
| Ok v -> result v
| Exception e -> raise e)
/// The tryWith operator.
/// This is boilerplate in terms of "result", "catch" and "bind".
let tryWith e handler =
catch e
|> bind (function Ok v -> result v | Exception e -> handler e)
/// The whileLoop operator.
/// This is boilerplate in terms of "result" and "bind".
let rec whileLoop gd body =
if gd() then body |> bind (fun v -> whileLoop gd body)
else result ()
/// The sequential composition operator
/// This is boilerplate in terms of "result" and "bind".
let combine e1 e2 =
e1 |> bind (fun () -> e2)
/// The using operator.
let using (resource: #System.IDisposable) f =
tryFinally (f resource) (fun () -> resource.Dispose())
/// The forLoop operator.
/// This is boilerplate in terms of "catch", "result" and "bind".
let forLoop (e:seq<_>) f =
let ie = e.GetEnumerator()
tryFinally (whileLoop (fun () -> ie.MoveNext())
(delay (fun () -> let v = ie.Current in f v)))
(fun () -> ie.Dispose())
// Give the mapping for F# computation expressions.
type EventuallyBuilder() =
member x.Bind(e,k) = Eventually.bind k e
member x.Return(v) = Eventually.result v
member x.ReturnFrom(v) = v
member x.Combine(e1,e2) = Eventually.combine e1 e2
member x.Delay(f) = Eventually.delay f
member x.Zero() = Eventually.result ()
member x.TryWith(e,handler) = Eventually.tryWith e handler
member x.TryFinally(e,compensation) = Eventually.tryFinally e compensation
member x.For(e:seq<_>,f) = Eventually.forLoop e f
member x.Using(resource,e) = Eventually.using resource e
The tutorial at "F# for fun and profit" is first class in this regard.
http://fsharpforfunandprofit.com/posts/computation-expressions-intro/
Following a similar struggle to Joel's (and not finding §6.3.10 of the spec that helpful) my issue with getting the For construct to generate a list came down to getting types to line up properly (no special attributes required). In particular I was slow to realise that For would build a list of lists, and therefore need flattening, despite the best efforts of the compiler to put me right. Examples that I found on the web were always wrappers around seq{}, using the yield keyword, repeated use of which invokes a call to Combine, which does the flattening. In case a concrete example helps, the following excerpt uses for to build a list of integers - my ultimate aim being to create lists of components for rendering in a GUI (with some additional laziness thrown in). Also In depth talk on CE here which elaborates on kvb's points above.
module scratch
type Dispatcher = unit -> unit
type viewElement = int
type lazyViews = Lazy<list<viewElement>>
type ViewElementsBuilder() =
member x.Return(views: lazyViews) : list<viewElement> = views.Value
member x.Yield(v: viewElement) : list<viewElement> = [v]
member x.ReturnFrom(viewElements: list<viewElement>) = viewElements
member x.Zero() = list<viewElement>.Empty
member x.Combine(listA:list<viewElement>, listB: list<viewElement>) = List.concat [listA; listB]
member x.Delay(f) = f()
member x.For(coll:seq<'a>, forBody: 'a -> list<viewElement>) : list<viewElement> =
// seq {for v in coll do yield! f v} |> List.ofSeq
Seq.map forBody coll |> Seq.collect id |> List.ofSeq
let ve = new ViewElementsBuilder()
let makeComponent(m: int, dispatch: Dispatcher) : viewElement = m
let makeComponents() : list<viewElement> = [77; 33]
let makeViewElements() : list<viewElement> =
let model = {| Scores = [33;23;22;43;] |> Seq.ofList; Trainer = "John" |}
let d:Dispatcher = fun() -> () // Does nothing here, but will be used to raise messages from UI
ve {
for score in model.Scores do
yield makeComponent (score, d)
yield makeComponent (score * 100 / 50 , d)
if model.Trainer = "John" then
return lazy
[ makeComponent (12, d)
makeComponent (13, d)
]
else
return lazy
[ makeComponent (14, d)
makeComponent (15, d)
]
yield makeComponent (33, d)
return! makeComponents()
}
I have a discriminated union, such as
type Dish =
| Eggs
| Spam of Dish
This is basically a linked list, without any content, e.g. Spam(Spam(Spam(Eggs))). I want to strictly perform a computation on this structure, such as counting the length, and memorize the result. In a normal type, I'd use class-local let bindings, but those aren't available in discriminated unions.
One way to do this would be,
type Count = int
type Dish =
| Eggs
| Spam of Dish * Count
But this is really messy, when the data I need is easily computable, but I still hope there is a better way (without using external mutable constructs).
One option is making the union cases private to hide the cached length.
//the 'guts' of Dish -- entirely hidden
type private DishImpl =
| Eggs
| Spam of DishImpl
// Dish wrapper type -- implementation hidden
type Dish =
private
| Dish of DishImpl * int
with
// O(1), just get the 'length' field
member x.Length = let (Dish(_, len)) = x in len
static member Eggs() = Dish(Eggs, 1)
static member Spam(Dish(dish, len)) = Dish(Spam dish, len + 1)
let eggs = Dish.Eggs()
let spam = Dish.Spam(eggs)
printfn "%d" eggs.Length //outputs: 1
printfn "%d" spam.Length //outputs: 2
To do it up right, create an accompanying module with let-bound functions and active patterns for destructuring.
If you tolerate a bit internal mutable state, here is a memoize function which creates a dictionary per function:
let memoize f =
let dict = Dictionary()
fun n ->
match dict.TryGetValue(n) with
| (true, v) -> v
| _ ->
let res = f(n)
dict.Add(n, res)
res
// This function results in a warning though
let rec length = memoize (function Eggs -> 0 | Spam d -> 1 + length d)
The approach isn't that bad since the mutable dictionary is hidden.
A purely functional approach could be using Map to hold values and a kind of State computation expression to hide Map values passing around. Please refer to this snippet to see how a memoize computation expression looks like.
There is also Memo Functions, Polytypically! by Ralph Hinze (2000). Adapting to F#:
type Dish =
| Eggs
| Spam of Dish
type DishTable<'T> =
{
Eggs : Lazy<'T>
Spam : Lazy<DishTable<'T>>
}
let rec tabulate (f: Dish -> 'T) : DishTable<'T> =
{
Eggs = lazy f Eggs
Spam = lazy tabulate (f << Spam)
}
let rec lookup (table: DishTable<'T>) (dish: Dish) : 'T =
match dish with
| Eggs -> table.Eggs.Value
| Spam x -> lookup table.Spam.Value x
let memo (f: Dish -> 'T) : (Dish -> 'T) =
lookup (tabulate f)
let rec len x =
match x with
| Eggs -> 0
| Spam x -> 1 + len x
let l2 = memo len
This is what I came up with. It's not true memoization because it counts eagerly when you call mem, but might work for your needs.
type Dish =
| Eggs
| Spam of Dish
| Memo of Dish * int
with
member d.length =
match d with
| Eggs -> 1
| Spam d -> 1 + d.length
| Memo (d, i) -> i
member d.mem =
match d with
| Eggs -> Memo(d, d.length)
| Spam d2 -> Memo(d, d.length)
| Memo(d2, i) -> d // no need to memo it again
let x = Spam (Spam(Spam Eggs))
let m = x.mem
x.length // val it : int = 4
m.length // val it : int = 4
Note that in your case, literally the only interesting property of a value of your type is its length, so you might as well just use integers as your representation instead:
let Eggs = 0
let Spam n = 1 + n
let (|Eggs|Spam|) = function
| 0 -> Eggs
| n -> Spam(n-1)
let length = id
// example usage
let dish = Spam(Spam(Eggs))
let l = length dish
let kind =
match dish with
| Eggs -> "Eggs"
| Spam(Eggs) -> "One Spam"
| Spam(Spam _) -> "At least two Spams"
If your real question is how to do this for a more interesting type, then one approach would be to create mutually recursive types, one of which is annotated:
type 'a AnnotatedDish = { dish : 'a Dish; value : 'a }
and 'a Dish =
| Eggs
| Spam of 'a AnnotatedDish
// "smart" constructors, given that you want to annotate with length
let eggs = { dish = Eggs; value = 0 }
let spam d = { dish = Spam d; value = 1 + d.value }
let length { value = l } : int = l
// active patterns
let (|Eggs|Spam|) = function
| { dish = Eggs } -> Eggs
| { dish = Spam d } -> Spam d
// example usage
let dish = spam(spam(eggs))
let l = length dish
let kind =
match dish with
| Eggs -> "Eggs"
| Spam(Eggs) -> "One Spam"
| Spam(Spam _) -> "At least two Spams"
After reviewing the answers, I've decided to go with a model that seems the least obtrusive to me. I've used a modified object to demonstrate how it would work in a slightly more complex scenario.
type StackDef<'a>(v : 'a, s : Stack<'a>) =
member val Length = s.Length + 1
member val Inner = v, s
and Stack<'a> =
| Empty
| Stack of StackDef<'a>
member this.Length =
match this with
| Empty -> 0
| Stack(def) -> def.Length
let Stack (v, s) = Stack(StackDef(v, s))
let (|Stack|Empty|) = function | Empty -> Empty | Stack(sd) -> Stack(sd.Inner)
//...
let example = Stack(1, Stack(2, Stack(3, Empty))).Length
It doesn't contain any external mutable state.
The discriminated union Dish (or in the example, Stack) continues to exist.
The field length doesn't appear in the union definition at all, nor is it provided by any constructor, just as it should be.
The memoized data is associated with the instance, as it should be.
However, having thought about it, by using a static weaver such as Afterthought it might be possible to replace any method such as:
Stack<'a> =
| Empty
| Stack of 'a * Stack<'a>
[<Lazy>] //custom attribute that would work with a static weaver
member this.Length =
match this with
| Empty -> 0
| Stack(_, s) -> s.Length + 1
With a private readonly Lazy<int> __length initialized in the constructor with a delegate that executes the above code, and change the actual content of the method to simply invoking __length.Value.
While F# doesn't allow union types to contain fields, possibly for very valid reasons, I highly doubt the IL would have such restrictions.
In fact, it would be possible to do a lot of things using some IL manipulation. Maybe it's something to think about.
Given a type (Candidate) which has muiltiple fields one may score (here one concrete example with _scoreXXX) and calculate the total percentage score:
type ScorableCandidate() =
let mutable _scoreXXX: int = 0 ;
let mutable _percentXXX: float = 0. ;
member this.scoreXXX
with get() = _scoreXXX
and set(v) =
_scoreXXX<- v
propertyChanged.Trigger(this, new PropertyChangedEventArgs("scoreXXX"))
member this.percentXXX
with get() = _percentXXX
and set(v) =
_percentXXX <- v
propertyChanged.Trigger(this, new PropertyChangedEventArgs("percentXXX"))
I would like to absctract the calculation of the percentages so I don't need to redefine the function everytime. Unfortunately I am lost on how to do this with member functions. The template of the code I am abstracting looks is:
let scoreXXXs () =
let totalXXXScore = List.fold (fun acc (candidate: ScorableCandidate) -> acc + candidate.scoreXXXs) 0 brokers
let score (candidate: ScorableCandidate) =
candidate.percentXXXs <- (float candidate.scoreXXXs) / float totalXXXScore * 100.
if totalXXXScore > 0 then
List.iter score <| brokers
I guess I would like to be able to define the function as "score" and pass in the aapropriate member accessors. Such that i could write
score XXX // where xxx is some property in the class that needs to be scored
score YYY // where yyy is some property in the class that needs to be scored
One thought I had was passing in functions to get access, and that is easy enough to do for the getter but I can't seem to figure out the setter. I realize I could defer to reflection for this - but I feel that might not be the best (f#) way... AT this point I am at:
let scoreField (accessF : (ScorableCandidate -> int)) (setF : (float -> unit)) =
let totalScore = List.fold (fun acc (candidate: ScorableCandidate) -> acc + accessF candidate) 0 brokers
let score (candidate: ScorableCandidate) =
setF <| (accessF candidate |> float) / float totalScore * 100.
if totalScore > 0 then
List.iter score <| brokers
scoreField (fun c -> c.scoreXXX) (fun f -> ())
But I don't know how (or if it is possible) to represent the setter as a lambda function on the type (where maybe I can pass the instace as paraneter to lambda function and invoke it somehow).
Thoughts? Thanks ahead.
Update Found this approach (thoughts):
http://laurent.le-brun.eu/site/index.php/2009/10/17/52-dynamic-lookup-operator-aka-duck-typing-in-fsharp
You're on the right track, but your settor is missing the object you're going to set the field on. So the type of setF should be:
setF : (ScorableCandidate -> float -> unit)
so then you'd use it like:
let score (candidate: ScorableCandidate) =
(setF candidate) <| (accessF candidate |> float) / float totalScore * 100.
and call your scoreField thus:
scoreField (fun c -> c.scoreXXX) (fun c f -> c.percentXXX <- f)
So, if I understand it correctly, you need to store several scores (for various different things) in a single candidate and then perform calculations over these scores.
In that case, I would consider making Score a separate type that would be used by the candidate - you can then easily add multiple scores. If you need to expose score & percent as direct properties of candidate & notify using IPropertyChange, then you should be able to write something like this:
/// Takes a name of this score item (e.g. XXX) and a
/// function to trigger the property changed event
type Score(name:string, triggerChanged) =
let mutable score = 0
let mutable percent = 0.0
member x.Score
with get() = score
and set(v) =
score <- v
triggerChanged("Score" + name)
member x.Percent
with get() = percent
and set(v) =
percent <- v
triggerChanged("Percent" + name)
Now you can simply use the Score object as many times you need in the candidate (because we also want to expose direct properties, there is some boilerplate, but it should be reasonable amount):
type ScorableCandidate() as this =
// Create trigger function & pass it to Score objects
let trigger n = propertyChanged.Trigger(this, n)
let infoXxx = new Score("Xxx", trigger)
member this.InfoXxx = scoreXxx // Exposes the whole Score object
member this.ScoreXxx = scoreXxx.Score // & individual...
member this.PercentXxx = scoreXxx.Percent // ...properties
Now, your parameterized function can simply take a function that takes ScorableCandidate and returns a Score object:
let scores f =
let totalScore = List.fold (fun acc (candidate: ScorableCandidate) ->
let so = f candidate // Get 'Score' object
acc + so.Score) 0 brokers
let score (candidate: ScorableCandidate) =
let so = f candidate // Get 'Score' object
so.Percent <- (float so.Score) / float totalScore * 100.0
if totalScore > 0 then
List.iter score <| brokers
Example call would be simply:
scores (fun (c:ScorableCandidate) -> c.InfoXxx)
This makes the call to the scores function as easy as it can get and it is also scalable solution that makes it easy to add other calculations to the Score object. The disadvantage (e.g. when compared with Pavel's straightforward solution) is that there is a bit more work with designing the Score type (however, declaring new scores in ScorableCandidate is then arguably easier if you only need to expose readable property directly in the class - which I think should be enough).
To make the API simpler, you might want to consider one of the following options:
Use reflection. Then you could do scoreField "XXX", and your method could explicitly translate "XXX" into MethodInfos for your get_scoreXXX and set_percentXXX methods. This has the disadvantage that it doesn't check the method names at compile time and it has the performance penalty that comes along with reflection.
Use quotations. Then you could do scoreField <# fun c -> c.scoreXXX, c.percentXXX #>. This would otherwise work similarly to the reflection example, except that you'd have a bit more compile-time checking.
Create a type which represents score/percent combinations, and create properties of that type rather than separate properties for each. Your ScorePercent class could have getters and setters for the score and percent (and raise its own change notifications). Then, you could do scoreField (fun c -> c.XXX), where the XXX member is of type ScorePercent.