在F#monad中,如果你说let!,编译器会将其转换为Bind你在monad构建器上定义的成员.
现在我看到有一个Query monads,如MSDN所示,你可以说:
query {
for student in db.Student do
select student
count
}
Run Code Online (Sandbox Code Playgroud)
和select和count,例如,将被翻译到QueryBuilder成员Linq.QueryBuilder.Select和Linq.QueryBuilder.Count.
我的问题是,关键字映射到成员是否已硬连接到F#编译器,还是可扩展?例如,我可以这样说:
FooMonadBuilder() {
bar
}
Run Code Online (Sandbox Code Playgroud)
并以某种方式告诉F#编译器bar映射到一个FooMonadBuilder.Bar()方法?
扩展计算表达式的意思是使用CustomOperation属性定义的自定义关键字的计算表达式.
在阅读扩展计算表达式时,我遇到了@kvb非常酷的IL DSL:
let il = ILBuilder()
// will return 42 when called
// val fortyTwoFn : (unit -> int)
let fortyTwoFn =
il {
ldc_i4 6
ldc_i4_0
ldc_i4 7
add
mul
ret
}
Run Code Online (Sandbox Code Playgroud)
我想知道如何在不使用for..in..do构造的情况下构成操作.我的直觉是它从x.Zero成员开始,但我没有找到任何参考来验证.
如果上面的例子太技术性,那么这里是类似的DSL,其中列出了幻灯片的组件而没有for..in..do:
page {
title "Happy New Year F# community"
item "May F# continue to shine as it did in 2012"
code @"…"
button (…)
} |> SlideShow.show
Run Code Online (Sandbox Code Playgroud)
我有几个密切相关的问题:
For成员的情况下定义或使用扩展计算表达式(即提供一个小的完整示例)?如果他们不再是monad我不担心,我对他们开发DSL感兴趣.let!和 …我一直在查看查询表达式http://msdn.microsoft.com/en-us/library/vstudio/hh225374.aspx
而且我一直在想为什么以下是合法的
let testQuery = query {
for number in netflix.Titles do
where (number.Name.Contains("Test"))
}
Run Code Online (Sandbox Code Playgroud)
但你真的不能做这样的事情
let christmasPredicate = fun (x:Catalog.ServiceTypes.Title) -> x.Name.Contains("Christmas")
let testQuery = query {
for number in netflix.Titles do
where christmasPredicate
}
Run Code Online (Sandbox Code Playgroud)
当然F#允许这样的可组合性,所以你可以重用一个谓词?如果我想在特定日期之前将圣诞节标题与另一个谓词结合起来怎么办?我必须复制并粘贴我的整个查询?C#与此完全不同,有几种方法可以构建和组合谓词
F#3.0 beta包含一个查询{}计算表达式,包含大量新关键字.
如何在计算构建器中定义自己的关键字?
F#计算表达式具有以下语法:
ident { cexpr }
Run Code Online (Sandbox Code Playgroud)
ident构建器对象在哪里(此语法取自Don Syme的2007年博客条目).
在我看到的所有示例中,构建器对象是单例实例,无状态引导.Don给出了定义一个名为的构建器对象的示例attempt:
let attempt = new AttemptBuilder()
Run Code Online (Sandbox Code Playgroud)
我的问题:为什么F#不AttemptBuilder直接在计算表达式中使用该类?当然,符号可以像实例方法调用一样轻松地去除静态方法调用.
使用实例值意味着理论上可以实例化同一类的多个构建器对象,可能以某种方式参数化,甚至(天堂禁止)具有可变的内部状态.但我无法想象这将是多么有用.
更新:我上面引用的语法表明构建器必须显示为单个标识符,这是误导性的,可能反映了该语言的早期版本.最新的F#2.0语言规范将语法定义为:
expr { comp-or-range-expr }
Run Code Online (Sandbox Code Playgroud)
这清楚地表明任何表达式(评估为构建器对象)都可以用作构造的第一个元素.
F#给我带来了类型推理规则的一些麻烦.我正在编写一个简单的计算构建器,但无法使我的泛型类型变量约束正确.
我想要的代码在C#中如下所示:
class FinallyBuilder<TZ>
{
readonly Action<TZ> finallyAction;
public FinallyBuilder(Action<TZ> finallyAction)
{
this.finallyAction = finallyAction;
}
public TB Bind<TA, TB>(TA x, Func<TA, TB> cont) where TA : TZ
{ // ^^^^^^^^^^^^^
try // this is what gives me a headache
{ // in the F# version
return cont(x);
}
finally
{
finallyAction(x);
}
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,我为F#版本提出的最佳(但非编译代码)是:
type FinallyBuilder<?z> (finallyAction : ?z -> unit) =
member this.Bind (x : ?a) (cont : ?a -> ?b) =
try …Run Code Online (Sandbox Code Playgroud) 假设我想Option在async工作流程中返回一段时间:
let run =
async {
let! x = doAsyncThing
let! y = doNextAsyncThing x
match y with
| None -> return None
| Some z -> return Some <| f z
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我会使用FSharpx中的可能计算表达式同时作为异步来避免这样做match.我可以创建一个自定义构建器,但是有没有办法一般地组合两个计算表达式?它可能看起来像这样:
let run =
async {
let! x = doAsyncThing
let! y = doNextAsyncThing x
return! f y
}
Run Code Online (Sandbox Code Playgroud) monads f# asynchronous monad-transformers computation-expression
我正在创建一个计算表达式 (CE),以简化建模者计划的定义。我想定义仅在CE中可用的函数。在此示例中,编译器表示自定义操作step和branch的使用不正确,但我不明白为什么。编译器所说的只是它们没有被正确使用。
请注意,我知道我可以在 CE 之外定义
step和来完成此操作。branch这个问题明确是关于使用自定义运算符的。我想隔离这个逻辑,以便它仅在 CE 上下文中可用。
type Step =
| Action of string
| Branch of string list list
type Plan =
{
Name : string
Steps : Step list
}
type PlanBuilder () =
member _.Yield _ =
{
Name = ""
Steps = []
}
member _.Run state = state
[<CustomOperation "name">]
member _.Name (state, name) =
{ state with Name = name }
[<CustomOperation "steps">]
member _.Steps …Run Code Online (Sandbox Code Playgroud) 我正在阅读Tomas Petricek和Jon Skeet撰写的真实函数式编程书,我很难消化关于计算表达式1)(又名monads)的部分.
通过本书,我了解到 - 与我之前的经验相反 - LINQ查询表达式不仅限于此IEnumerable<T>,而且可以在其他自定义类型上工作.这对我来说似乎很有趣,我想知道是否存在查询表达式语法(from x in ... select ...)非常适合的情况.
显然,这种自定义类型称为计算类型,它们被描绘为与Haskell中的monad基本相同.我从来没有能够理解monad究竟是什么,但根据这本书,它们是通过两个叫做bind和return的操作来定义的.
在函数式编程中,这两个操作的类型签名将是(我认为):
// Bind : M<A'> -> (A' -> B') -> M<B'>
//
// Return : A' -> M<A'>
Run Code Online (Sandbox Code Playgroud)
Mmonadic类型的名称在哪里.
在C#中,这对应于:
Func< M<A>, Func<A,B>, M<B> > Bind;
Func< A, M<A> > Return;
Run Code Online (Sandbox Code Playgroud)
事实证明,LINQ Enumerable.Select(投影运算符)与绑定操作具有完全相同的签名M := IEnumerable.
使用这些知识,我现在可以编写一个不是 的自定义计算类型IEnumerable: …
我有一个计算表达式构建器,可以随时构建一个值,并具有许多自定义操作.但是,它不允许使用标准的F#语言结构,而且我在确定如何添加此支持时遇到了很多麻烦.
为了给出一个独立的例子,这里有一个简单且毫无意义的计算表达式来构建F#列表:
type Items<'a> = Items of 'a list
type ListBuilder() =
member x.Yield(()) = Items []
[<CustomOperation("add")>]
member x.Add(Items current, item:'a) =
Items [ yield! current; yield item ]
[<CustomOperation("addMany")>]
member x.AddMany(Items current, items: seq<'a>) =
Items [ yield! current; yield! items ]
let listBuilder = ListBuilder()
let build (Items items) = items
Run Code Online (Sandbox Code Playgroud)
我可以使用它来构建列表就好了:
let stuff =
listBuilder {
add 1
add 5
add 7
addMany [ 1..10 ]
add 42
}
|> build
Run Code Online (Sandbox Code Playgroud)
但是,这是编译器错误:
listBuilder {
let x …Run Code Online (Sandbox Code Playgroud)