Thomas
Thomas

Reputation: 12107

implementing an equivalent of the C#'s null test in F#

I'm using quite a lot this piece of code:

let inline (||>) (a: 'a option) (b: 'a -> unit) = if a.IsSome then b a.Value

so I can do things like

myData ||> DoSomethingWithIt

without having to test if myData is Some or None since there are many functions that don't generally need to test for an option. This avoid to put the test in the function itself.

I would like to extend this to methods of a type where I could do like C#'s:

myData?.DoSomethingWithIt

essentially replacing:

if myData.IsSome then myData.Value.DoSomethingWithIt

with some syntactic sugar.

but I have no idea how I could do the operator so that it allows to get access to the type's method in the expression. Is that possible in F#?

I'm also open to learn about why it could be a bad idea if it is :)

Upvotes: 1

Views: 91

Answers (4)

tranquillity
tranquillity

Reputation: 1685

Depending on your return type of DoSomethingWithIt the F# library offers a few standard functions for working with Options that can be turned into operators.

let x = Some 1

let aPrinter a = printfn "%i" a
let add1 a = a + 1

let (|?>) opt f = Option.iter f opt
let (|??>) opt f = Option.map f opt

x |?> aPrinter
let y = x |??> add1

You can also consider redefining your DoSomethingWithIt to work with an option by partial application.

let DoSomethingWithIt' = Option.iter DoSomethingWithIt 
let something' = Option.iter (fun (b:B) -> b.DoSomethingWithIt()) //For instance methods

That may end up being a lot of work depending how many functions you are dealing with. Ultimately you shouldn't try to hide the fact you are working with Options. By making something an Option you are telling the compiler that you aren't sure whether it exists or not. It is trying to help you by forcing you to deal with the None case. If there are lots of cases in your code where you know your Option is Some then there's probably a larger architectural issue in your code and you should try to lift all your Option<'T> to just T prior to doing work with them. e.g.:

let lift xs = 
    [
        for x in xs do 
            match x with
            | Some x -> yield x
            | None -> ()
    ]

Upvotes: 1

Piotr Rodak
Piotr Rodak

Reputation: 1941

I believe that the ?. operator in c# is a syntactic sugar that hides the if statement checking for null before invoking a member of the type. Even if you could make it work the way you plan, I feel that it would go against the FP principles and could cause more problems down the line.

The Option module contains probably most of what you need already. The iter function allows to call a function on the value of the Option if that value is present (Some).

If you have situation that your input parametes can be nulls, but not options, you can use the Option.ofObj function that will convert the parameter to an Option with Some if the parameter is not null, else None.

So assuming that your function DoSomethingWithit accepts a string and returns unit:

   let DoSomethingWithIt = //(string -> unit)
       printf "%s; "

You can use this more verbose syntax to (for example) iterate over nullable values in your list:

   let lst = [ "data"; "data 2"; null; "data3" ]
   lst
   |> List.iter (fun v -> v |> Option.ofObj |> Option.iter DoSomethingWithIt)

Alternatively you can compose the Optioni.ofObj and Option.iter DoSomethingWithIt functions and do something like

   let SafeDoSomethingWithIt = //(string -> unit)
      Option.ofObj >> Option.iter DoSomethingWithIt 

This gives you safe invocation:

   let lst2 = [ "data"; "data 2"; null; "data3" ]
   lst2
   |> List.iter SafeDoSomethingWithIt

You can generalize the combination of the functions returning unit (but not only)

   let makeSafe fn =
      Option.ofObj >> Option.iter fn

Then you can create a series of safe functions:

   let SafeDoSomethingWithIt = makeSafe DoSomethingWithIt 
   
   let safePrint = makeSafe (printf "%s; ")

   //...etc

Then this still works:

   lst2
   |> List.iter SafeDoSomethingWithIt
   
   lst2
   |> List.iter safePrint

You can also write a wrapper for functions returning values using Option.bind function.

   let makeSafeReturn fn = //(string -> string option)
       Option.ofObj >> Option.bind fn

Upvotes: 1

JL0PD
JL0PD

Reputation: 4498

There is no analogical syntax for such constructions but F# have alternatives. The easiest way is to use FSharpx.Extras library and FSharpx.Option.maybe computation expression which will allow you to use Option related operations.

open FSharpx.Option

let a = Some 1
let b = maybe {
    let! v = a
    return v + 3
} // b is (Some 4)

let c : int option = None
let d = maybe {
    let! v = c
    return v + 3 // this line won't be reached
} // d is None

Upvotes: 1

user1981
user1981

Reputation: 586

Have a look at Option.iter. It has the same signature as your operator.

Upvotes: 1

Related Questions