Reputation: 265
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
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
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