Search code examples
functionf#booleanfor-in-loop

How do I make this F# Function properly return a boolean value while using a For In statement?


Sorry if my title isn't descriptive enough, I wasn't sure how to phrase the problem... I'm used to programming in C# and have been dabbling in F#. I'm trying to write a function I use in some C# scripts that checks if the string is numeric. I have a F# function written up like this though its not correct according to VS because its expecting an else:

let IsNumeric (entry : string) : bool =     // Function to make sure the data entered is numeric
    for c : char in entry do
        if not ((c >= '0') && (c <= '9')) then
            false
    true

If I put in an else statement and remove the true at the bottom:

let IsNumeric (entry : string) : bool =     // Function to make sure the data entered is numeric
    for c : char in entry do
        if not ((c >= '0') && (c <= '9')) then
            false
        else true

I get this error

FS0001 This expression was expected to have type 'bool' but here has type 'unit'

... if I keep the true at the bottom like in the first code block I get a warning about it returning a bool but should be ignored which I don't quite understand.

FS0020 The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.

This is the C# method I wrote that I've been trying to adapt:

public static bool IsNumeric(string s) //this just makes sure the string is numeric.
{
    foreach (char c in s)
    {
        if (!(c >= '0' && c <= '9') && c != '.' && c != '-')
        {
            return false;
        }

    }
    return true;
}

How should I approach this?


Solution

  • F# as most functional languages doesn't support early return. Last evaluated expression is return value.

    Canonical solution would be to use recursive function, which compiles to simple loop if you do it right.

    let isNumeric (str: string) : bool =
        let rec loop index =
            if index >= str.Length then
                true
            else
                let c = str.[index]
                if (c >= '0' && c <= '9') || c = '.' || c = '-' then
                    loop (index + 1)
                else false
        loop 0  
    

    You can check what it compiles to with ILSpy, ILDasm or SharpLab

    Most F#-ish is to use

    let isNumeric str =
        String.forall (fun c -> (c >= '0' && c <= '9') || c = '.' || c = '-') str
    

    And most correct is to use, because you don't check correctness and ..- would be correct number

    let isNumeric (str: string) = System.Double.TryParse str |> fst