如何结合需要单位结果的 ConditionalAttribute,将 TextWriterFormat 的功能用于 printfn 样式

How to leverage power of TextWriterFormat for printfn style in combination with ConditionalAttribute which requires unit result

我自己创建了一个跟踪函数,它的行为类似于 sprintfprintfn,但是通过使用 ConditionalAttribute .

目前的结果:我认为不可能。

问题的核心是当您使用 Conditional("DEBUG") 属性时,函数必须 return 单位结果。 "Normal" 参数按应有的方式工作并且方法被正确装饰(编辑:装饰,是的,但是可咖喱成员未被删除,参见 discussion,必须改用元组形式):

type Trace() =    
    [<Conditional("DEBUG")>]
    static member inline trace msg b = (msg, b) |> ignore

// using it, x is unit
let x = Trace.trace "test" "foo"

(请注意,如果没有 ignore,由于 Conditional 属性,这将无法编译)

但是,一旦我尝试了 Printf.TextWriterFormat<'T> 的任何变体,它就会失败,而且我看不到解决方法:

type Trace() =
    [<Conditional("DEBUG")>]
    static member inline trace msg = printfn msg    // inline or not doesn't matter

    // using it, x is unit
    let x = Trace.trace "hello: %s" "foobar"

这在没有属性的情况下有效,但是 属性,它将引发:

This expression was expected to have type
      unit
but here has type
      string -> unit

错误特别强调 Trace.trace "hello: %s"。所以看起来编译器没有意识到整个表达式导致 unit,并引发错误,因为它在内部创建了一个包装函数 returns string -> unit,它不是ConditionalAttribute.

规则允许

当我尝试通过在函数 return 类型上明确指定 :unit 或将 printfn msg |> ignore 作为正文来修复它时,我就失去了使用文本编写器格式的能力具有类型安全性的字符串,事实上,它根本不会再识别调用站点上的第二个参数。

因此,虽然整个函数签名都遵守 CLR 的规则,但 F# 创建的内联函数似乎不遵守,至少在这种特定情况下不遵守。

我尝试了各种变体,包括 kprintfsprintf 以查看是否有帮助,但都无济于事。

有什么想法吗?或者这是您尝试铺地毯的情况之一,一旦您在一个角上适当地平滑它,它就会在另一个角上起泡,反之亦然,即它永远不适合?


PS:如果您想知道 为什么 我想要它:只是尝试创建一个行为类似于现有 Trace 的便利函数,但还有一些其他功能在引擎盖下。我目前使用的是有效的,但它只需要一个字符串,而不是静态类型检查的参数,因此它会强制用户编写如下内容:

(sprintf "Hello %s" >> Trace.trace) "foobar"

基于重载的版本。

您可能需要更多重载

#if DEBUG
type Log() =
    static member inline log(x) = printfn x
    static member inline log(x,y) = printfn x y
#else
type Log =
    static member inline log(x) = ()
    static member inline log(x,y) = ()
#end

更新:

所以这有效:

open System.Diagnostics
type Log() =
    [<Conditional("DEBUG")>]  
    static member log(x) = printfn x
    [<Conditional("DEBUG")>]
    static member log(x,y) = printfn x y

您需要切换到元组形式以允许重载并使用元组形式,因为柯里化的东西不能被重载