Как создать вычислительное выражение, которое принимает параметры? - PullRequest
4 голосов
/ 13 мая 2011

Я хочу создать пару вычислительных выражений, которые будут использоваться для доступа к базе данных и возврата списка элементов, например, так (у меня также есть вопросы в комментариях к коду):

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 ]

5 голосов
/ 14 мая 2011

Да, вы можете сделать это.Вы можете использовать синтаксис выражений вычислений после любого выражения со статически известным типом для предоставления правильных методов.Таким образом, следующий код работает (но не делает ничего особенно интересного):

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

Здесь f имеет тип 'a -> AsyncBuilder, поэтому f "test" имеет тип AsyncBuilder и может сопровождатьсясинтаксис выражений вычислений.Ваш пример let proc name = ProcedureBuilder(connStr, factory) вполне подходит, если предположить, что ProcedureBuilder определен соответствующим образом, хотя вы, вероятно, хотите, чтобы name появлялся где-то в аргументах конструктора.

4 голосов
/ 14 мая 2011

Ответ Кейта (kvb) правильный - вы можете использовать параметризованные компоновщики вычислений.Синтаксис выражений вычислений:

<expr> { <cexpr> }

Итак, конструктор может быть создан любым выражением.Обычно это какое-то значение (например, async), но это может быть вызов функции или даже вызов конструктора.При его использовании вы обычно определяете параметризованный построитель и затем передаете аргумент в конструктор, используя функцию (как подсказывает @kvb).

Я действительно написал пример этого, не так давно, такЯ могу поделиться примером, где - я думаю - это весьма полезно.Вы можете найти его в F # snippets: 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 }
...