Rune FS
Rune FS

Reputation: 21742

gettings attributes of f in a bind

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

Answers (1)

Tomas Petricek
Tomas Petricek

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

Related Questions