如何使用Option.map做空条件?F#如何获取null对象的属性?错误?

ca9*_*3d9 0 f#

下面的代码返回Some "Test"代替None.基本上,我正在尝试实现C#代码cObj?.B.A.P.

// Setup
[<AllowNullLiteral>]
type A() =
    member x.P = "Test"

[<AllowNullLiteral>]
type B(a:A) =
    member x.A = a

[<AllowNullLiteral>]
type C(b:B) =
    member x.B = b

// Test
let aObj: A = null
let cObj = new C(new B(aObj))

let r = 
    cObj |> Option.ofObj
    |> Option.map(fun c -> c.B)
    |> Option.map(fun b -> b.A) 
    |> Option.map(fun a -> a.P) // Expect return None since a is null
                                // printfn "%A" a; will print <null>.
                                // How can F# got property of null object?

r
Run Code Online (Sandbox Code Playgroud)

这似乎F#不把null作为None在Option.map.是否有一个简单的修复方法,一旦null找到它就会返回None ?

Fyo*_*kin 5

与C#不同,F#试图在任何地方都是明确的.从长远来看,这会带来更多可维护和正确的程序.

特别是,null绝对没有任何关系Option.null是不一样的None.None是一个类型的值Option,而null这是一个非常模糊的概念 - 一个可以是任何类型的值.

如果您想返回None时的说法是nullSome否则,你需要的是Option.bind,没有Option.map.Option.bind采用一个取值(从前一个中提取Option)并返回另一个值的函数Option.像这样的东西:

let maybeC = Option.ofObj cObj
let maybeB = maybeC |> Option.bind (c -> Option.ofObj c.B)
let maybeA = maybeB |> Option.bind (b -> Option.ofObj b.A)
let maybeP = maybeA |> Option.bind (a -> Option.ofObj a.P)
Run Code Online (Sandbox Code Playgroud)

或者一气呵成:

let maybeP = 
    Option.ofObj cObj
    |> Option.bind (c -> Option.ofObj c.B)
    |> Option.bind (b -> Option.ofObj b.A)
    |> Option.bind (a -> Option.ofObj a.P)
Run Code Online (Sandbox Code Playgroud)

如果你经常这样做,你可以组合Option.bind,Option.ofObj调用和编码作为一个单独的功能:

let maybeNull f = Option.bind (x -> Option.ofObj (f x))

let maybeP =
    Option.ofObj cObj
    |> maybeNull (c -> c.B)
    |> maybeNull (b -> b.A)
    |> maybeNull (a -> a.P)
Run Code Online (Sandbox Code Playgroud)

但是,如果你发现自己被埋没了null,我会建议你的域名设计可能没有经过深思熟虑.Nulls不是一个好的建模工具,应该尽可能避免它们.我鼓励你重新考虑你的设计.