This question already has answers here:
F#: Why can't I use optional parameters in loose functions?
(2 answers)
Closed 7 years ago.
Ok I am very new to F# so maybe this has been well explained in the textbooks but why F# function with curried arguments cannot have optional parameter. For instance function Fun1 doesn't compile whereas the other two functions compile fine. I want to know what's reason for having this constraint on optional parameter? And is there, if any, way to get around this constraint?
type TestOptionalParameter =
member x.Fun1 (a: int) (?b : bool) =
if defaultArg b true then a else 2 * a
member x.Fun2 (?b : bool) =
if defaultArg b true then "yes" else "no"
member x.Fun3 (a: int, ?b : bool) =
if defaultArg b true then a else 2 * a
Let's look at an example if it would be allowed:
let top = TestOptionalParameter()
let fn1 = top.Fun1 4
What in this case would be the value of fn1: function of type (bool -> int) or the result of method Fun1 call with default second parameter value (false)?
I think because of this ambiguity functions with curried arguments are not allowed to have optional parameters
I guess that this is a design decision taken in F# since it is possible to have optional parameters in curried functions in OCaml.
Optional parameters in F# are implemented through the option type, assuming that you have a function of (for example) int -> bool option -> int, even if you have some special annotation for an optional parameter version, e.g. int -> ?bool -> int I think it could become more difficult to reason about how these functions behave in actual usage, especially in the presence of partial application because the number of arguments won't always match what's implied by the type signature.
This answer could be of interest to you with respect to some of the downsides of this approach as implemented in OCaml: https://stackoverflow.com/a/23703761/5438433
There is a suggestion to implement optional parameters in let bound functions on the F# Language Design User Voice site: https://fslang.uservoice.com/forums/245727-f-language/suggestions/5663215-optional-and-named-parameters-on-let-bindings-on-m, it's not clear to me whether, if accepted, this would implement the same behaviour as currently available for optional parameters on types or implement the OCaml-like behaviour for curried arguments.
Related
Let's assume this code:
let ToLower x = (string x).ToLower()
I can call it with:
ToLower 3
or:
ToLower 3.0
but not both since the first caller defines the type.
so, I did this change, with my C# knowledge of generics:
let ToLower (x : 'a) = (string x).ToLower()
but same problem, it looks like the first caller is specializing the generic and that's it.
I'm looking for a solution where the compiler will generate n versions of the assignment, as needed based on the use cases.
What is the proper way to achieve this in F#?
F# type inference uses constraint solving where the use of a function later in the code can affect the type.
This is however only ever a problem when you are using some built-in primitives such as + or string, which are generic, but have special meaning for certain types. For those, you can use inline (in which case, the code gets inlined and the compiler can handle the special meanings in the place where they are used). If you do not use inline, the compiler will fix the type when you first use the function.
If you just define the function and never use it anywhere, then you get a type obj -> string. This is because the compiler used obj as the default type when there were no other constraints.
If you define the function and call it with ToLower 3, the compiler adds a constraint that restricts the argument type to be int (but then you cannot use it with anything else).
The case of string is a bit odd, because you can convert any value to string - but if you want to do this for any value, you have to box it first. Boxing is something that can be done on a generic value, so in this case, you can define this as a generic function:
let ToLower (x:'a) = (string (box x)).ToLower()
ToLower 3.0
ToLower 3
This works because the type of box is 'a -> obj without any other caveats (unlike the type of string which is 'a -> string, but with special handling of certain type parameters).
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.
F# allows overloaded functions to differ only by an optional parameter, for example:
type MyClass() =
member this.func(a: string, b:string) = "func(a,b)"
member this.func(a: string, ?b:string) = "func(a,?b)"
How would you call the first function?
I don't think there is a sensible way to call the first function if the two overloaded functions differ only by an optional parameter. As mentioned in the comments, using this is probably a poor design and you should rename the parameters.
As you probably noticed, when you try calling the function in an ordinary way using MyClass().func("A","B"), you get an error message complaining about the ambiguity:
error FS0041: A unique overload for method 'func' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: member MyClass.func : a:string * ?b:string -> string, member MyClass.func : a:string * b:string -> string
You can call the second overload explicitly in two ways (with or without ?b) thanks to the fact that you can explicitly provide Some value for an optional argument:
MyClass().func("A")
MyClass().func("A",?b=Some "B")
Out of curiosity, it turns out that you can call the first overload via a static member constraint. This is quite ugly and you probably shouldn't be doing this, but it calls the first overload:
let inline callFunc (o:^T) a b =
(^T : (member func : string * string -> string) (o, a, b))
callFunc (MyClass()) "A" "B"
In OCaml, it is legal to have in .mli:
val f : 'a -> 'a
val g : 'a -> 'a
and .ml:
let f x = x
let g = f
Yet in F#, this is rejected:
eta_expand.ml(2,5): error FS0034: Module 'Eta_expand' contains
val g : ('a -> 'a)
but its signature specifies
val g : 'a -> 'a
The arities in the signature and implementation differ. The signature specifies that 'g' is function definition or lambda expression accepting at least 1 argument(s), but the implementation is a computed function value. To declare that a computed function value is a permitted implementation simply parenthesize its type in the signature, e.g.
val g: int -> (int -> int)
instead of
val g: int -> int -> int.
One workaround is to η-expand the definition of g:
let g x = f x
If my code is purely functional (no exceptions, no side effects, etc.) this should be equivalent (actually, it might be even better with respect to polymorphism, depending on how the language generalizes types : in OCaml partial applications do not produce polymorphic functions, but their η-expansion does).
Is there any drawback to systematic η-expansion?
Two answers elude the question on η-expansion :-) and instead suggest that I add parentheses around my functional type. This is because, apparently, F# distinguishes at the typing level between "true" definition of functions (as λ-expressions and computed definitions, as in partial applications); presumably this is because λ-expressions directly map to CLR functions while computed definitions map to delegate objects. (I'm not sure of this interpretation and would appreciate if somebody very familiar with F# could point to reference documents describing this.)
A solution would be to systematically add parentheses to all the function types in .mli, but I fear this could lead to inefficiencies. Another would be to detect the computed functions and add parenthesize the corresponding types in the .mli. A third solution would be to η-expand the obvious cases, and parenthesize the others.
I'm not familiar enough with F# / CLR internals to measure which ones incur significant performance or interfacing penalties.
In theory, the F# function type 'a -> 'b -> 'c is the same type as 'a -> ('b -> 'c). That is, multiple argument functions are represented using the curried form in F#. You can use one where the other is expected in most cases e.g. when calling a higher-order function.
However, for practical reasons, F# compiler actually distinguishes between the types - the motivation is that they are represented differently in the compiled .NET code. This has impact on performance and also interoperability with C#, so it is useful to make that distinction.
A function Foo : int -> int -> int is going to be compiled as a member int Foo(int, int) - the compiler does not use the curried form by default, because this is more efficient when calling Foo with both arguments (more common case) and it is better for interop. A function Bar : int -> (int -> int) will be compiled as FSharpFunc<int, int> Bar(int) - actually using the curried form (and so it is more efficient to call it with just a single parameter and it will be hard to use from C#).
This is also why F# does not treat the types as equal when it comes to signatures - signature specifies the type, but here it also specifies how is the function going to be compiled. The implementation file has to provide function of the right type, but - in this case - also of the right compiled form.
Interestingly my fsi gives a more helpful error message:
/test.fs(2,5): error FS0034: Module 'Test' contains
val g : ('a -> 'a) but its signature specifies
val g : 'a -> 'a The arities in the signature and implementation differ.
The signature specifies that 'g' is function definition or lambda expression
accepting at least 1 argument(s), but the implementation is a computed
function value. To declare that a computed function value is a permitted
implementation simply parenthesize its type in the signature, e.g.
val g: int -> (int -> int) instead of
val g: int -> int -> int.
If you add brackets to get g :('a -> 'a) all is fine
I have F# class library assembly that contains two functions:
let add a b = a + b
and
let rec aggregateList list init (op:int -> int -> int) =
match list with
|[] -> init
|head::tail ->
let rest = aggregateList tail init op
op rest head
I have a C# console application which references the F# library and is attempting to do the following:
FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);
However, the compiler complains that [myFsLibrary.add] cannot be converted from 'method group' to FSharpFunc<int, FSharpFunc<int, int>>
Other people have provided answers, but I'll just step in to say that you shouldn't do this.
Don't expose F# lists to C#. Don't expose curried functions to C#. The impedance mismatch is visible at this boundary, so it is better to expose common framework types at cross-language assembly boundaries. See
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/fsharp-component-design-guidelines.pdf
for more advice.
You can explicitly create a function using the FSharpFunc delegate. In C#, it is more convenient to create function that takes all arguments as a tuple, so you can do that and then convert the function to a curried type using FuncConvert. Something like:
FuncConvert.FuncFromTupled(new FSharpFunc<Tuple<int, int>, int>(args =>
arags.Item1 + args.Item2))
However, if you need to call some F# function from your C# code, it is recommended to expose a function with a C#-friendly interface. In this case, I you can use Func delegate and the first argument should be IEnumerable instead of F#-specific list type:
module List =
let AggregateListFriendly inp init (op:Func<int, int, int>) =
aggregateList (List.ofSeq inp) init (fun a b -> op.Invoke(a, b))
Then your C# appplication can just use:
List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a + b));
The reason why is that add is exported as a normal .Net style function and has the rough signature
int add(int, int)
C#, and most .Net languages, see this as a method which takes 2 int parameters and returns a single int value. F# though doesn't see functions this way. Instead it sees add as a function takes an int and returns a function which in turn takse an int and returns an int. This view of functions makes it very easy to implement operations like currying.
In order to convert from the C# view of the world to F# you need to do a bit of magic to fold a method onto itself. I accomplish this by defining a set of F# factory and extension methods to do the magic for me. For example
[<Extension>]
type public FSharpFuncUtil =
[<Extension>]
static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
fun x y -> func.Invoke(x,y)
static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
FSharpFuncUtil.ToFSharpFunc func
I can use this library to get the appropriate F# delegate type for the add method like so
var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);