如何结合需要单位结果的 ConditionalAttribute,将 TextWriterFormat 的功能用于 printfn 样式
How to leverage power of TextWriterFormat for printfn style in combination with ConditionalAttribute which requires unit result
我自己创建了一个跟踪函数,它的行为类似于 sprintf
或 printfn
,但是通过使用 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# 创建的内联函数似乎不遵守,至少在这种特定情况下不遵守。
我尝试了各种变体,包括 kprintf
、sprintf
以查看是否有帮助,但都无济于事。
有什么想法吗?或者这是您尝试铺地毯的情况之一,一旦您在一个角上适当地平滑它,它就会在另一个角上起泡,反之亦然,即它永远不适合?
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
您需要切换到元组形式以允许重载并使用元组形式,因为柯里化的东西不能被重载
我自己创建了一个跟踪函数,它的行为类似于 sprintf
或 printfn
,但是通过使用 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# 创建的内联函数似乎不遵守,至少在这种特定情况下不遵守。
我尝试了各种变体,包括 kprintf
、sprintf
以查看是否有帮助,但都无济于事。
有什么想法吗?或者这是您尝试铺地毯的情况之一,一旦您在一个角上适当地平滑它,它就会在另一个角上起泡,反之亦然,即它永远不适合?
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
您需要切换到元组形式以允许重载并使用元组形式,因为柯里化的东西不能被重载