5

I have the following c# code, it does a check on permissions. I'm wondering if, when converted to f#, would computational expressions be a way to factor out the null checks.

bool ShouldGrantPermission(ISecurityService security, User user, Item item) {
  return user != null && item != null && user.Id == item.AuthorId
    && security.Does(user).Have(MyPermission).On(item);
}

I would like to note that the ISecurityService API currently returns false if any of the items are null. However it makes a database call, so the code here checks for null and then does the id check, because in most cases this will return false and avoid a database call.

Charles Lambert
  • 5,042
  • 26
  • 47

2 Answers2

8

You can define a computation builder that hides null checking, but it doesn't give you a very convenient syntax, so I probably wouldn't write it that way. It would be cool if there was some more lightweight syntax for this, because it would be quite useful. Also, the computation builder just propagates the null, so you would end with a result of type Nullable<bool>:

nullable { let! u = user
           let! i = item
           return u.Id == i.AuthorId && security.Does(user).Have(MyPermission).On(i) }

The idea is that the let! operation calls the rest of the computation only when the argument is not null. When it is null, it immediately returns null as the overall result.

I don't think there is much you could do to make the code nicer. Of course, if it was all written in F#, then none of the values could be null (because F# declared types do not permit the null value), but that's a different story.

Another approach in F# would be to declare an active pattern that matches only when a value is not null. This has the benefit that you won't have any variables that may have null value in the code, so there is no danger of using a wrong variable and getting NullReferenceException:

let shouldGrantPermission = function
  | NotNull(security:ISecurityService), NotNull(user), NotNull(item) ->
      security.Does(user).Have(MyPermission).On(item)
  | _ -> true

The declaration of the active pattern is:

let (|NotNull|_|) a = if a <> null then Some(a) else None

However, even this isn't really too much nicer than the direct equivalent of what you have. I guess dealing with null values is just pain :-). This article by Ian Griffiths has some related ideas, but again, none of them really solves the problem.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
1

I would make one slight adjustment to Tomas's answer: use Object.ReferenceEquals to do the null check instead of =. It's faster and, more importantly, you don't have to mark types declared in F# with the AllowNullLiteral attribute. I generally define an Interop module for F# code that will be used from C#. This isolates null handling, and since it doesn't require the use of [<AllowNullLiteral>], you can ignore null within F# and only deal with it at the point of interaction with C# (i.e., your public interface). Here's the module I use (copied from this answer):

[<AutoOpen>]
module Interop =

    let inline (===) a b = obj.ReferenceEquals(a, b)
    let inline (<=>) a b = not (a === b)
    let inline isNull value = value === null
    let inline nil<'T> = Unchecked.defaultof<'T>
    let inline safeUnbox value = if isNull value then nil else unbox value
    let (|Null|_|) value = if isNull value then Some() else None

type Foo() = class end

type Test() =
    member this.AcceptFoo(foo:Foo) = //passed from C#
        if isNull foo then nullArg "foo"
        else ...

    member this.AcceptFoo2(foo:Foo) = //passed from C#
        match foo with
        | Null -> nullArg "foo"
        | _ -> ...

    member this.AcceptBoxedFoo(boxedFoo:obj) =
        let foo : Foo = safeUnbox boxedFoo
        ...

    member this.ReturnFoo() : Foo = //returning to C#
        if (test) then new Foo()
        else nil

Snippet on fssnip.net.

Community
  • 1
  • 1
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • Along these lines, having a couple reference equals and not equals operators around is handy: `let inline (=&) a b = obj.ReferenceEquals(a,b)` and `let inline (<>&) a b = a =& b |> not` – Stephen Swensen May 09 '11 at 16:35
  • Ah, yes, I like to keep those handy myself, but I prefer the JavaScript-y `===` and `!==`. – Daniel May 09 '11 at 16:40
  • Cool, I was considering using those also – Stephen Swensen May 09 '11 at 16:50
  • 1
    The only problem with `!==` is that `!` doesn't mean "not" anywhere else in F#. Some might call it an abomination, but I almost think `<<>>` would be a more idiomatic companion to `===`. – Joel Mueller May 09 '11 at 17:28
  • @Joel: Yep, that's an abomination. :-) I settled on `===` and `<=>` until someone suggests something better. – Daniel May 09 '11 at 19:04
  • @Joel: The fish operator? :-) – Daniel May 09 '11 at 19:17
  • @Daniel - `<>=<` I fear it's only a matter of time until the "Doing Math with ASCII Art" blog post. – Joel Mueller May 09 '11 at 19:22
  • @Daniel, @Joel: I chose `=&` and `<>&` since `&` hearkens to the pass by reference operator `&`, and of course leading with `=` and `<>` is self-explanatory (and also ensures precedences and associativity are in line with the normal equals / not equals operators). – Stephen Swensen May 09 '11 at 20:36
  • 1
    @Stephen - I'm not really wild about how your operators look, but I can't fault your reasoning. – Joel Mueller May 09 '11 at 22:35
  • 2
    It's a shame F# doesn't support unicode operators! We could use for example `=•=` and `<•>` (bullet represents `null`, which is quite appropriate, because it is great for shooting yourself into the foot :-)). – Tomas Petricek May 10 '11 at 02:49
  • 1
    @Tomas: "It's a shame F# doesn't support unicode operators!". Amen! I'd love to be able to use ×, ±, ⊕, ⊗ and so on. Also ∂ in identifiers... – J D May 10 '11 at 21:35
  • 3
    @Jon - Then you could also sell a special keyboard with your libraries :-). – Tomas Petricek May 10 '11 at 22:20