这段代码:
type Result = Success of string
type Tracer() =
member x.Bind(p: Result, rest: (string -> Result)) =
match p with
| Success s -> rest s
let tracer = new Tracer()
let t = tracer {
let! x = Success "yes!"
let! y = Success "waste of time"
return! Success x
}
printfn "%A" t
Run Code Online (Sandbox Code Playgroud)
打印成功"是的!"
但是发出警告意味着它不应该起作用:
File1.fs(19,3):warning FS0708:只有在计算表达式构建器定义'ReturnFrom'方法时才可以使用此控件构造
看起来像一个奇怪的警告:如果它是正确的,那么代码应该不起作用.它真的只是说建造者必须合成ReturnFrom吗?
(F#版本1.9.7.4,编译.NET Framework版本v4.0.21006)
我希望能够在F#中编写一个计算表达式,如果它抛出异常,它将能够重试一个操作.现在我的代码看起来像:
let x = retry (fun() -> GetResourceX())
let y = retry (fun() -> GetResourceY())
let z = retry (fun() -> DoThis(x, y))
etc. (this is obviously an astract representation of the actual code)
Run Code Online (Sandbox Code Playgroud)
我需要能够重试每个函数一定次数,我已经定义了elswhere.
我在想一个计算表达式可以帮助我,但是我没有看到它如何帮助我删除明确地将每个右侧包装到Retryable <'T>
我可以看到计算表达式看起来像:
let! x = Retryable( fun() -> GetResourceX())
etc.
Run Code Online (Sandbox Code Playgroud)
我知道莫纳德,粗糙的,是包装类型,但我希望能解决这个问题.我知道我可以重载一个操作符并且有一个非常简洁的语法将操作转换为Retryable <'T>,但对我来说这只是让重复/包装更简洁; 它还在那里.我可以换每个功能是可重试<'T>,但再一次,我看不出在做什么在后的顶部完成值(每个操作调用重试.至少这是非常明确的).
也许计算表达式在这里是错误的抽象,我不确定.关于在这里可以做些什么的任何想法?
我认为我已经对F#monads(工作流)有了足够的了解,我在代码中看到了一些实现它们的地方.
例如,我有一个具有多个嵌套if/thens的函数,即只要数据通过某些"测试",函数就应该继续.
我熟悉"也许"monad,但在我看过的所有例子中,它都被编码为对let!绑定进行操作,我没有这样做.我希望有人可以为我提供一个为嵌套布尔测试量身定制的"可能"工作流程的示例,而不是let绑定.
以下计算序列无错误地运行:
type Monad_1 () =
member M.Bind (so : 'T option, bf : 'T -> 'T option) : 'T option =
match so with
| Some s -> bf s
| None -> None
member M.Delay (df : unit -> 'T) : 'T = // SEE CORRECTION 1
df ()
member M.Return (rv : 'T) : 'T option =
Some rv
let test_1 () : unit =
let m_1 = Monad_1 ()
let cero =
m_1 {
let x1 = …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用我从心爱的堆栈溢出中获取的Retry Monad:
type RetryBuilder(max, sleep : TimeSpan) =
member x.Return(a) = a
member x.Delay(f) = f
member x.Zero() = failwith "Zero"
member x.Run(f) =
let rec loop(n) =
if n = 0 then failwith "Failed"
else
try f()
with ex ->
sprintf "Call failed with %s. Retrying." ex.Message |> printfn "%s"
Thread.Sleep(sleep);
loop(n-1)
loop max
Run Code Online (Sandbox Code Playgroud)
我想用它来使我的文件复制代码更健壮:
let retry = RetryBuilder(3, TimeSpan.FromSeconds(1.))
retry {
System.IO.File.Move("a", "b")
}
Run Code Online (Sandbox Code Playgroud)
现在我注意到它有时会因"零"异常而失败.我试图删除member x.Zero() = failwith "Zero"但现在我得到一个编译时错误:
仅当构建器定义"零"方法时,才能使用此构造.
任何想法如何进行?
我有以下计算表达式构建器:
type ExprBuilder() =
member this.Return(x) =
Some x
let expr = new ExprBuilder()
Run Code Online (Sandbox Code Playgroud)
我理解方法Return,Zero和Combine的目的,但我不明白下面的表达式有什么区别:
let a = expr{
printfn "Hello"
return 1
} // result is Some 1
let c = expr{
return 1
printfn "Hello"
} // do not compile. Combine method required
Run Code Online (Sandbox Code Playgroud)
我也不明白为什么在第一种情况下,零方法不需要printfn语句?
我最近开始使用计算表达式来简化我的代码.到目前为止,对我来说唯一有用的是MaybeBuilder,因此定义:
type internal MaybeBuilder() =
member this.Bind(x, f) =
match x with
| None -> None
| Some a -> f a
member this.Return(x) =
Some x
member this.ReturnFrom(x) = x
Run Code Online (Sandbox Code Playgroud)
但我想探索其他用途.一种可能性是我目前面临的情况.我有供应商提供的一些定义对称矩阵的数据.为了节省空间,仅给出矩阵的三角形部分,因为另一侧只是转置.所以,如果我在csv中看到一行代码
abc,def,123
这意味着行abc和列def的值是123.但是我不会看到像这样的行
def,abc,123
因为矩阵的对称性质已经给出了这个信息.
我已经在a中加载了所有这些数据,Map<string,Map<string,float>>并且我有一个函数可以获得任何看起来像这样的条目的值:
let myLookupFunction (m:Map<string,Map<string,float>>) s1 s2 =
let try1 =
match m.TryFind s1 with
|Some subMap -> subMap.TryFind s2
|_ -> None
match try1 with
|Some f -> f
|_ ->
let try2 =
match m.TryFind s2 with
|Some subMap -> subMap.TryFind …Run Code Online (Sandbox Code Playgroud) 我正在编写异步HTTP API客户端模块/库.为了使所有内容尽可能干,我试图从进行API调用的单独部分组成每个HTTP API调用,自下而上:构建请求,获取响应,将响应读入字符串缓冲区,解析JSON内容那个字符串缓冲区成一个对象.
到目前为止,我有这个代码:
module ApiUtils =
// ... request builder fns omitted ...
let getResponse<'a> (request : Net.WebRequest) =
request.AsyncGetResponse()
let readResponse (response : Net.WebResponse) =
use reader = new StreamReader(response.GetResponseStream())
reader.AsyncReadToEnd()
let getString = getResponse >> (Async.flatMap readResponse)
let parseJson<'T> responseText : 'T =
Json.JsonConvert.DeserializeObject<'T> responseText
let getJson<'T> = getString >> (Async.map parseJson<'T>)
Run Code Online (Sandbox Code Playgroud)
而且,正如您所看到的,我已经使用我自己的附加功能扩展了Async模块:
module Async =
let map f m =
async {
let! v = m
return f v
}
let flatMap f m =
async …Run Code Online (Sandbox Code Playgroud) .net f# asynchronous functional-programming computation-expression
Computation Expressions是面向方面编程的另一种方法吗?
这是F#解决跨领域问题的解决方案吗?
我查看了以下文章,不禁想到AOP(即面向方面编程).
在本文中,作者提供了一个处理日志记录的计算表达式的示例,但是在不模糊业务逻辑主要意图的情况下隔离了代码的实际日志记录方面.
我的想法准确吗?
我正在编写一个使用FSharp.Collections.ParallelSeq和重试计算的刮刀.我想从多个页面并行检索HTML,我想在失败时重试请求.
例如:
open System
open FSharp.Collections.ParallelSeq
type RetryBuilder(max) =
member x.Return(a) = a // Enable 'return'
member x.Delay(f) = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Zero() = failwith "Zero" // Support if .. then
member x.Run(f) = // Gets function created by 'Delay'
let rec loop(n) =
if n = 0 then failwith "Failed" // Number of retries exceeded
else try …Run Code Online (Sandbox Code Playgroud)