Need commentary on what this Map.ofSeq signature means - f#

Can anyone please breakdown what this method signature means?
/// Returns a new map made from the given bindings.
/// elements: The input sequence of key/value pairs.
val ofSeq : elements:seq<'Key * 'T> -> Map<'Key,'T> when 'Key : comparison
Here is how it's used:
let grid = [ for i in 0..8 -> (i, true) ] |> Map.ofSeq
Specifically, can some one break this method signature into segments and explain the function of each segment?
NOTE:
I am new to F# and really do want to understand the low level details of the language.

val ofSeq : elements : seq<'Key * 'T> -> Map<'Key,'T> when 'Key : comparison
Let's break it down. First of all, the presence of arrows means it's a function.
Each arrow says, simplistically speaking, that when I give it something of the type on the left, I get the thing on the right. Note that F# supports curried arguments, that means I can supply arguments one at a time, in the process returning functions with progressively fewer arguments.
In this case, your function only takes only one argument.
Your argument elements is of type seq<'Key * 'T>. That is a sequence of tuples of two generic types: 'Key' and 'T'.
When I supply this function with something of type seq<'Key * 'T>, it is telling me that it will return a Map<'Key,'T>. That is a Map which has keys of type 'Key and values of type 'T.
Finally you have a type constraint when 'Key : comparison this restricts the generic type 'Key to be something which supports comparison, i.e. a type which implements the IComparable interface. This type constraint is required because the Map is implemented using a binary tree which uses comparsion for its concept of ordering.

Related

Not undestanding generics in F#

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).

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).

Understanding type mismatch error?

Why does this statement give me a type mismatch error,
let x = List.rev [] in (3::x, true::x);;
while this statement does not?
let x = [] in (3::x, true::x);;
I'm assuming it is because x is given a function call in the first statement, while it is only give an empty list in the second statement. But, I am not sure exactly why the second works and the first does not? Any help will be much appreciated. Thanks!
Try the following:
let x = [] ;;
Result: val x : 'a list. F# knows that x is a list of as-yet unknown type. If it had any contents, its type would be known. This works perfectly well.
But the following will not work:
let x = List.rev [] ;;
Result:
error FS0030: Value restriction. The value 'x' has been inferred to have generic type
val x : '_a list
Either define 'x' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.
The "value restriction" error in F# can be hard to understand — why is [] allowed when List.rev [] is not? — but this article goes into some details. Essentially, F# can always feel safe making functions generic, but it can only feel safe making values generic if the following two conditions apply:
The expression on the right-hand side of the let is a pure value, e.g. it has no side-effects,
and
The expression on the right-hand side of the let is immutable.
When the expression is [], then F# knows that it's a pure, immutable value, so it can make it generic ('a list). But when the expression is someFunction [], the F# compiler doesn't know what someFunction is going to do. Even though in this case, we know that List.rev is part of the standard library and matches those two scenarios, the F# compiler can't know that. In a completely pure language like Haskell, you can know from a function's type signature that it's pure, but F# is a more pragmatic language. So F# takes the guaranteed-safe approach and does not make the result of List.rev [] generic.
Therefore, when you write let x = [] in (3::x, true::x), the [] value is a generic empty list, so it can become either an int list or a bool list as needed. But when you write let x = List.rev [] in (3::x, true::x), F# cannot make List.rev [] generic. It can say "This is a list of a type I don't yet know", and wait for the type to become clear. Then the first expression of a specific type that uses this list, in this case 3::x, will "lock" the type of that list. I.e., F# cannot consider this list to be generic, so now it has figured out that this empty list is an empty list of ints. And then when you try to append a bool to an empty int list, that's an error.
If you flipped the tuple around so that it was true::x, 3::x then the type error would "flip" as well: it would expect a bool list and find an int list.
So the short version of this answer is: you're hitting the F# value restriction, even though that's not immediately obvious since the error you got didn't mention value restriction at all.
See Understanding F# Value Restriction Errors for a good discussion of the value restriction, including the most common place where you'd normally see it (partially-applied functions).

How to write function where argument is type but not typed value?

I want to convert a string representations of few dozen enum types to enum values.
It's easy to convert string to concrete type:
Enum.Parse(typeof<FontStyle>,"Bold") |> unbox<FontStyle>
but for now i want to write function where type and string are parameters.
The best one i can write is:
> let s2e (_: 'a) s = Enum.Parse(typeof<'a>,s) |> unbox<'a>;;
val s2e : 'a -> string -> 'a
> s2e FontStyle.Regular "Bold";;
val it : FontStyle = Bold
Is there any option to write something like this but with type itself as first argument?
The function needs to take a single type parameter, which will be the type of the returned enum. However, you don't need to specify the type using a "fake" parameter that isn't used anywhere in the code - you can specify the actual type parameter when calling the function using the same notation as for example when calling defaultof<SomeType>.
The following modified function takes a single type parameter which occurs only in the return type (to avoid confusing SO code formatter, I replaced ' with ´ in the code):
> let parseEnum<´a> s = Enum.Parse(typeof<´a>,s) |> unbox<´a>;;
val parseEnum : string -> ´a
When calling the function, you need to specify the type explicitly:
> parseEnum<FontStyle> "Bold";;
val it : FontStyle = Bold
Your solution was pretty close to this - the only change is that you can specify the type parameter explicitly instead of providing a witness value to guide the type inference.
Tomas's answer is good. Additionally, note that you can apply F#'s enum constraints to prevent nonsensical type parameters from being used:
let parseEnum<'t,'u when 't:enum<'u>> s = System.Enum.Parse(typeof<'t>, s) :?> 't
(parseEnum "Class" : System.AttributeTargets)
Now calling (parseEnum "Test" : int) will fail at compile time since int is not an enum.
EDIT
Since we don't actually do anything with the underlying type 'u, we don't need the full power of F#'s enum constraints, as ssp points out in a comment. This is easier to use because it only has a single type parameter:
let parseEnum<'t when 't :> System.Enum and 't : struct> s =
System.Enum.Parse(typeof<'t>, s) :?> 't
Note that the struct constraint prevents using System.Enum itself as the type argument.

Resources