如何在F#中实现null-safe运算符

时间:2014-04-29 15:34:53

标签: exception-handling f# operator-overloading nullreferenceexception quotations

我想知道你是否可以在F#中添加类似" null-safe" -operator的内容。我知道可能有计划在C#中使用这样一个运算符来进行下一个更大的发布。

如果没有办法实现这样的行为,是否有办法将一个可能抛出NullReferenceException的语句包装成一个捕获异常并只返回null的块。

我想到了类似的东西:

let sth = %% <@ someobject.method().another().andAnother() @>

其中%%是执行表达式并检查异常的自定义运算符。

我做的一个尝试是:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation

let (~%%) (right:Expr) =
  try 
    right.CompileUntyped()
    right()
  with
  | ex -> null

但这不符合我的要求(实际上它甚至没有编译:)

我通读了:

有没有人有想法如何优雅地为链式方法创建这样的单行try-catch?

3 个答案:

答案 0 :(得分:5)

我认为你不能为F#提供类似.?运算符的东西,这在实际上非常有用且语法上方便F#作为库特性。虽然,我认为这将是一个非常有用的语言扩展,所以我将其提交给F# user voice,也许submit a pull request: - )。

异常处理。您的代码示例与C#完全不同,因为您只是在处理任意异常。 C#功能的关键是它在每个null之后插入 .检查 - 您可以通过转换引号然后编译它来做到这一点,但编译将会慢一点你的功能可能只是:

let orNull f = try f () with :? NullReferenceException -> null

然后你可以写

orNull <| fun () -> x.Foo().Bar()

..但如前所述,这只是将代码包装在标准try块中并处理异常,因此它不会像C#代码那样做。

计算构建器。如果你想要一个更高级的F#解决方案,你可以想出一个计算表达式构建器,它可以让你写这样的东西:

safe { let! a = x.Foo()
       let! b = a.Bar()
       return b }

这可以像C#.?运算符一样进行空值检查,但是对于方法调用链的每个部分都需要单独的绑定,因此需要更多的输入。计算构建器在Bind成员中插入隐藏的空检查,如下所示:

type NullBuilder() = 
  member x.Return(v) = v
  member x.Bind(v, f) = if (box v) = null then null else f v 

let safe = NullBuilder()

答案 1 :(得分:3)

这是一个有趣的问题。幸运的是,仅存在于F#世界中的代码(即不使用其他语言编写的.NET库)并没有.?旨在解决的普遍问题,因为可空性必须是明确的(使用[<AllowNullLiteral>])。但是,这个概念还有一个功能:option类型和bind函数(bind是工作流的基础,如Tomas所示)。

你可以采取这样的方式:

type T() =
    member this.M(b) = if b then Some this else None

T().M(true)
|> Option.bind (fun t -> t.M(true))
|> Option.bind (fun t -> t.M(false)) 
|> Option.bind (fun t -> t.M(true))

并定义bind运算符

let (?>) o f = Option.bind f o

并将其缩短为:

T().M(true)
?> fun t -> t.M(true)
?> fun t -> t.M(false)
?> fun t -> t.M(true)

但是,这样做的价值非常小。它既不简洁也不清晰。但是,我认为它证明了:

  • 您正在处理的一般概念
  • 它并非固有地与null
  • 相关联
  • 通常用函数式语言处理这个问题

答案 2 :(得分:1)

也许使用F#worflows,扩展了Maybe monad的想法,并且能够写下这个:

let sth = maybe { someobject.method().another().another() }

请参阅http://santialbo.com/blog/2013/03/27/monads-in-f-sharp/