F# equivalent to Kotlin's ?. operator

Viewed 141

I just started my first F# project and coming from the JVM world, I really like Kotlin's nullability syntax and was wondering how I could achieve similarily compact syntax in F#.

Here's an example:

class MyClass {
    fun doSomething() {
        // ...
    }
}

// At some other place in the code:
val myNullableValue: MyClass? = null
myNullableVallue?.doSomething()

What this does:

  1. If myNullableValue is not null, i.e. there is some data, doSomething() is called on that object.
  2. If myNullableValue is null (like in the code above), nothing happens.

As far as I see, the F# equivalent would be:

type MyClass = 
    member this.doSomething() = ()

type CallingCode() = 
    let callingCode() = 
        let myOptionalValue: MyClass option = None
        match myOptionalValue with 
        |Some(x) -> x.doSomething()
        |None -> ()

A stamement that is 1 line long in Kotlin is 3 lines long in F#. My question is therefore whether there's a shorter syntax that acomplishes the same thing.

2 Answers

There is no built-in operator for doing this in F# at the moment. I suspect that the reason is that working with undefined values is just less frequent in F#. For example, you would never define a variable, initialize it to null and then have some code that may or may not set it to a value in F#, so the usual way of writing F# eliminates many of the needs for such operator.

You still need to do this sometimes, for example when using option to represent something that can legitimately be missing, but I think this is less frequent than in other languages. You also may need something like this when interacting with .NET, but then it's probably good practice to handle nulls first, before doing anything else.

Aside from pattern matching, you can use Option.map or an F# computation expression (there is no standard one, but it's easy to use a library or define one - see for example). Then you can write:

let myOptionalValue: MyClass option = None

// Option #1: Using the `opt` computation expression
opt { let! v = myOptionalValue 
      return v.doSomething() }

// Option #2: Using the `Option.map` function
myOptionalValue |> Option.map (fun v -> v.doSomething() )

For reference, my definition of opt is:

type OptionBuilder() =
  member x.Bind(v,f) = Option.bind f v
  member x.Return v = Some v
  member x.ReturnFrom o = o
  member x.Zero () = None

let opt = OptionBuilder()
Related