F# 如何将TextWritePerformat的强大功能与需要单位结果的ConditionalAttribute结合使用

F# 如何将TextWritePerformat的强大功能与需要单位结果的ConditionalAttribute结合使用,f#,printf,partial-application,debug-build,conditional-attribute,F#,Printf,Partial Application,Debug Build,Conditional Attribute,我将自己设置为创建一个跟踪函数,该函数的行为类似于sprintf或printfn,但通过使用 目前为止的结果:我认为这是不可能的。 问题的核心是,当您使用条件(“DEBUG”)属性时,函数必须返回单元结果。“正常”参数正常工作,并且方法被正确修饰(编辑:修饰,是的,但可转换成员不会被删除,请参见,必须改用元组形式): 这在不使用属性的情况下有效,但使用属性时,将引发: 此表达式应具有类型 单元 但是这里有类型 string->unit 错误特别在Trace.Trace“hello:%s”下面划线

我将自己设置为创建一个跟踪函数,该函数的行为类似于
sprintf
printfn
,但通过使用

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

问题的核心是,当您使用
条件(“DEBUG”)
属性时,函数必须返回单元结果。“正常”参数正常工作,并且方法被正确修饰(编辑:修饰,是的,但可转换成员不会被删除,请参见,必须改用元组形式):

这在不使用属性的情况下有效,但使用属性时,将引发:

此表达式应具有类型
单元

但是这里有类型
string->unit

错误特别在
Trace.Trace“hello:%s”
下面划线。因此,编译器似乎没有意识到整个表达式会产生一个
单元
,并引发错误,因为它在内部创建了一个包装函数,返回
字符串->单元
,这是
条件属性
的规则所不允许的

当我试图通过在函数返回类型上显式指定
:unit
,或将
printfn msg |>ignore
作为主体来修复它时,我就失去了使用文本编写器格式字符串进行类型安全的能力,事实上,它再也无法识别调用站点上的第二个参数

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

我尝试过各种变体,包括
kprintf
sprintf
,看看是否有帮助,但都没有效果

有什么想法吗?或者,这是一种情况,即你试图铺设地毯,一旦你在一个角落将其适当平滑,它就会在另一个角落起泡,反之亦然,也就是说,它永远不合适


PS:如果你想知道我为什么想要它:只是尝试创建一个方便的功能,它的行为类似于现有的跟踪,但其他一些功能正在幕后进行。我目前所做的工作是有效的,但它只需要一个字符串,而不是一个静态类型的检查参数,因此它强制用户编写如下内容:

(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
开放式系统诊断
类型Log()=
[]  
静态成员日志(x)=printfn x
[]
静态成员日志(x,y)=printfn x y

您需要切换到元组形式以允许重载,并使用元组形式,因为咖喱食品不能重载

我想知道您是否可以使用运算符来实现这一点?@JohnPalmer:知道怎么做吗?运算符afaik接受一个或两个参数,而不是数量可变的参数。我觉得奇怪的是,重新思考我的帖子,(1)在签名上强制
unit
会禁用格式化参数,即使它已经返回了unit,(2)在
printfn
之后返回unit也会取消使用格式化参数的功能。。。我明白为什么,但我不明白为什么…;)运算符的想法是编写一组具有不同数量参数的重写。问题是,
printfn
的返回类型非常奇怪,取决于格式string@JohnPalmer,我不太确定这对使用
ConditionalAttribute
会有什么帮助,但如果您有一个原型想法可以显示您的目标,请务必继续添加答案;)我最初认为需要操作员的诡计,但不需要。你们可能真的可以用条件来标记这些。啊,现在我知道它是如何工作的了,它是如何工作的!类型推断工作正常。呼叫站点完全被JIT删除,一切都很好!(奇怪,因为我以为我尝试过元组,但显然从未尝试过简单的方法;)。这是很容易扩展的,我之前对该扩展的评论是多余的,您只需要为每个参数数指定一个变量(并且您必须对参数进行元组设置)。您知道这为什么适用于元组而不适用于curried参数吗?您不能重载curried参数functions@Abel作为旁注,可以对多变量函数进行编码(用咖喱语)在F#中,但它非常粗糙。参见。
#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