一些背景优先.我目前正在学习一些关于monadic解析器组合的东西.当我试图从本文中转移'chainl1'功能时(第16-17页),我提出了这个解决方案:
let chainl1 p op = parser {
let! x = p
let rec chainl1' (acc : 'a) : Parser<'a> =
let p' = parser {
let! f = op
let! y = p
return! chainl1' (f acc y)
}
p' <|> succeed acc
return! chainl1' x
}
Run Code Online (Sandbox Code Playgroud)
我用一些大输入测试了函数,得到了一个StackOverflowException.现在我想知道,是否可以重写一个递归函数,它使用一些计算表达式,因此它使用尾递归?
当我扩展计算表达式时,我看不出它通常是如何可能的.
let chainl1 p op =
let b = parser
b.Bind(p, (fun x ->
let rec chainl1' (acc : 'a) : Parser<'a> =
let p' =
let b = …Run Code Online (Sandbox Code Playgroud) 当我比较ILF#为seq{}表达式生成的代码与用户定义的计算工作流程生成的代码时,很明显seq{}实现的方式非常不同:它生成一个类似于C#用于其迭代器方法的状态机.另一方面,用户定义的工作流使用您期望的相应构建器对象.
所以我想知道 - 为什么差异?
这是出于历史原因,例如"seq是否在工作流程之前"?
或者,是否有重要的表现?
还有其他原因吗?
我最近一直在使用F#工作,最近用C#web项目和F#类库编写了一个MVC应用程序,后者包含大部分代码.
我想使用MVC更高版本的异步控制器功能,你可以返回一个Task<ActionResult>.我正在使用F#异步计算表达式编写我的代码,但是不得不经常使用Async.AwaitTask和从Async <'T>转换为Task <'T> StartAsTask.
由于我当时正在学习计算表达式,我想"必须有一种方法可以使用任务和异步的计算表达式来实现更好的F#/ C#interop",所以我有一个去:
type TaskBuilder () =
member this.ReturnFrom task = task
member this.Return value = Task.FromResult value
member this.Bind (task : Task<'a>, continuation: 'a -> Task<'b>) =
task.ContinueWith(fun (t : _ Task) ->
(t.Result |> continuation).Result)
member this.Zero () = Task.FromResult ()
Run Code Online (Sandbox Code Playgroud)
这似乎有效; 运行以下代码:
let task = TaskBuilder()
task {
let! data = (new WebClient()).DownloadStringTaskAsync(Uri "http://www.google.com")
printfn "Downloaded from Google: %A" (data.Length)
}
|> ignore
task {
let! data = …Run Code Online (Sandbox Code Playgroud) 现在我正在尝试 F# 计算表达式。总体思路是返回控制机制来驱动从计算表达式构建递归函数调用的每一步之后执行的操作。整个例子可以在这里看到。
使用以下示例:
let rec loop () =
actor {
let! msg = m.Receive ()
match msg with
| "stop" -> return 0 // expected result: Return (0)
| "unhandled" -> unhandled // expected result: Unhandled
| x ->
mailbox.Sender() <! x
return! loop () // expected result: (Become(fun m -> loop ()))
}
loop ()
Run Code Online (Sandbox Code Playgroud)
不幸的是,这以编译时错误结束unhandled:在此计算中,自定义操作可能无法与“use”、“try/with”、“try/finally”、“if/then/else”或“match”运算符结合使用表达。
是否可以以任何方式在匹配语句中使用自定义运算符?
我有一个Result<'T, 'E> list我想Result<'T list, 'E>按照这些规则变成单一的:
Result,Error那么结果应该是ErrorError它应该是Error列表中的第一个OK则结果应为a,Ok并且应保持列表顺序所以我有一个去实现如下:
let all xs =
let folder = fun state next ->
match (state, next) with
| (Result.Ok ys, Result.Ok y) -> ys |> List.append [ y ] |> Result.Ok
| (Result.Error e, _) -> Result.Error e
| (_, Result.Error e) -> Result.Error e
Seq.fold folder (Result.Ok []) xs
Run Code Online (Sandbox Code Playgroud)
但是,这似乎已经在标准库中实现了.有吗?
其次,我有一个Result像这样的计算表达式:
type ResultBuilder …Run Code Online (Sandbox Code Playgroud) 我choice在标准库中找不到一个允许我写的对象
let safeDiv (numer : Choice<Exception, int>) (denom : Choice<Exception, int>) =
choice {
let! n = numer
let! d = denom
return! if d = 0
then Choice1Of2 (new DivideByZeroException())
else Choice2Of2 (n / d)
}
Run Code Online (Sandbox Code Playgroud)
就像在Haskell.我是否想念任何东西,或者是否有第三方图书馆来写这类东西,还是我必须重新发明这个轮子?
我正在编写一个基本上实现状态monad的计算表达式,我正在尝试使用for表达式.
我可以使用样板函数forLoop甚至MBuilder.For(),它们都返回一个很好的M<seq<'U>, _>,可以通过进一步的let!表达式处理.但是当我试图用for表达式做同样的事情时,它无法编译告诉我里面的表达式for必须返回unit.
对于一个我无法缩小的大型代码块,我感到很遗憾.
type M<'T, 'E> = 'T * 'E // Monadic type is a simple tuple
type MFunc<'T, 'U, 'E> = 'T -> M<'U, 'E> // A function producing monadic value
// typical boilerplate functions
let bind (x: M<'T, 'E>) (f: MFunc<'T, 'U, 'E>) : M<'U, 'E> =
let a, s = x
let b, s1 = f a
b, …Run Code Online (Sandbox Code Playgroud) 我正在尝试理解如何使用F#计算表达式,这当然让我很困惑.
以下示例对我有些意义.
type ListMonad() =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = List.collect f m
member o.Return(x) = [x]
let list = ListMonad()
let test =
list {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,你如何为这个计算表达式添加一个条件?具体来说,如果x值严格小于y值,你将如何改变它以返回元素列表?(我们之后不要过滤掉它).
假设我想从F#调用这个C#函数:
public static class Foo {
public Bar Baz()
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
问题是这个功能是CPU密集型的,我不想阻止它.不幸的是,C#库没有Task<Bar> BazAsync()过载.
然后我想通过创建一个调用它并返回(已经启动)的F#函数来自己提供异步版本Async<Bar>.也就是说,我不想用System.Threading.Task<Bar>.
我想我正在寻找的东西相当于Task<T>.Run()F#Async的做事方式.
我已经看过以下选项:
Async.StartAsTask - >处理C#ish任务类型.Async.Start和Async.StartImmediately- >收到Async<unit>不Async<T>是Async.StartChild我在寻找什么呢?如果是,那将是:
let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
Async.StartChild asyncTask
Run Code Online (Sandbox Code Playgroud)
但是,如果以上是解决方案:
Async<Async<Bar>>而不是Async<Bar>?可以使用F#中的计算表达式实现CPS吗?
Brian McNamara的博客提供了以下解决方案:
type ContinuationBuilder() =
member this.Return(x) = (fun k -> k x)
member this.ReturnFrom(x) = x
member this.Bind(m,f) = (fun k -> m (fun a -> f a k))
member this.Delay(f) = f()
let cps = ContinuationBuilder()
Run Code Online (Sandbox Code Playgroud)
看起来不错。我可以用List.mapCPS 编写:
let rec mapk f xs = cps {
match xs with
| [] -> return []
| x::xs ->
let! xs = mapk f xs
return f x::xs
}
Run Code Online (Sandbox Code Playgroud)
但是它会溢出:
mapk ((+) 1) [1..1000000] id
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?