Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# lambda expression with multiple statements

Tags:

f#

I'm learning F# and finding I'm writing things that work but which I don't entirely understand. Here's an example

let processArgs args =
    match args with
    | null
    | [||]          -> [fun() -> getCredentials(); home 20; mentions 20; messages 20]
    | [|"-h"|] 
    | [|"-?"|]      -> [showHelp]
    | [|"-reset"|]  -> [clearAllSettings]
    | _             -> [fun() -> printfn "%s" (String.Join(" ", args))]

[<EntryPoint>]
    let main (args:string[]) =
    try
        let actions = processArgs args
        List.iter (fun action -> action()) actions
        0
    finally
        Console.ResetColor()
        Console.CursorVisible <- true

The methods getCredentials, home, mentions, messages, showHelp, and clearAllSettings are all simple functions and do what you expect. (Yes, it's a twitter client, isn't that the new 'Hello World' demo?)

The line:

[fun() -> getCredentials(); home 20; mentions 20; messages 20]

works like I want. It calls getCredentials, then home, then mentions, then messages

From my perspective, the semicolon is acting like a statement separator. I haven't seen this described before. Is that what is going on here?

Is there a more idiomatic way of writing this (in other words, would a seasoned F# programmer roll on the floor laughing when he/she saw this)?

Further information: My original intent was to have a list of actions and then add actions as I discover options. In C# I would typically do this with List< Action >(). The semicolon thing surprised me because originally I tried to write it like:

[getCredentials; home 20; mentions 20; messages 20]

But the compiler didn't like it.

like image 937
Mike Ward Avatar asked May 13 '12 16:05

Mike Ward


1 Answers

When you write:

[fun() -> getCredentials(); home 20; mentions 20; messages 20]

the compiler creates a list with only one element which is a function of type unit -> unit. S1 ; S2 is sequence composition when S1 has type of unit, and S1 and S2 are executed in order and S2's result is returned. Therefore, three functions home, mentions and messages actually have the signature int -> unit.

If you want to create a list of 4 different functions, it should be:

[ getCredentials; // ; is optional
  fun () -> home 20; 
  fun () -> mentions 20; 
  fun () -> messages 20 ]

These functions are separated by whitespace to avoid confusion of using ; as list delimiter and sequence composition.

Since your example has all lists with only one elements, it could be simplified a lot:

let processArgs = function
    | [||]          -> getCredentials(); home 20; mentions 20; messages 20
    | [|"-h"|] 
    | [|"-?"|]      -> showHelp()
    | [|"-reset"|]  -> clearAllSettings()
    | args          -> printfn "%s" (String.Join(" ", args))

[<EntryPoint>]
let main (args:string[]) =
  try
    processArgs args
    0
  finally
    Console.ResetColor()
    Console.CursorVisible <- true
like image 89
pad Avatar answered Nov 04 '22 03:11

pad