为什么seq计算生成器不允许"let!"

Luc*_*c C 11 f#

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

let xx =
  seq {
    let! i = [ 1; 2 ]
    let! j = [ 3; 4 ]
    yield (i,j)
  }
Run Code Online (Sandbox Code Playgroud)

这给出的错误是" 错误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)
  }
Run Code Online (Sandbox Code Playgroud)

我的问题不是如何解决这个问题,而是为什么"让!" 首先不允许在序列表达式中?我可以看到让我们这样的事实!迭代表达式可能会让一些人感到意外,但这不足以禁止构造.我也看到"for"在这里是如何更强大的,因为"let!"的版本 在迭代范围内烘焙为"直到序列表达式结束".

但是,能够迭代序列而不必缩进代码正是我所寻找的(用于遍历树结构).我假设要获得这个语义,我将不得不创建一个新的表达式构建器,其行为主要类似于"seq"表达式构建器,但允许"let!" 迭代,不是吗?


根据Brian的评论添加,为我的潜在问题提供解决方案:

我没有意识到for block中的缩进是不需要的,第二个样本可以重写为:

let xx =
  seq {
    for i in [ 1; 2 ] do
    for j in [ 3; 4 ] do
    yield (i,j)
  }
Run Code Online (Sandbox Code Playgroud)

...当遍历树结构时,它摆脱了不断增加的缩进.语法甚至允许在两者之间附加声明陈述,而不需要额外的缩进,如:

let yy =
  seq {
    for i in [ 1; 2 ] do
    let i42 = i+42
    for j in [ 3; 4 ] do
    yield (i42,j)
  }
Run Code Online (Sandbox Code Playgroud)

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

Tom*_*cek 8

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

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

For  : seq<'T> -> ('T -> M<'T>) -> M<'T>
Bind : M<'T>   -> ('T -> M<'T>) -> M<'T>
Run Code Online (Sandbox Code Playgroud)

输入For应始终为某个序列seq<'T>,而输入BindM<'T>为您要定义的类型.

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

  • 好纸,托马斯! (2认同)