11

我注意到以下代码在您尝试编译时会出错:

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!” 迭代,不是吗?


添加,基于下面布赖恩的评论,为我的潜在问题提供解决方案:

我没有意识到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)
  }

现在,如果我能弄清楚为什么我认为这些语句需要缩进......

4

1 回答 1

8

就在几周前,我与 Don Syme 写了一篇论文,试图解释 F# 计算表达式(例如序列表达式、异步工作流等)中语法选择背后的一些动机。你可以在这里找到它。它没有对您的问题给出明确的答案,但它可能会有所帮助。

一般来说,当您有某种类型M<'T>并且您正在定义计算构建器时,您可以添加方法ForBind启用forlet!语法:

For  : seq<'T> -> ('T -> M<'T>) -> M<'T>
Bind : M<'T>   -> ('T -> M<'T>) -> M<'T>

for 的输入For应该始终是一些 sequence seq<'T>,而 for 的输入Bind应该是M<'T>您定义它的类型。

在序列表达式中,这两个操作将具有相同的类型,因此它们必须做同样的事情。尽管序列表达式可以同时提供两者,但只允许一个可能是个好主意,因为对一件事使用两个不同的关键字会造成混淆。一般原则是comp { .. }块中的代码应该像普通代码一样运行 - 由于for存在于普通 F# 代码中,因此在计算表达式中使用相同的语法是有意义的seq<'T>

于 2012-04-18T10:20:50.607 回答