开发者

how do i create a computational expression that takes parameters?

开发者 https://www.devze.com 2023-03-06 00:19 出处:网络
I want to create a couple of computational expressions that would be used to access the database and return a list of items like so (I also have questions in the code comments):

I want to create a couple of computational expressions that would be used to access the database and return a list of items like so (I also have questions in the code comments):

let foo x y z = proc "foo" {
    let! cmd = proc.CreateCommand() // can I do this?
    do! In "x" DbType.Int32 // would i gain anything by replacing DbType with a union 
                            // type since the names would match actual data types?
    do! In "y" DbType.String 15;
    cmd?x <- x
    cmd?y <- y
    use! r = cmd.ExecuteReader() // would this be bad form for creating a workflow builder?
    return! r {
        let item = MyItem()
        do! item.a <- r.GetInt32("a")
        do! item.a <- r.GetString("b")
        do! item.c <- r.GetDateTime("c")
        yield! item
    }
}

How can I create a workflow builder such that an instance of it takes a parameter?

let proc name = ProcedureBuilder(connStr, factory) // h开发者_Python百科ow do I do this?


Yes, you can do this. You can use computation expression syntax after any expression with a type statically known to expose the right methods. So the following code works (but doesn't do anything particularly interesting):

let f x = async
let v = f "test" { return 1 }

Here, f has type 'a -> AsyncBuilder, so f "test" has type AsyncBuilder and can be followed with computation expression syntax. Your example of let proc name = ProcedureBuilder(connStr, factory) is perfectly fine, assuming that ProcedureBuilder is defined appropriately, though you presumably want name to appear somewhere in the constructor arguments.


The answer from Keith (kvb) is correct - you can use parameterized computation builders. The syntax of computation expressions is:

<expr> { <cexpr> }

So, the builder can be created by any expression. Usually, it is some value (e.g. async) but it can be a function call or even a constructor call. When using this, you would typically define a parameterized builder and then pass the argument to a constructor using a function (as @kvb suggests).

I actually wrote an example of this, not a long time ago, so I can share an example where - I think - this is quite useful. You can find it on F# snippets: http://fssnip.net/4z

The example creates a "special" asynchronous computation builder for ASP.NET MVC that behaves just like standard async. The only difference is that it adds Run member that uses AsyncManager (provided by ASP.NET) to execute the workflow.

Here are some relevant parts from the snippet:

/// A computation builder that is almost the same as stnadard F# 'async'.
/// The differnece is that it takes an ASP.NET MVC 'AsyncManager' as an
/// argumnet and implements 'Run' opration, so that the workflow is 
/// automatically executed after it is created (using the AsyncManager)
type AsyncActionBuilder(asyncMgr:Async.AsyncManager) = 
  // (Omitted: Lots of boilerplate code)

  /// Run the workflow automatically using ASP.NET AsyncManager
  member x.Run(workflow) = 
    // Use 'asyncMgr' to execute the 'workflow'

The snippet wraps the construction in a base class, but you could define a function:

let asyncAction mgr = new AsyncActionBuilder(mgr)

And then use it to define asynchronous action in ASP.NET MVC:

member x.LengthAsync(url:string) = asyncAction x.AsyncManager {
    let wc = new WebClient()
    let! html = wc.AsyncDownloadString(url)
    return html.Length }
0

精彩评论

暂无评论...
验证码 换一张
取 消