F# inferred types in If/Then - f#

If I have the following function:
let myFunc x y =
if y = 0 then 1
x
I get the error:
Program.fs(58,17): error FS0001: This expression was expected to have type
unit
but here has type
int
Why does the compiler expect 'unit' instead of int ?

It might worth adding that this is not just a property of if. F# is an expression-based language meaning that pretty much every piece of code (aside from type declarations and a few exceptions) is an expression that evaluates to some result. In fact, F# does not call if the if statement, but an if expression.
This means that you can use if in unexpected places. For example, this might be useful:
x/2 + (if x%2=0 then 0 else 1)
As already explained by Garry, if you omit else, then the expression still needs to return something - if the result was to be an int, then it would not really make sense (which number should the compiler pick?), so it requires that the result is of type unit, which is a special type representing "no result".
The unit type is also the result of all imperative functions (e.g. printf) or of all expressions that do not logically return any value (assignment or e.g. loop). This means that if you write:
if x > 0 then printfn "Big!"
... then the expression is well-typed, because printfn "Big!" has a return type unit and the implicitly added else branch also returns unit. You can create a value of type unit directly by hand (the type has exactly one value), so the above actually corresponds to:
if x > 0 then printfn "Big!" else ()
From the C# perspective, it makes more sense to read if .. then .. else as the conditional operator:
x/2 + (x%2 == 0 ? 0 : 1)

In F# when using an if statement when there is no else branch then it implicitly returns unit. If your then branch returns a type other than unit you must have an explicit else branch for it to work correctly. In your example you could write:
let myFunc x y = if y = 0 then 1 else x
MSDN - http://msdn.microsoft.com/en-us/library/dd233231.aspx

Related

F# kprintf: missing warning about about redundant arguments

