0

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?

Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • 1
    you can think of `f` here as the *rest of the workflow* - so quite hard to get this out of there (it will change based on the rest of your code) – Random Dev Sep 10 '15 at 15:56
  • What are you trying to do with this? – Random Dev Sep 10 '15 at 16:00
  • Yeah your right f is of course the continuation and I don't might very hard but I'm stuck :) – Rune FS Sep 10 '15 at 16:00
  • @Carsten I'm trying to grab information from a called function for auditing purposes. Essentially I guess I'm trying to solve a cross cutting concern with the use of computation expressions – Rune FS Sep 10 '15 at 16:02
  • right now I cannot thing of anything beside quotations/reflection which is quite nasty - not sure what your goal is but sometimes you can just use types/generics to tag information along - for example instead of using attributes you could tag Information with `type Taged<'tag,'value> = Taged of 'value` - here you can use the `'tag` type for additional information quite similar to units of measure - just as an idea – Random Dev Sep 10 '15 at 16:07
  • I afraid that is not possible. Compiler may choose to inline functions so you have no reflection available for it. You can try to use quotations based design to make metaprogramming available. – ntr Sep 10 '15 at 17:58

1 Answers1

5

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.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Definately puts me in a position where I can proceed. The simple version won't work because the metadata of the function determines how the auditing will proceed. The function would be tagged with various information regarding auditing. We might be required by law to track who's calling said function whereas in other cases there might be other requirements and the flow might fail based on the combination of the auditing requirements and the user. In short the (might) metadata impacts the flow of the computation. – Rune FS Sep 11 '15 at 05:49