I will answer the second half of the question, why did you get the error "limit value". If you are looking for [f#] value restriction
in Stack Overflow, you will find many answers that may or may not confuse you. But the really short version: F # is built on top of the .NET structure, and .Net imposes certain restrictions. In particular, functions are allowed to be shared, but values cannot be shared. So you can do this:
let f<'TData> (a:'TData) = printfn "%A" a
but you cannot do this:
let (a:'TData) = Unchecked.defaultof<'TData>
The definition of a function is beautiful because the underlying .Net infrastructure knows how to handle common functions. But you are not allowed to have common values in .Net; any value must be of a specific type.
(Note: I wrote explicitly <'TData>
in the definition of f
, but I didn’t have to: I could only write let f (a:'TData) = printfn "%A" a
, and the general f
would be understood. I could even write let fa = printfn "%A" a
, and he would do the same).
Now let's look at the error you received: "the value" it "was inferred to have the general type val it : Series<string,obj>
". If you look at the signature of the getRow
function that you posted, it looks like this:
('a -> Frame<'a,'b> -> Series<'b,'c>)
When you called it getRow "Joe" people
, the F # compiler was able to conclude that type 'a
was string
(since the parameter "Joe"
is string
). And since the second argument to people
is Frame<string,string>
, the F # compiler was able to conclude that type 'b
also string
. But the result of this function call is Series<'b,'c>
, and so far the F # compiler knows nothing about what will be 'c
. And since you ran getRow "Joe" people
in the interactive REPL of F #, he tried to save the result of what you entered as the value of the name it
(F # interactive REPL always provides the value of the previous expression as it
) - but since the only type he knew so far, there was Series<string,'c>
, F # could not determine which specific type to assign to the value of it
. I know, looking at your code, that type 'c
was a Person
entry, but the F # compiler could not know that it was only from one call to getRow
due to the way the getRow
function is getRow
.
There are two ways to resolve this value limitation error:
One way to solve this problem would be to pass the result of getRow
to another function that would allow the F # compiler to output a specific type of its result. Unfortunately, since I don’t know Deadl very well, I cannot give you a good example. Maybe someone else will come up with one and comment on this answer, and I will edit it. It will look like this:
getRow "Joe" people |> (some Deedle function)
But I don’t know which Deedle function to use in my example: it should be a function that takes a Series
and performs some specific calculations with it, so that F # infers that it is Series<string,Person>
. Sorry, this is not a good example, but I will leave it anyway if that helps.
The second way you could solve this error is to indicate the type of value you are getting. In F #, you do this using the syntax : (type)
, for example:
getRow "Joe" people : Series<string,Person>
Or, since the F # compiler has enough information to output the string
part of this type, you could also write:
getRow "Joe" people : Series<_,Person>
When you write _
in a type signature, you tell the F # compiler "You will find out what type it is." This only works when the F # compiler has enough information to output this type correctly, but it is often a convenient shorthand when type signatures are large and cumbersome.
Both of these approaches would solve your immediate problem, get rid of the error "limit value" and allow you to continue working.
I hope this answer helps you. If this is hopelessly embarrassing you, let me know and I'll see if I can explain that you are embarrassed.
EDIT: In the comments, Soldalma asks if the F # compiler (which is a one-pass compiler that works from top to bottom and from left to right) can infer the type from the direct channel. The answer is yes, because the expression is not finished yet. Until the expression is complete, the output of type F # (which is based on a system of type Hindley-Milner *) is in order with the transfer of a set of not allowed types. And if the types are resolved before the expression is completed, the expression can go to a specific value (or a specific function). If the types are not yet allowed when the expression is complete, then it must solve the general value or function. And common functions are allowed in .Net, but not in common values, so the error is "limit value".
To see this in practice, consider the sample code. Copy and paste the following code into the F # editor, which allows you to attach the name of a variable (or function) to see its type. I recommend VS Code with the Ionide-fsharp extension, as it is cross-platform, but Visual Studio will work just as well.
open System.Collections.Generic let mkDict (key:'K) = new Dictionary<'K,'V>() // Legal let getValueOrDefault (key:'a) (defaultVal:'b) (dict:Dictionary<'a,'b>) = match dict.TryGetValue key with | true,v -> v | false,_ -> defaultVal let d = mkDict "foo" // Error: value restriction let bar = mkDict "foo" |> getValueOrDefault "foo" "bar" // Legal: type string let five = mkDict "foo" |> getValueOrDefault "foo" 5 // Legal: type int
Go and hover over each function and variable name to see its type, or press Alt + Enter to send the declaration of each function or variable to F # Interactive. (And as soon as you notice that the line let d
gives the error "limit value", comment it out so that the rest of the code compiles).
What is going on here is a good demonstration of how it all works. The mkDict
function has two unresolved types: 'K
and 'V
, so it must be common. But this is great, because .Net does not have problems with universal functions. ( mkDict
is actually not very useful, since it actually “discards” the data of its argument and does nothing for it. But it should be a trivial example, so just ignore the fact that it seems to be useless.) Similarly, getValueOrDefault
has two unresolved type, 'a
and 'b
, so it is also a common function.
However, let d = mkDict "foo"
not legal. Here, the generic type 'K
was defined as a specific type of string
, but 'V
was not yet resolved by the time the expression was completed, so d
would have to be generic (this would be like d<'V>
in explicit syntax). But d
not a function (since it has no parameters), this name is a value , and .Net does not allow the creation of common values.
But in the next two lines, the expression will not be completed at the time of analysis by the syntax mkDict "foo"
compiler, so it does not need to "block" unknown types yet. It may well carry the unresolved type 'V
to the next part of the expression. And there the getValueOrDefault
function has two specific types: string
and string
in the first line, and string
and int
in the second line. Since type 'b
matches type 'V
from mkDict
, so F # can resolve 'V
on both lines. And so bar
is of type string
, and five
is of type int
.
* Scott Vlashchin says that he should "more accurately ... be called" Damas-Milner-Algorithm W "". Since I myself have not studied it in detail, I will take his word for it, but if you are interested in learning more, the Wikipedia link that I provided is probably halfway a good starting point.