Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a standard implementation of F# defaultArg accepting lazy?

Tags:

f#

I need an implementation of defaultArg function with Lazy as second parameter. Here is a usage example:

defaultArgLazy option (lazy doSomeHeavyWorkThatShouldBeAvoided())

It's quite easy to implement

let defaultArgLazy o (p:'a Lazy) = 
  match o with 
  | Some v -> v 
  | None -> p.Force()

But I wonder if there is a standard implementation that I'm missing.

like image 260
Mikhail Shilkov Avatar asked Oct 30 '22 04:10

Mikhail Shilkov


2 Answers

I don't know of anything built in, but here's a more general version of your function:

module Option =
    let lazyDefault f opt =
        match opt with
        | Some x -> x
        | None -> f()

let lazyVal = lazy 1
let opt = None

opt |> Option.lazyDefault lazyVal.Force  // returns 1

It takes any function of (unit -> 'a) instead of specifically a Lazy<'a>', so you just pass through Lazy's Force method instead, and retain the guarantee that the evaluation only happens once.

Perhaps Lazy.Force() was added in F# on top of .NET's existing Lazy.Value so that you could use it more idiomatically with higher order functions.

Note that I have swapped the parameter order compared to the built-in defaultArg to be more consistent with functions in other modules and more convenient for piping/currying.

like image 160
TheQuickBrownFox Avatar answered Jan 04 '23 14:01

TheQuickBrownFox


If the lazy value is only used at a later point in code, it might also be an idea to use defaultArg and pass it a Lazy<'a> option as a first argument. That way choosing the option or the default argument is separated from when the result is evaluated.

Taking this idea, the function

let defaultArgLazy opt lazyDef =
  (defaultArg (Option.map Lazy.CreateFromValue opt) lazyDef).Value

would do what you want - as does the one by TheQuickBrownFox.

like image 32
Mathias Körner Avatar answered Jan 04 '23 15:01

Mathias Körner