Francisco Berrocal
Francisco Berrocal

Reputation: 265

What is the best way to do extensive null checks in F# when calling C# code

I would like to know the best way to interact with C# code from F# in a functional way, when I have to check null many times.

From C#, is easy because I have null operator

public bool Authorize(DashboardContext dashboardContext)
{
    var context = new OwinContext(dashboardContext.GetOwinEnvironment());
    var user = context.Authentication.User;
    return user?.Identity?.IsAuthenticated ?? false;
}

From F#, I made this

let authorize (ctx:DashboardContext) =
    match OwinContext(ctx.GetOwinEnvironment()) with
    | null -> false
    | c -> match c.Authentication.User with
            | null -> false
            | user -> user.Identity.IsAuthenticated

But I am not happy with this. What is the functional way for doing this right? I thought maybe some computation expression would help, but I don't know how to accomplish yet.

Upvotes: 8

Views: 502

Answers (2)

Melvyn
Melvyn

Reputation: 660

I liked TheQuickBrownFox's idea of using Option.ofObj, but I still found it tedious to write with the built-in functions, so I wrote a custom operator to chain null checks:

let inline (>>?) f g = f >> Option.bind (g >> Option.ofObj)

With this operator, what in C# would be

user?.Identity?.IsAuthenticated ?? false

Becomes:

user
|> (Option.ofObj
    >>? (fun x -> x.Identity)
    >>? (fun x -> x.IsAuthenticated)
    >> Option.defaultValue false)

Which is much prettier!

Disclaimer: I've been using F# for maybe two or three weeks total, so this might be completely stupid :)

Upvotes: 1

TheQuickBrownFox
TheQuickBrownFox

Reputation: 10624

Option.ofObj will convert a nullable object into an Option. Then you can use the helpers already defined in the Option module. For example, part of the pattern that you've written there is already encapsulated by Option.bind.

let authorize (ctx:DashboardContext) =
    ctx.GetOwinEnvironment() |> OwinContext |> Option.ofObj
    |> Option.bind (fun c -> c.Authentication.User |> Option.ofObj)
    |> Option.map  (fun user -> user.Identity.IsAuthenticated)
    |> Option.defaultValue false

Option.bind takes an Option<'a> and a function that takes the type 'a and returns an Option<'a>. When it is used in a pipeline it's a way of "mapping" a Some or filtering it out into a None.

I would say that the function you wrote looks fine actually, but this way might be considered a little bit more idiomatic, though it's arguably also a bit harder to follow in this example. Option.bind really comes into its own when it saves multiple levels of nesting.

It's worth noting that in both your F# function and mine we're assuming non-nullness of the Authentication and Identity properties and risking null reference exceptions when accessing their properties. That's in contrast to the C# method using null propagation. There isn't currently a built-in way to do that in F# but there are probably some advanced methods for simulating it.

It is also possible to do this with computation expressions. See the MaybeBuilder here.

Upvotes: 11

Related Questions