I have this code:
open System
let func<'t when 't:comparison> (a: 't[]) = a
[<EntryPoint>]
let main argv =
let array = [||]
let actual = func array
printfn "array = %A, actual = %A, same objects: %b" array actual (Object.ReferenceEquals(array, actual))
Console.ReadKey()
0
When I try it in LinqPad5 I get a reasonlable error:
Value restriction. The value 'actual' has been inferred to have
generic type val actual : '_a [] when '_a : comparison Either
define 'actual' 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.
However, when I successfully (!) compile and run it (checked for full .NET Framework and DotNetCore both Debug/Release) in Visual Studio I get this output:
array = [||], actual = [||], same objects: false
The only way I could expect this result if 't[] were a value type, but it is definitely not. So, WTF?!?
Decompiled assembly contains this code:
[CompilationMapping(SourceConstructFlags.Module)]
public static class Program
{
public static t[] func<t>(t[] a)
{
return a;
}
[EntryPoint]
public static int main(string[] argv)
{
FSharpTypeFunc fsharpTypeFunc = (FSharpTypeFunc) new Program.array\u00409();
IComparable[] comparableArray = Program.func<IComparable>((IComparable[]) fsharpTypeFunc.Specialize<IComparable>());
FSharpFunc<object[], IComparable[]>.InvokeFast<bool, Unit>((FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>>) new Program.main\u004011(ExtraTopLevelOperators.PrintFormatLine<FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>>>((PrintfFormat<FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>>, TextWriter, Unit, Unit>) new PrintfFormat<FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>>, TextWriter, Unit, Unit, Tuple<object[], IComparable[], bool>>("array = %A, actual = %A, same objects: %b"))), (object[]) fsharpTypeFunc.Specialize<object>(), comparableArray, object.ReferenceEquals((object) (object[]) fsharpTypeFunc.Specialize<object>(), (object) comparableArray));
Console.ReadKey();
return 0;
}
[Serializable]
internal sealed class array\u00409 : FSharpTypeFunc
{
[CompilerGenerated]
[DebuggerNonUserCode]
internal array\u00409()
{
}
public override object Specialize<a>()
{
return (object) new a[0];
}
}
[Serializable]
internal sealed class main\u004011\u002D2 : FSharpFunc<bool, Unit>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public FSharpFunc<bool, Unit> clo3;
[CompilerGenerated]
[DebuggerNonUserCode]
internal main\u004011\u002D2(FSharpFunc<bool, Unit> clo3)
{
this.clo3 = clo3;
}
public override Unit Invoke(bool arg30)
{
return this.clo3.Invoke(arg30);
}
}
[Serializable]
internal sealed class main\u004011\u002D1 : FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public FSharpFunc<IComparable[], FSharpFunc<bool, Unit>> clo2;
[CompilerGenerated]
[DebuggerNonUserCode]
internal main\u004011\u002D1(FSharpFunc<IComparable[], FSharpFunc<bool, Unit>> clo2)
{
this.clo2 = clo2;
}
public override FSharpFunc<bool, Unit> Invoke(IComparable[] arg20)
{
return (FSharpFunc<bool, Unit>) new Program.main\u004011\u002D2(this.clo2.Invoke(arg20));
}
}
[Serializable]
internal sealed class main\u004011 : FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>> clo1;
[CompilerGenerated]
[DebuggerNonUserCode]
internal main\u004011(FSharpFunc<object[], FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>> clo1)
{
this.clo1 = clo1;
}
public override FSharpFunc<IComparable[], FSharpFunc<bool, Unit>> Invoke(object[] arg10)
{
return (FSharpFunc<IComparable[], FSharpFunc<bool, Unit>>) new Program.main\u004011\u002D1(this.clo1.Invoke(arg10));
}
}
}
This line seems to be a culprit:
IComparable[] comparableArray = Program.func<IComparable>((IComparable[]) fsharpTypeFunc.Specialize<IComparable>());
If I remove comparison constraint Specialize uses object instead of IComparable.
So, as I've gathered from the comments, your actual question was this:
Why is it that returned object differs from the passed one?
First of all, the expectation of referential identity for values that are logically "equal" is vastly overrated. If your program relies on referential identity, you're doing it wrong. If you have to force referential identity to be preserved everywhere, you end up with Java.
Indeed, try this:
> obj.ReferenceEquals( 5, 5 )
it : bool = false
> obj.ReferenceEquals( [1;2;3], [1;2;3] )
it : bool = false
Huh?
Of course, you may end up with true in some special cases, for example:
> let l = [1,2,3]
> obj.ReferenceEquals( l, l )
it : bool = true
But that's merely a coincidence arising from the specific implementation that the compiler chose to represent your code. Don't rely on it.
Secondly, your function does, in fact, return the "same" (in referential identity sense) object. Try this:
> let x =
let array = [||]
let typedArray : int[] = array
let actual = func typedArray
obj.ReferenceEquals( actual, typedArray )
x : bool = true
See how the "malfunction" disappeared as soon as I created an intermediate typedArray? You can even replace int with IComparable, it will still be true.
The secret is that the function func is actually fine: it does return the "same" object.
The creation of a new object happens not inside func, but every time you reference array.
Try this:
> let x =
let array = [||]
obj.ReferenceEquals( array, array )
x : bool = false
Huh? WTF?!
This happens, because array is not actually an object, but a function behind the scenes. Because you didn't specify what type array was, it must be generic - i.e. have any type that the user wants it to have. This must work:
let array = [||]
let a : int[] = array
let b : string[] = array
Clearly, array cannot have type int[] and type string[] at the same time, so the only way to implement such construct is to compile it as a function that takes no value parameters, but a single type parameter. Kind of like this:
static a[] array<a>() { return new a[0]; }
And then use that function to construct a and b:
var a = array<int>();
var b = array<string>();
And this is exactly what the compiler does. A function that takes only type parameters, one might call it "type function" in this context. And indeed, that's what it's called in compiled code - FSharpTypeFunc.
The key thing that Fyodor mentioned in his answer is that the way F# handles generic values is tricky. You can see that by looking at the following code which compiles fine:
let oops () =
let array = [||]
array.[0] <- 'a'
array.[0] <- 1
Of course, you cannot put both 'a' and 1 in the same array! What happens here is that the compiler actually compiles let array = [||] as a generic function that returns a new empty array when you access it (with a specific instantiation).
Note that this is possible only for simple values that are created without any side-effects. For example, if you wanted to print a message each time the array is accessed, it would not work. The following:
let oops () =
let array = printfn "Creating!"; [||]
array.[0] <- 'a'
array.[0] <- 1
Gives a type error:
error FS0001: This expression was expected to have type
char but here has type
int
This is because the inference engine realised that it cannot compile array as a generic function and, instead, specialized the type based on the first use.
In your case, specializing the type would not cause any problem, because you are not using the generic value with multiple different types. This means that you can make the value the same by adding a side-effect to the creation - or even just ignore a unit value () - which is enough to make the compiler specialize the type:
let func<'t when 't:comparison> (a: 't[]) = a
let same () =
let array = (); [||]
let actual = func array
printfn "same: %b" (Object.ReferenceEquals(array, actual))
let notSame () =
let array = [||]
let actual = func array
printfn "same: %b" (Object.ReferenceEquals(array, actual))
notSame() // same: false
same () // same: true
I guess if someone ever decides to make a Wat talk about F#, this would be a good candidate! The compiler could just disallow all generic values (which is what other ML languages do), but that would remove some useful constructs like Array.empty and replace them with Array.createEmpty ().
Related
Minimal reproducible code:
void main() {
print(foo<int>('1'));
print(foo<double>('1.0'));
print(foo<double>('1'));
print(foo<int>('1.0'));
}
T foo<T extends num>(String s) {
final res = num.parse(s);
return (T == double ? res.toDouble() : res.toInt()) as T;
}
As you can see I'm manually handling the types. Is there any better way to do it?
I don't see a much better solution. You have a function which changes behavior entirely based on the type argument. Since all you can do with a type argument is subtype-checks, or comparing it to a constant type literal, you need to do something like that.
I'd prefer subtype-checks, because that also allows promotion, but for something limited like this, where there are only four possible types for T, checking Type object equality can also work. There'll just need to be at least one as T before returning.
Either approach also only works for type hierarchies which are finite, and you account for all the possible types. Even here, the current code is not covering <num> and <Never> which are also valid type arguments for the bound num. So, be vigilant.
Maybe, using subtype checks for promotion:
T parse<T extends num>(String source) {
var value = num.parse(source);
if (value is T) return value;
// T is not `num`.
num d = value.toDouble();
if (d is T) return d; // T was double.
try {
num n = value.toInt(); // Can fail for Infinity/NaN
if (n is T) return n; // T was int
} catch (_) {
// T was `int` after all, so throwing was correct.
if (1 is T) rethrow; // T was int, but the input was not valid.
}
// T was neither num, double, nor int, so must be `Never`.
throw ArgumentError.value(T, "T", "Must not be Never");
}
Or. using Type object equality:
T parse<T extends num>(String source) {
var value = num.parse(source);
switch (T) {
case num: return value as T;
case int: return value.toInt() as T;
case double: return value.toDouble() as T;
default:
throw ArgumentError.value(T, "T", "Must not be Never");
}
}
With this type:
type A =
{
S: string
}
static member private l = Dictionary<string, A>()
static member add s = A.l.[s] <- { S=s }
static member list () = l.Values
if I do:
A.add "hello"
A.add "world"
I'd expect A.list() to return something since the dictionary is static, but it returns an empty list. Why is that?
To clarify what I'm trying to do: I'd like to have the ability to register the objects of type A into a static dictionary that is attached to the type itself as it would make the object repository 'self contained' in the type, in a way.
Your l is not a field, but a property with a getter.
A "property", contrary to appearances, is not a memory cell with some value in it. A "property" is a pair of get+set functions. Just functions, that's all. No memory cell.
So what you made yourself is a property with a getter (without a setter), and all that getter does is create a new Dictionary and return it.
This means, every time you access A.l, you get yourself a new, fresh dictionary. Because l is a function, not a memory cell.
Now, in order to make a memory cell (aka "field"), one would ordinarily use static member val, like so:
static member val private l = Dictionary<string, A>()
Unfortunately, in this particular case this doesn't work, because static fields are not permitted on F# records and unions. They work fine on actual classes, but not on F# types.
So instead what I would recommend is to put those functions in a module rather than making them static methods:
type A = { S: string }
module A =
let private l = Dictionary<string, A>()
let add s = l.[s] <- { S=s }
let list () = l.Values
(and just in general: try to use fewer classes and more modules and functions; they're more idiomatic in F# and lead to fewer problems in general)
Now this works as expected:
> A.add "hello";;
val it : unit = ()
> A.add "world";;
val it : unit = ()
> A.list();;
val it : Dictionary`2.ValueCollection<string,A> =
seq [{ S = "hello" }; { S = "world" }]
F# has many different ways to define variables/members in types. When should I use let, member val and member this. in F#, and what is the difference between them? How about static and mutable members?
The answer from #meziantou already gives a nice overview of the options (and how they behave differently), so let me just give a brief summary, or list of recommendations:
Use let or let mutable if you want to define a local value that is visible only within the type (essentially a private field or a private function). Inside a module at top-level, these are publicly accessible and evaluated once. let mutable at module level creates a single writable field with no backing value.
You can use val to create an auto-property, it is short for member val Foo = .. with get. From F# this is seen as a field, but it's internally implemented as a get-property with a backing field to prevent mutation.
You can use val mutable to define a public field, but I wouldn't recommend this unless you actually need a public field (e.g. some .NET library may require types with this structure).
Using member x.Foo = ... is the best way to expose (read-only) state from a type. Most F# types are immutable, so this is perhaps the most common public member. It is short for a get-only instance property.
Using member x.Foo with get() = .. and set(value) ... is useful when you need to create a get/set property with your own custom code in the gettor and settor. This is sometimes useful when you're creating a mutable object.
Using member val Foo = ... with get, set is basically the same thing as auto-implemented properties in C#. This is useful if you need a mutable property with a getter and setter that just reads/writes a mutable backing field.
Using static let on a type creates a static (class-level) read-only field, which internally creates a property with a backing field. Use static mutable let ... for a read/write static field without a backing field.
Using static val mutable private creates a static read/write auto-property with a backing field, it cannot be public.
I found out easier to just decompile what's happening, so:
type Region() =
let mutable t = 0.0f
member val Width = 0.0f
member x.Height = 0.0f
member val Left = 0.0f with get,set
member x.Top with get() = 0.0f and set(value) = t <- value
is actually the following:
public class Region
{
internal float t;
internal float Width#;
internal float Left#;
public float Width
{
get
{
return this.Width#;
}
}
public float Height
{
get
{
return 0f;
}
}
public float Left
{
get
{
return this.Left#;
}
set
{
this.Left# = value;
}
}
public float Top
{
get
{
return 0f;
}
set
{
this.t = value;
}
}
public Region() : this()
{
this.t = 0f;
this.Width# = 0f;
this.Left# = 0f;
}
}
This sample explains the difference between syntaxes:
type MyClass() =
let random = new System.Random()
[<DefaultValue>] val mutable field : int
member val AutoProperty = random.Next() with get, set
member this.ExplicitProperty = random.Next()
let c = new MyClass()
// c.random is not accessible
c.field <- 42 // 'field' is accessible
// An automatic property is only evaluated upon initialization, and not every time the property is accessed
printfn "AutoProperty = %d" c.AutoProperty // x
printfn "AutoProperty = %d" c.AutoProperty // Still x
// The value of the explicit property is evaluated each time
printfn "ExplicitProperty = %d" c.ExplicitProperty // y
printfn "ExplicitProperty = %d" c.ExplicitProperty // The value is re-evaluated so you'll get a different value
I want to write the equivalent of this C# in F#:
struct Vector2 {
public readonly int X;
public readonly int Y;
public Vector2(int x, int y) {
X = x;
Y = y;
}
}
This forces the user to supply arguments to create an instance [EDIT: this is wrong for value types - all value types have a default constructor]. A default Vector2 could be provided with a static readonly field as well, i.e. Vector2.Zero.
It looks like the only way to get public fields is via the "val" keyword, but doesn't seem to let me initialize them with the default constructor, and I don't want to have two constructors:
[<Struct>]
type MyInt(value) =
val public Value : int = value;;
val public Value : int = value;;
-------------------------------^
stdin(7,32): error FS0010: Unexpected symbol '=' in member definition
I know this can be done with member bindings but this creates properties, not fields, if I understand well.
According to this http://msdn.microsoft.com/en-us/library/vstudio/dd233233.aspx, that could be done as
type Vector2 =
struct
val public X: int
val public Y: int
new(x: int, y: int) = { X = x; Y = y }
end
If an F# Record would work for you, then this will work:
type MyInt = {
Value: int
};;
Then to initialize:
let i = {Value=1};;
Since I'm not completely sure of your use case, I'm not sure how helpful this is.
EDIT:
For what it's worth, if your reason for preferring a value type is that you want value type equality semantics, records support that (even though they are reference types). Consider this:
type Vector2 = {
X:int;
Y:int
};;
type Vector2 =
{X: int;
Y: int;}
let v = {X=1;Y=1};;
val v : Vector2 = {X = 1;
Y = 1;}
let v2 = {X=1;Y=1};;
val v2 : Vector2 = {X = 1;
Y = 1;}
v = v2;;
val it : bool = true
let v3 = {X=2;Y=2};;
v = v3;;
val it: bool = false
I mean to say that even though records are reference types, one of the reasons that people use value types (the equality semantics) is not a problem with records. Even though it's a reference type, I believe it's a lot closer to the behavior of the C# code you're trying to emulate--for what it's worth.
I'm writing a generic class that has two constructors: the first one initializes every field, the second (parameter-less) should not initialize anything.
The only way I found to achieve this is calling the main constructor with "empty" arguments, i.e. Guid.Empty and null. Besides not looking good functional style to my untrained eyes, this means that I have to put a a' : null constraint on the second parameter, which I don't want:
type Container<'a when 'a : null>(id : Guid, content : 'a) =
let mutable _id = id
let mutable _content = content
new() = Container<'a>(Guid.Empty, null)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
I see two ways to solve this:
use something like the default c# keyword instead of null (does such a thing exist in F#?)
use a different syntax to specify constructors and private fields (how?)
What is the best way to implement this class?
The F# analog to default is Unchecked.default<_>. It is also possible to use explicit fields which you don't initialize:
type Container<'a>() =
[<DefaultValue>]
val mutable _id : Guid
[<DefaultValue>]
val mutable _content : 'a
new (id, content) as this =
new Container<'a>() then
this._id <- id
this._content <- content
However, in general, your overall approach is somewhat unidiomatic for F#. Typically you'd use a simple record type (perhaps with a static method to create uninitialized containers, although this seems to have questionable benefit):
type 'a Container = { mutable id : Guid; mutable content : 'a } with
static member CreateEmpty() = { id = Guid.Empty; content = Unchecked.defaultof<_> }
In many situations, you could even use an immutable record type, and then use record update statements to generate new records with updated values:
type 'a Container = { id : Guid; content : 'a }
[<GeneralizableValue>]
let emptyContainer<'a> : 'a Container =
{ id = Guid.Empty;
content = Unchecked.defaultof<_> }
let someOtherContainer = { emptyContainer with content = 12 }
If the type will be used from languages other than F#, the following provides a natural interface in F#, and C#, for example.
type Container<'a>(?id : Guid, ?content : 'a) =
let orDefault value = defaultArg value Unchecked.defaultof<_>
let mutable _id = id |> orDefault
let mutable _content = content |> orDefault
new() = Container(?id = None, ?content = None)
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
If it will only be used from F#, you can omit the following constructor overloads
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
new() = Container()
because the overload accepting optional args handles both these cases equally well in F#.