我如何创建一个带参数的计算表达式?

时间:2011-05-13 18:28:50

标签: f# monads

我想创建几个用于访问数据库并返回项目列表的计算表达式(我在代码注释中也有问题):

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
    }
}

如何创建工作流构建器,使其实例采用参数?

let proc name = ProcedureBuilder(connStr, factory) // how do I do this?

2 个答案:

答案 0 :(得分:5)

是的,你可以这样做。您可以在具有静态已知类型的任何表达式之后使用计算表达式语法来公开正确的方法。所以下面的代码可以工作(但没有做任何特别有趣的事情):

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

此处,f的类型为'a -> AsyncBuilder,因此f "test"的类型为AsyncBuilder,并且可以使用计算表达式语法。假设let proc name = ProcedureBuilder(connStr, factory)被正确定义,您的ProcedureBuilder示例完全正常,但您可能希望name出现在构造函数参数中的某处。

答案 1 :(得分:4)

Keith(kvb)的答案是正确的 - 您可以使用参数化计算构建器。计算表达式的语法是:

<expr> { <cexpr> }

因此,可以通过任何表达式创建构建器。通常,它是一些值(例如async),但它可以是函数调用,甚至是构造函数调用。使用它时,通常会定义一个参数化构建器,然后使用函数将参数传递给构造函数(如@kvb建议的那样)。

我实际上写了一个这样的例子,不是很久以前,所以我可以分享一个例子 - 我认为 - 这非常有用。您可以在F#片段中找到它:http://fssnip.net/4z

该示例为ASP.NET MVC创建了一个“特殊”异步计算构建器,其行为与标准async类似。唯一的区别是它添加Run成员使用AsyncManager(由ASP.NET提供)来执行工作流。

以下是摘录中的一些相关部分:

/// 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'

该片段将构造包装在基类中,但您可以定义一个函数:

let asyncAction mgr = new AsyncActionBuilder(mgr)

然后使用它在ASP.NET MVC中定义异步操作:

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