Is there a way to do string formatting for Exceptions with kprintf (or similar) and still get warnings about redundant arguments? So far I have:
type MyException (s:string) =
inherit System.Exception(s)
static member Raise msg =
Printf.kprintf (fun s -> raise (MyException(s))) msg
do
MyException.Raise "boom %d" 9 1 // Gives NO warning about redundant arguments
failwithf "boom %d" 9 1 // Gives warning about redundant arguments
I know I could use the new $ formatting but would like to stay compatible with older versions of F#.
The problem does not come from kprintf, but from raise, which allows the return type of the function to be any type, including a curried function. The function failwithf has this warning as a special case.
If you return, let’s say, a string or a unit, you would actually get an error. The downside is that now you cannot raise in every position anymore, as then the return argument is not generic anymore.
You could fix that by forcing a type argument, but that would make it less than ideal for the caller.
For instance, this works, but if you change the return type of f to be a string, it fails:
open System
type MyException (s:string) =
inherit System.Exception(s)
static member Raise msg =
Printf.kprintf (fun s -> raise (MyException s) |> ignore) msg
module Test =
let f() : unit = // string won’t work
MyException.Raise "boom %d" 9 10 // error, as expected
Edit: workaround
Here's some kind of a workaround. You can use generic type constraints to limit the type, and use the fact that an F# function type is not comparable or equatable, does not have a proper value null.
Most types you'll use are equatable, because of F#'s powerful structural equality support. Other options for a type constraint are struct (only structs), null (this excludes record and DU types) or a certain inheritance constraint if that serves your use case.
Example:
type MyException (s:string) =
inherit System.Exception(s)
static member Raise<'T when 'T : equality> msg =
let raiser s =
raise (MyException s)
Unchecked.defaultof<'T> // fool the compiler
Printf.kprintf raiser msg
module Test =
let f() = MyException.Raise "boom %d" 9
let g() = MyException.Raise "boom %d" 9 10 // error
Downside, apart from the type restriction, is that the error you get will be a type error, not a warning as you requested.
The check for this warning in the compiler specifically looks for a number of known F# library functions. You can see the checks in the compiler source code and here (the functions are raise, failwith, failwithf, nullArg, invalidOp and invalidArg).
The reason for this is that, there is, in principle, nothing wrong with a generic function that returns 'T and is used so that 'T is inferred to be a function. For example, it is perfectly fine to do:
let id f = f
id sin 3.14
Here, we define a function id with one argument, but if I give it a function as an argument, it also becomes valid to call it with an extra argument as id sin 3.14.
The special functions are special in that they look like they return a value of a generic type, but they never actually return, because they raise an execption. The compiler cannot, in general, know whether that is the case for any custom function or method you yourself write - and so it only checks this for known special functions. (It would have to do more sophisticated analysis, possibly marking the return type of these functions as the bottom type, but that is beyond what F# does...).

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

I was attempting to convert this to F# but I can't figure out what I'm doing wrong as the error message (in title) is too broad of an error to search for, so I found no resolutions.
Here is the code:
let getIP : string =
let host = Dns.GetHostEntry(Dns.GetHostName())
for ip in host.AddressList do
if ip.AddressFamily = AddressFamily.InterNetwork then
ip.ToString() // big fat red underline here
"?"
A for loop in F# is for running imperative-style code, where the code inside the for loop does not produce a result but instead runs some kind of side-effect. Therefore, the expression block in an F# for loop is expected to produce the type unit, which is what side-effect functions should return. (E.g., printfn "Something" returns the unit type). Also, there's no way to exit a for loop early in F#; this is by design, and is another reason why a for loop isn't the best approach to do what you're trying to do.
What you're trying to do is go through a list one item at a time, find the first item that matches some condition, and return that item (and, if the item is not found, return some default value). F# has a specialized function for that: Seq.find (or List.find if host.AddressList is an F# list, or Array.find if host.AddressList is an array. Those three functions take different input types but all work the same way conceptually, so from now on I'll focus on Seq.find, which takes any IEnumerable as input so is most likely to be what you need here).
If you look at the Seq.find function's type signature in the F# docs, you'll see that it is:
('T -> bool) -> seq<'T> -> 'T
This means that the function takes two parameters, a 'T -> bool and seq<'T> and returns a 'T. The 'T syntax means "this is a generic type called T": in F#, the apostrophe means that what follows is the name of a generic type. The type 'T -> bool means a function that takes a 'T and returns a Boolean; i.e., a predicate that says "Yes, this matches what I'm looking for" or "No, keep looking". The second argument to Seq.find is a seq<'T>; seq is F#'s shorter name for an IEnumerable, so you can read this as IEnumerable<'T>. And the result is an item of type 'T.
Just from that function signature and name alone, you can guess what this does: it goes through the sequence of items and calls the predicate for each one; the first item for which the predicate returns true will be returned as the result of Seq.find.
But wait! What if the item you're looking for isn't in the sequence at all? Then Seq.find will throw an exception, which may not be the behavior you're looking for. Which is why the Seq.tryFind function exists: its function signature looks just like Seq.find, except for the return value: it returns 'T option rather than 'T. That means that you'll either get a result like Some "ip address" or None. In your case, you intend to return "?" if the item isn't found. So you want to convert a value that's either Some "ip address or None to either "ip address" (without the Some) or "?". That is what the defaultArg function is for: it takes a 'T option, and a 'T representing the default value to return if your value is None, and it returns a plain 'T.
So to sum up:
Seq.tryFind takes a predicate function and a sequence, and returns a 'T option. In your case, this will be a string option
defaultArg takes a 'T option and a default value, and returns a normal 'T (in your case, a string).
With these two pieces, plus a predicate function you can write yourself, we can do what you're looking for.
One more note before I show you the code: you wrote let getIP : string = (code). It seems like you intended for getIP to be a function, but you didn't give it any parameters. Writing let something = (code block) will create a value by running the code block immediately (and just once) and then assigning its result to the name something. Whereas writing let something() = (code block) will create a function. It will not run the code block immediately, but it will instead run the code block every time the function is called. So I think you should have written let getIP() : string = (code).
Okay, so having explained all that, let's put this together to give you a getIP function that actually works:
let getIP() = // No need to declare the return type, since F# can infer it
let isInternet ip = // This is the predicate function
// Note that this function isn't accessible outside of getIP()
ip.AddressFamily = AddressFamily.InterNetwork
let host = Dns.GetHostEntry(Dns.GetHostName())
let maybeIP = Seq.tryFind isInternet host.AddressList
defaultArg maybeIP "?"
I hope that's clear enough; if there's anything you don't understand, let me know and I'll try to explain further.
Edit: The above has one possible flaw: the fact that F# may not be able to infer the type of the ip argument in isInternet without an explicit type declaration. It's clear from the code that it needs to be some class with an .AddressFamily property, but the F# compiler can't know (at this point in the code) which class you intend to pass to this predicate function. That's because the F# compiler is a single-pass compiler, that works its way through the code in a top-down, left-to-right order. To be able to infer the type of the ip parameter, you might need to rearrange the code a little, as follows:
let getIP() = // No need to declare the return type, since F# can infer it
let host = Dns.GetHostEntry(Dns.GetHostName())
let maybeIP = host.AddressList |> Seq.tryFind (fun ip -> ip.AddressFamily = AddressFamily.InterNetwork)
defaultArg maybeIP "?"
This is actually more idiomatic F# anyway. When you have a predicate function being passed to Seq.tryFind or other similar functions, the most common style in F# is to declare that predicate as an anonymous function using the fun keyword; this works just like lambdas in C# (in C# that predicate would be ip => ip.AddressFamily == AddressFamily.InterNetwork). And the other thing that's common is to use the |> operator with things like Seq.tryFind and others that take predicates. The |> operator basically* takes the value that's before the |> operator and passes it as the last parameter of the function that's after the operator. So foo |> Seq.tryFind (fun x -> xyz) is just like writing Seq.tryFind (fun x -> xyz) foo, except that foo is the first thing you read in that line. And since foo is the sequence that you're looking in, and fun x -> xyz is how you're looking, that feels more natural: in English, you'd say "Please look in my closet for a green shirt", so the concept "closet" comes up before "green shirt". And in idiomatic F#, you'd write closet |> Seq.find (fun shirt -> shirt.Color = "green"): again, the concept "closet" comes up before "green shirt".
With this version of the function, F# will encounter host.AddressList before it encounters fun ip -> ..., so it will know that the name ip refers to one item in host.AddressList. And since it knows the type of host.AddressList, it will be able to infer the type of ip.
* There's a lot more going on behind the scenes with the |> operator, involving currying and partial application. But at a beginner level, just think of it as "puts a value at the end of a function's parameter list" and you'll have the right idea.
In F# any if/else/then-statement must evaluate to the same type of value for all branches. Since you've omitted the else-branch of the expression, the compiler will infer it to return a value of type unit, effectively turning your if-expression into this:
if ip.AddressFamily = AddressFamily.InterNetwork then
ip.ToString() // value of type string
else
() // value of type unit
Scott Wlaschin explains this better than me on the excellent F# for fun and profit.
This should fix the current error, but still won't compile. You can solve this either by translating the C#-code more directly (using a mutable variable for the localIP value, and doing localIP <- ip.ToString() in your if-clause, or you could look into a more idiomatic approach using something like Seq.tryFind.

what does this error mean, and how to fix it?

Here's my code
let rec Interest a b c =
if (c=0) then b else Interest(a ((1.0+a)*b) (c-1));;
The error is:
if (c=0) then b else Interest(a ((1.0+a)*b) (c-1));;
-------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stdin(2,26): error FS0001: Type mismatch. Expecting a
'a but given a
'a -> int -> 'a The resulting type would be infinite when unifying ''a' and ''a -> int -> 'a'
>
You've defined Interest as a function that takes three arguments but what you pass doesn't match that. The way it's written, you're only passing in a single argument to the recursive call to Interest - the parenthesized expression a ((1.0=a)*b) (c-1). Here, a is inferred to be a function of two arguments, ((1.0=a)*b) and (c-1).
Long story short, this throws off the type inference algorithm, until it gives up saying that it can't get a hang of what type to give to Interest.
What you want is this:
let rec Interest a b c =
if (c=0) then b else Interest a ((1.0=a)*b) (c-1)
You'll also have a problem with (1.0=a). This evaluates to a boolean value that you later want to multiply with b. Not sure what the intent is, but you might be looking for something like (if 1.0 = a then 1 else 0)?
Unlike C-like languages that support implicit conversions between "bools" and ints, F# requires you to make all your conversions explicit to ensure correctness (this goes for converting between numeric types as well).

Postfix conditional operator in F#

In Perl language one can write something like
someFunction() if $x == 0
i.e. apply condition in postfix notation.
I was sure there must be similar type of expression in F#, since it is so flexible in working with functions. But when I try to write
someFunction() if x = 0
or
someFunction() << if x = 0
I recieve an expected error message. Is there any way to implement more or less general postfix conditional operator in F#?
There is no built-in F# support for this and so I would recommend just using the normal prefix style conditionals (or pattern matching). There is also no standard operator/function that would be common in the community, so the idiomatic style would just use ordinary if.
I think that the best you can do is something like this:
/// Evaluates lazy value only if the specified boolean holds
let assuming b (v:Lazy<_>) = if b then v.Value
// Print foo only when x > 40
let foo() = printfn "hi"
let x = 42
lazy foo() |> assuming (x > 40)
Note that I had to add lazy to the expression (to make sure it is not actually evaluated when the condition does not holds). This works, but it is certainly uglier than writing if x>40 then foo() - but it is a fun thing to experiment with :-)

