Search code examples
lambdaf#

do `fun` lambda expressions have shorthand syntax?


i want to make sure that i am not missing out on readable-but-terse F# syntax: is my use of fun below too verbose?

let toCamelCase word indexes =
    let mapping i c =
        match (indexes |> List.contains i) with
        | true                      -> Char.ToUpper(c)
        | _ when Char.IsUpper(c)    -> Char.ToLower(c)
        | _                         -> c

    word |> String.mapi mapping

[
    ("fsharP", [0; 1])
    ("nAtiveinterop", [0; 6])
    ("taskbuildereXtensions", [0; 4; 11])
    ("microsoftword", [0; 9])
]
|> List.map (fun (word, indexes) -> (word, indexes) ||> toCamelCase)

also, do let me know whether there can be improvements elsewhere in the code above


Solution

  • Wanted to add an answer that doesn't focus on currying/uncurrying as much. This touches on some points brought up by others here already, but hopefully some more details will be helpful.

    Regarding your question, you need the fun keyword if you want to pass in a lambda function. You could avoid this by changing your function signature:

    let toCamelCase (word, indexes) =
        let mapping i c =
            match (indexes |> List.contains i) with
            | true                      -> Char.ToUpper(c)
            | _ when Char.IsUpper(c)    -> Char.ToLower(c)
            | _                         -> c
    
        word |> String.mapi mapping
    
    [
        ("fsharP", [0; 1])
        ("nAtiveinterop", [0; 6])
        ("taskbuildereXtensions", [0; 4; 11])
        ("microsoftword", [0; 9])
    ]
    |> List.map toCamelCase
    

    If you were using a function you couldn't make this change to for whatever reason you could create an intermediate helper function (this is effectively manual uncurrying):

    let toCamelCase word indexes =
        let mapping i c =
            match (indexes |> List.contains i) with
            | true                      -> Char.ToUpper(c)
            | _ when Char.IsUpper(c)    -> Char.ToLower(c)
            | _                         -> c
    
        word |> String.mapi mapping
    
    let toCCHelper (word, indexes) =
        toCamelCase word indexes
    
    [
        ("fsharP", [0; 1])
        ("nAtiveinterop", [0; 6])
        ("taskbuildereXtensions", [0; 4; 11])
        ("microsoftword", [0; 9])
    ]
    |> List.map toCCHelper
    

    You could choose to simplify your lambda function slightly and change nothing else. This works because the double pipe (||>) will deconstruct the tuple input for you:

    let toCamelCase word indexes =
        let mapping i c =
            match (indexes |> List.contains i) with
            | true                      -> Char.ToUpper(c)
            | _ when Char.IsUpper(c)    -> Char.ToLower(c)
            | _                         -> c
    
        word |> String.mapi mapping
    
    [
        ("fsharP", [0; 1])
        ("nAtiveinterop", [0; 6])
        ("taskbuildereXtensions", [0; 4; 11])
        ("microsoftword", [0; 9])
    ]
    |> List.map (fun x -> x ||> toCamelCase)
    

    There are also some parenthesis that are not needed, so it is up to your preferences. Here's some cleanup there as well as some logic changes for your consideration:

    let toCamelCase (word, indexes) =
        let mapping i c =
            // You can omit parens here  if you want:
            match indexes |> List.contains i with
            // This logic might be easier to maintain, no parens needed here:
            | true  -> Char.ToUpper c
            | false -> Char.ToLower c
    
        word |> String.mapi mapping
    
    // The parens here are also optional when you're putting 1 entry per line
    // (1 tuple being 1 entry in this case):
    [
        "fsharP", [0; 1]
        "nAtiveinterop", [0; 6]
        "taskbuildereXtensions", [0; 4; 11]
        "microsoftword", [0; 9]
    ]
    |> List.map toCamelCase