Reputation: 21742
I have a bit of code in a workflow where I'd like to get the attributes to a function
the expression is this
let! accounts = _accounts()
and in my bind I have this
member this.Bind(x,f) =
let attributes =
f.GetType()
.GetCustomAttributes(typeof<myattribute>,false)
The idea is to get the attributes of the function _accounts(). However f represents the countinuation rather than _accounts and so anyway I can get the attributes of the called function?
Upvotes: 0
Views: 77
Reputation: 243041
I'd take one step back - first, you need to figure out what is the computation that you want to model. Based on what you said, you could keep a result with a list of some audit log information:
type Audited<'T> =
{ Log : string list
Result : 'T }
The standard basic computation builder would just create empty log in Return
and Bind
would simply concatenate the logs:
type AuditBuilder() =
member x.Return(v) = { Log = []; Result = v }
member x.Bind(c, f) =
let fr = f c.Result
{ fr with Log = c.Log @ fr.Log }
let audit = AuditBuilder()
You could really just use this, provided that your accounts
function would return a correct Audited<'T>
value:
let accounts () =
{ Result = 40
Log = ["accounts"] }
let stocks () =
{ Result = 2
Log = ["stocks"] }
audit {
let! a = accounts()
let! s = stocks()
return a + s }
Now, the question is, can we make this a bit nicer, so that accounts()
does not have to be special function. You could do this in various ways - but it's more of a question about creating Audited<'T>
values now!
One way to do something like this would be to pass quotation to Bind
. A very basic and simple implementation looks like this:
let plain () = 123
open Microsoft.FSharp.Quotations
type AuditBuilder with
member x.Bind(e:Expr<'T>, f:'T -> _) =
match e with
| Patterns.Call(None, mi, []) ->
let r = f (mi.Invoke(null, [| |]) :?> 'T)
{ r with Log = r.Log @ [mi.Name] }
| _ -> failwith "invalid"
This adds an overloaded Bind
that lets you "call" a quoted function, but it automatically extracts the name:
audit {
let! p = <@ plain() @>
return p }
This still needs the quotation - I guess you could experiment with other ways of doing this - but the key idea is that you have a basic computation, which really defines what the structure is.
Upvotes: 5