Break a statement (expression) into multiple lines: how to indent

I found it's very hard to search for the simple indentation guide in F#.
Basically, I am wondering what's the rule for multiple-line statement indentation.
In C#, there is no problem because whitespace doesn't count.
Although I can write F# code according to my intuition and it works, I really want to know what's the rule for breaking one statement into multiple lines.
I write as
printfn "%d"
1
It works as expected
And if I write them in the same column, something goes wrong.
>
printfn "%A%A"
1
[];;
> //nothing is returned... and no error in this case
I want to confirm the basic rule for doing this. It's a little annoying when you can't be sure what you are doing.
Thanks in advance
I just tried another case
List.iter
(printfn "%d")
[1..10];;
And it prints out 1 to 10.
Why it's not
List.iter
((printfn "%d")
[1..10]);;
As Yin points out, the rule is that arguments of a function should be indented further than the call to the function. To add more details, your first snippet is interpreted like this:
printfn "%A%A";
1;
[];
Each of these is a valid expression that returns something (function, number, empty list) and then ignores the result and continues. Because they are written in the top-level scope, F# Interactive doesn't emit a warning that you're ignoring some values. If they were in a do block or let declaration:
do
printfn "%A%A"
1
[]
The F# compiler would emit a warning when sequencing expressions (using ;) that do not return unit:
stdin(5,3): warning FS0193: This expression is a function value, i.e. is missing arguments. Its type is 'a -> 'b -> unit.
stdin(6,3): warning FS0020: This expression should have type 'unit', but has typ
e 'int'. Use 'ignore' to discard the result of the expression, or 'let' to bind
the result to a name.
stdin(5,3): warning FS0020: This expression should have type 'unit', but has typ
e ''a list'. Use 'ignore' to discard the result of the expression, or 'let' to b
ind the result to a name.
In your second example, you should indent:
>
printfn "%A%A"
1
[];;
Otherwise the three expressions are three sequential expressions, not a single expression.
You can refer F# Language Specification for firm rules, e.g. Chapter 15 in the specification.

Resources