我注意到以下代码在您尝试编译时会出错:
let xx =
seq {
let! i = [ 1; 2 ]
let! j = [ 3; 4 ]
yield (i,j)
}
这给出的错误是“错误FS0795:不再允许在序列表达式中使用'let!x = coll'。而是使用'for x in coll'。”此消息是当然清楚并演示如何解决它;固定代码是:
let xx =
seq {
for i in [ 1; 2 ] do
for j in [ 3; 4 ] do
yield (i,j)
}
我的问题不是如何解决这个问题,而是为什么“让!”首先不允许在序列表达式中?我可以看到让我们这样的事实!迭代表达式可能会让一些人感到意外,但这不足以禁止构造。我也看到“for”在这里是如何更强大的,因为“let!”的版本在迭代范围内烘焙为“直到序列表达式结束”。
然而,能够迭代序列而不必缩进代码正是我所寻找的(用于遍历树结构)。我假设要获得这个语义,我将不得不创建一个新的表达式构建器,其行为主要类似于“seq”表达式构建器,但允许“let!”对于迭代,不是吗?
根据Brian的评论添加,为我的潜在问题提供解决方案:
我没有意识到 for 块中的缩进是不需要的,第二个样本可以重写为:
let xx =
seq {
for i in [ 1; 2 ] do
for j in [ 3; 4 ] do
yield (i,j)
}
...在遍历树结构时摆脱了不断增加的缩进。语法甚至允许 for 语句之间的其他语句,而不需要额外的缩进,如:
let yy =
seq {
for i in [ 1; 2 ] do
let i42 = i+42
for j in [ 3; 4 ] do
yield (i42,j)
}
现在,如果我能弄清楚为什么我认为这些陈述需要缩进......
答案 0 :(得分:8)
就在几个星期前,我和Don Syme一起撰写了一篇论文,试图解释F#计算表达式(如序列表达式,异步工作流程等)中语法选择背后的一些动机。你可以find it here。它没有给出你的问题的明确答案,但它可能有所帮助。
通常,如果您有某个类型M<'T>
并且您正在定义计算构建器,则可以添加方法For
和Bind
以启用for
和{{1语法:
let!
For : seq<'T> -> ('T -> M<'T>) -> M<'T>
Bind : M<'T> -> ('T -> M<'T>) -> M<'T>
的输入应始终为某个序列For
,而seq<'T>
的输入应为您要定义的Bind
类型。
在序列表达式中,这两个操作具有相同的类型,因此它们必须执行相同的操作。尽管序列表达式可以同时提供两者,但是允许只有一个是一个好主意,因为对一件事使用两个不同的关键字会让人感到困惑。一般原则是M<'T>
块中的代码应该像普通代码一样 - 并且由于comp { .. }
存在于普通F#代码中,因此在for
的计算表达式中使用相同的语法是有意义的。