Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I reduce code duplication with nested 'if' statements?

Tags:

f#

let's consider this code:

let getBuildDate (assembly: Assembly) : DateTime option =

    let buildVersionMetadataPrefix = "+build"
    let attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()

    if attribute <> null && attribute.InformationalVersion <> null then
        let value = attribute.InformationalVersion
        let index = value.IndexOf(buildVersionMetadataPrefix)
        if index > 0 then
            let value = value.Substring(index + buildVersionMetadataPrefix.Length)
            let success, timestamp = DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None)
            if success then
                Some timestamp
            else
                None
        else
            None
    else
        None

Is there a way to get rid of all the 'else None' statements to have only one?

On one side, I can imagine that for some people the code is more clear with all the None statements spelled out, but on the other side, coming from the C world, I see it as clutter that reduces readability.

There are many cases where you need a series of conditions to be met and all the failed cases go to one place.

If I have a list of conditions that depend on each others' success, how can I make a concise short exit without duplication.

like image 367
Thomas Avatar asked Mar 09 '20 12:03

Thomas


1 Answers

Another approach might be to use the Option functions - each of these steps will effectively short circuit if the input from the previous step is None.

let getBuildDate (assembly: Assembly) : DateTime option =    
    let tryDate value =
         match DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None) with
         | true, date -> Some date
         | false, _ -> None

    let buildVersionMetadataPrefix = "+build"
    let attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()

    Option.ofObj attribute
    |> Option.bind (fun attr -> Option.ofObj attr.InformationalVersion)
    |> Option.map (fun infVer -> infVer, infVer.IndexOf buildVersionMetadataPrefix)
    |> Option.filter (fun (_, index) -> index > 0)
    |> Option.map (fun (infVer, index) -> infVer.Substring(index + buildVersionMetadataPrefix.Length))
    |> Option.bind tryDate

Whether this is 'better' is arguable - and definitely a matter of opinion!

like image 181
Charles Mager Avatar answered Nov 15 '22 10:11

Charles Mager