【问题标题】:Indentation change after if-else expression not taken into account?不考虑 if-else 表达式后的缩进变化?
【发布时间】:2020-11-11 09:53:51
【问题描述】:

使用 this 运算符评估管道中的副作用

let inline (|>!) a f = f a ; a 

这个代码sn-p

if 1 = 1 then  
    "same"
else 
    "different"  
|>! printfn "The numbers are %s." 
|>  printfn "Yes, they are %s." 

这从不打印The numbers are same,但它确实打印Yes, they are same

为什么这里忽略了副作用运算符|>!,但考虑了|>,尽管缩进相同?

我是否必须以不同的方式定义副作用运算符?

这样写它可以按预期工作。

if 1 = 1 then   "same"
else "different"  
|>! printfn "The numbers are %s." 
|>  printfn "Yes, they are %s." 

代码实际上表现得好像写出来的那样对我来说只是不直观

if 1 = 1 then  
    "same"
else 
    "different"  
    |>! printfn "The numbers are %s." // with indent here
|>  printfn "Yes, they are %s." 

编辑:

另请参阅https://github.com/fsharp/fslang-suggestions/issues/806 以获得答案。

【问题讨论】:

    标签: f#


    【解决方案1】:

    这不是错误,也不会专门针对超过两个字符的运算符发生。这是 F# 允许偏移规则的一个有趣结果。

    当对齐相同嵌套级别的行时,它们必须在相同的缩进处,如下所示:

    let foo =
         bar
         baz
         qux
    

    但这是不允许的:

    let foo =
         bar
          baz  // Indented too much to the left
         qux
    

    这也不是:

    let foo =
         bar
        baz  // Indented too little
         qux
    

    在处理创建嵌套块的构造时,例如if/then,此规则用于确定块何时结束:即缩进对齐被破坏的时间。

    let x =
         if 1 = 1 then
           bar
           baz
         qux
    

    但是这个规则有一个例外:如果行以运算符开头,那么它最多允许左移运算符大小加1个字符,仍然会被认为是处于“当前”缩进

    例如,这是有效的:

    let x =
           1
         + 2
         + 3
    

    但是当你有不同规模的运营商时,它变得很棘手。这有效:

    let x =
           1
         + 2
         + 3
        <> 6
    

    但这不是:

    let x =
            1
         +  2
         +  3
         <> 6
    

    ? 这不起作用,因为 23 向左移动 更多 比运算符大小加一个字符。


    所以这就是你的情况:

    • 第一个 printfn 被认为是 else 块的一部分,因为它恰好与 "different" 对齐,但向左移动了运算符大小加一。
    • 但是,第二个 printfn 向左移动 更多 比运算符大小加一,因此它不再是 else 的一部分。
    • 但它仍然是正确的语法,因为现在它可以成为周围块的一部分,if/then/else 的整个结果通过管道传输到其中。
    • 通常你会得到一个语法错误,就像我在上面的 1+2+3 &lt;&gt; 6 示例中一样,但在这种情况下,语法恰好对齐(或者,也许,只是错误)。

    您可以通过删除第二个printfn 前面的多余空格来验证这一点:

    if 1 = 1 then  
        "same"
    else 
        "different"  
    |>! printfn "The numbers are %s." 
     |> printfn "Yes, they are %s." 
    

    现在第二个printfn 将成为else 的一部分,您会收到一个错误:types string and unit don't match。这是因为then 返回一个string,但else 现在返回一个unit。这可以通过修改then 部分来解决:

    if 1 = 1 then  
        ()
    else 
        "different"  
    |>! printfn "The numbers are %s." 
     |> printfn "Yes, they are %s." 
    

    现在编译并且不打印任何东西。如果您将1 = 1 替换为1 = 2,它将正确打印两次“不同”。

    最后,如果您希望整个if/then/else 块通过两个printfn 调用进行管道传输,您必须以某种方式打破"different" 与第一个printfn 的对齐。您为自己提供了一种方法:将"different"else 放在同一行。另一种方法是进一步缩进"different",使其不再与第一个printfn对齐:

    if 1 = 1 then  
        "same"
    else 
          "different"  
    |>! printfn "The numbers are %s." 
    |>  printfn "Yes, they are %s." 
    

    【讨论】:

    • 有趣。这可能是定义的行为,但对我来说似乎不直观。我很乐意让我的操作员在左侧排队(就像在惯用的多线 F# 管道中一样)。对操作员“取消”的能力似乎没有用。我从来没有做过。
    • 我认为已经有人建议为此添加警告:github.com/fsharp/fslang-suggestions/issues/806
    猜你喜欢
    • 1970-01-01
    • 2012-10-26
    • 2021-11-30
    • 2019-09-04
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多