在一行中编写F#对象表达式

6

当我准备为F#编写一个代码生成器时,我在想能否通过只生成单行值来避免缩进混乱的问题。

在这个过程中,我考虑如何用一行代码表达对象表达式,但除了冗长模式外,我无法成功。

let Expr() = (let ToString = "ToString" in { new System.Object() with member this.ToString() = ToString; member this.GetHashCode() = ToString.GetHashCode()})

问题在于我不想以冗长模式生成所有代码,这是一种兼容性特性。是否还有其他选项?
非常感谢您的见解!
François
我之所以这样问,是因为我必须在任意表达式中生成对象表达式,并且我希望避免计算当前行中字符的数量以计算下一行缩进量。
2个回答

3

(不要介意这个广告) 我维护着F#源代码格式化程序,它公开了API,可以美化完整的F# 3.0语法。你有几个选择:

  • 使用详细模式实现代码生成器,并在输出上运行源代码格式化API。然后你就不必担心缩进和详细模式下的换行问题。

  • 使用函数组合器实现代码生成器。在FormatConfig模块中有很多组合器,你可以复制并修改。F#缩进规则很清晰,你可以在这篇文章中了解更多。

  • 你可能有一个AST需要进行美化处理。如果你喜欢轻量级的解决方案,F#编译器CodeDom有类似的组合器用于代码生成。


谢谢,所有这些解决方案都是我会考虑的选项。 - FremyCompany

2

严格来说,这不是一个答案,但使用适当的缩进有什么坏处呢?您可以始终将生成的行列出,并在单独的步骤中添加缩进。这就是我通常生成代码的方式,即使语言(例如 HTML)不需要缩进。

为什么生成的代码必须难以阅读?恰当的缩进使得查看生成的代码更容易,并且您甚至可以使用通常的差异工具,例如如果已经将生成的源代码提交到源代码控制中。

至少根据我的经验,适当的缩进实际上比人们想象的要容易得多,特别是在生成时。请不要忘记,在生成过程中您拥有所有上下文,因此添加缩进级别应该非常容易。

let indent = List.map (sprintf "    %s")
[
    yield "let Expr() ="
    yield! [
        yield "let ToString = \"ToString\""
        yield "{"
        yield! [
            "new System.Object() with"
            "member this.ToString() = ToString"
            "member this.GetHashCode() = ToString.GetHashCode()"
        ] |> indent
        yield "}"
    ] |> indent
]

你是对的。我以前已经使用了一些更面向对象的方法添加了非常相似的解决方案(记录一下,IndentedStringBuilder基本上是一个带有IndentMore()/IndentLess()和重写WriteLine()的StringBuilder),但在F#中这更加困难,因为缩进不仅仅是像C#那样的“more/less”,例如
let k = DoSomethingWith({ ... })
而不是C#代码的形式
let k = DoSomethingWith({ ... })
- FremyCompany
1
在F#中,您有一些自由度,可以自由缩进。为了简单起见,您可以在“=”,“[”或“{”之后换行,然后进行更多缩进。这样,您就不需要考虑前一行字符所在的列,而始终可以缩进4个空格。查看我的示例输出,这应该几乎总是有效的,还要注意我放置换行的位置。 - Daniel Fabian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接