The following F# code doesn't compile due to value restriction problem:
let x = List.rev []
But this compiles:
let x = List.rev [] in 3::x
As I understand it, the compiler infers that x must be of type int list so it doesn't compile.
The following doesn't compile:
let x = List.rev [] in (3::x, true::x)
But this does:
let x = ([]) in (3::x, true::x)
Why?
As I understand it*, there are two ways to declare a value in F#:
Value has a concrete type (e.g. List<int>). You must either explicitly specify this concrete type, or the compiler must be able to infer it.
Value has a generic type (e.g. List<'t>). You must either explicitly specify this generic type, or the compiler must be able to infer it and the value must be a simple immutable value.
With that in mind, here are the explanations for what you're seeing:
// not allowed because x is generic, but not simple
let x = List.rev []
// allowed because the compiler can infer x's concrete type (List<int>)
let x = List.rev [] in 3::x
// not allowed because x is generic, but not simple
let x = List.rev [] in (3::x, true::x)
// allowed because x is both generic and simple (compiler can tell it's the empty list)
let x = ([]) in (3::x, true::x)
* The value restriction can be tricky, so there may be nuances I've overlooked in my explanation.
Related
Please see the below code.
let x = Seq.head [1.0; 2.0] // This is ok.
type Func<'T> = { f: seq<'T> -> 'T }
let func = { f = Seq.head }
// Compilation error: This expression was expected to have type seq<obj> but here has type 'a list
let y = func.f [1.0; 2.0]
let z = func.f ([1.0; 2.0] |> List.toSeq) // This is ok.
I don't understand why Seq.head and fund.f behavior differently here. It looks like a compiler bug to me. However, if this is by design, can anyone help explain a little bit to me? Thanks a lot!
The below is the answer from Don Syme (github.com/fsharp):
This is by design. The rule called "14.4.3 Implicit Insertion of Flexibility for Uses of Functions and Members" is only applied to uses of functions and members, not uses of record fields.
So here is my question. I have a python project that is very functionally oriented...but which fundamentally runs with classes...so F# seemed the ideal language for a compiled version of my code (I'd like to black box my source code...the python code is just sitting there in files. Not cool).
In my python class, I have a generic container--a python list. This container will contain arrays of a uniform length in a given implementation...
Here is my problem: I need to add arrays to the list after initialization.
What is the proper way to add arrays to the list in the class?
Here is what I have...but it seems like it will be slow, since it throws out the old list with a new copy (right??):
type myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value::_training
However, this fails (even after the edits suggested below) because the compiler claims that the set function is an obj with set rather than an obj list with set, even though it acknowledges that _training is a mutable obj list on the left side of the <- in the set function...I'm stumped)
What is the proper "F#" way to do this?
The 'proper' F# way to do it is to not have that myclass class at all. A class with a single getter and setter provides no value over an immutable list.
That said, the above code doesn't compile for a number of reasons:
A type is defined with =, not :.
_training isn't bound by with a let expression.
If you want to mutate the _training value, you must declare it let mutable
Even so, the return value of get is obj list, but the input value of set (which is the symbol value) is obj, because of the use of the cons operator (::). As the compiler says, the type of get and set must be the same.
You can make it compile by addressing all these concerns:
type Myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value
but it would accomplish nothing. If you need a list, then pass a list. It's not that we don't believe in encapsulation in F#, but properties aren't equivalent to encapsulation anyway...
BTW, it'd be idiomatic to name types using Pascal Case, so MyClass instead of myclass. The leading underscore isn't used much either, so it should be training instead of _training.
The problem is that a property has a type,
and the getter and setter must be compatible with that type.
If I run your code interactively, the compiler helpfully tells me exactly that!
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(value) = _training <- value::_training
// error FS3172: A property's getter and setter must have the same type.
// Property 'training' has getter of type 'obj list'
// but setter of type 'obj'.
The answer is simply to have a different method that prepends an element
to the list:
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(newTraining) = _training <- newTraining
member this.prepend(element) =
_training <- (element::_training)
member this.prependList(list) =
_training <- (list # _training)
// test
let myList = myclass()
myList.prepend(1)
myList.prepend(2)
myList.training // output => [2; 1]
myList.prependList([3;4])
myList.training // output => [3; 4; 2; 1]
It's not a nice implementation from a functional point of view,
but it does answer your question.
Note that if you do want a pure functional implementation,
you don't need a class at all.
// test
let myList = []
let myList2 = 1::myList
let myList3 = 2::myList2
// myList3 => [2; 1]
let myList4 = [3;4] # myList3
// myList4 => [3; 4; 2; 1]
Now, how to work with immutable state in a nice way -- that's a whole
other question! :)
Consider the following sample code where I have a generic type and 2 static member constructors that create a specialized instance of the said type.
type Cell<'T> = { slot: 'T }
with
static member CreateInt x : IntCell = { slot = x }
static member CreateString x : StringCell = { slot = x}
and IntCell = Cell<int>
and StringCell = Cell<string>
// Warnings on the next 2 lines
let x = Cell.CreateInt 123
let y = Cell.CreateString "testing"
I think I have the necessary type annotations in place and yet F# gives me warnings. E.g:
Warning 2 The instantiation of the generic type 'Cell' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'Cell<_>'.
How can I make the warning go away?
As hinted by #ildjarn, Cell is a generic type and the compiler wants to know the type 'T when calling the static member.
// Two ways to fix the compiler warning
let x = Cell<int>.CreateInt 123
let y = StringCell.CreateString "testing"
A way to avoid specifying 'T is to move the create functions into a module.
type Cell<'T> = { slot: 'T }
type IntCell = Cell<int>
type StringCell = Cell<string>
module Cell =
let createInt x : IntCell = { slot = x }
let createString x : StringCell = { slot = x }
let x = Cell.createInt 123
let y = Cell.createString "testing"
However, since you specify the desired type in the function name anyway, the following syntax may be preferred.
type Cell<'T> = { slot: 'T }
with
static member Create (x : 'T) = { slot = x }
type IntCell = Cell<int>
type StringCell = Cell<string>
let x = IntCell.Create 123
let y = StringCell.Create "testing"
// or simply
let z = Cell<float>.Create 1.0
Thanks to #Vandroiy for pointing out the missing type constraint in my Create method and for his answer that shows how the compiler can infer 'T for the generic type Cell when it can be determined by the static method being called.
The compiler cannot determine the generic parameter 'T of the methods CreateInt and CreateFloat because it is unrelated to the methods' return types. In the question, it is valid to write:
Cell<float>.Create 1.0 // useless type annotation to remove warning
However, you can just as well write
Cell<string>.Create 1.0 // Trollolol
To avoid this, you need to make sure the factory can only produce the type it is called on. When declaring a factory on a generic type, use a type annotation to equate the generic argument of its return type with the generic argument of the type it is called on.
In my opinion, the complicated formulation adds to the confusion. You can achieve the desired effect with
type Cell<'T> =
{ slot: 'T }
static member Create (x : 'T) = { slot = x }
let x = Cell.Create 123
let y = Cell.Create "testing"
Note the type annotation for x that equates the factory's input type with the generic parameter of the Cell<> type!
Edited to address the comment:
As is, the types IntCell and StringCell serve no purpose; they are just a less readable form of Cell<int> and Cell<string>. From a comment to this answer, I understand that these types should be exposed instead of Cell. As far as I know, this is not possible if they are defined as in the question, since type abbreviations have at most the accessibility of the type they abbreviate.
This is a reasonable design choice: if a type is generic, it should accept all valid generic type arguments. If IntCell and StringCell add specialized implementation, the usual way is to compose them of the appropriate instantiation of the Cell type and their specialized features. Then, the Cell type is allowed to have a more restricted accessibility than the specialized types.
I have an array and I want to cast it to an object. This is my code.
let a = [| 1 |]
let b = a :?> obj
but it tells me that int [] has no proper subtypes and cannot be used as a source of runtime type coercion. I'm pretty sure I can always do (object) in C# without the compiler complaining, so what's the issue here?
You're trying to downcast (:?>), instead of upcast (:>). Your code should be:
let a = [| 1 |]
let b = a :> obj
Please see http://msdn.microsoft.com/en-us/library/dd233220.aspx for more details.
While ebb's answer is of course correct, there is another solution for the special case of upcasting to obj, which is the box operator:
let a = [| 1 |]
let b = box a
In F#, box doesn't have the same meaning as it does in the CLR, where it means "make a reference-type object corresponding to a value-type value." In F#, it just means "cast to obj." Because of this, you can use box with reference types as well as with value types.
This came up while I looked at the following question: F# Unit of Measure, Casting without losing the measure type
Please note that I am not trying to use this unbox code, I just discovered some weird behavior while answering the question.
Why does the following code work
let intToFloat (x:int<'u>) : float<'u> = unbox float x
intToFloat 1<second>
while this yields a System.InvalidCastException: Unable to cast object of type 'float32ToFloat#86-6' to type 'Microsoft.FSharp.Core.FSharpFunc`2[System.Single,System.Double]'.?
let float32ToFloat (x:float32<'u>) : float<'u> = unbox float x
float32ToFloat 1.0f<second>
If I put parantheses around the (float x) the code works as expected, so I assume it must be some expression evaluation/type inference rule. What exactly is going on here and why are the parantheses needed in the second case?
The subtle thing in your code snippets is unbox float x - the compiler treats this as (unbox float) x. As a result the two functions are actually treated like this:
let intToFloat (x:int<'u>) : float<'u> =
let f = unbox float in f x
let float32ToFloat (x:float32<'u>) : float<'u> =
let f = unbox float in f x
So, you are taking the float function, casting it (unsafely) to a function of another type and then calling it. The type is int<'u> -> float<'u> in the first case and float32<'u> -> float<'u> in the second case.
I think the first one works because when the compiler sees the float function (without any type annotations), it defaults to int -> float and, since units of measure are erased at runtime, this can be converted to int<'u> -> float<'u> (but not to the second type - because you are performing unsafe cast).
So, the main problem is wrong parentheses in your implementation (which leads to a really subtle issue). I think you probably wanted something like this:
let intToFloat (x:int<'u>) : float<'u> = unbox (float x)
let float32ToFloat (x:float32<'u>) : float<'u> = unbox (float32 x)