Is it possible to create static member indexed properties in F#? MSDN show them only for instance members, however, I'm able to define the following class:
type ObjWithStaticProperty =
static member StaticProperty
with get () = 3
and set (value:int) = ()
static member StaticPropertyIndexed1
with get (x:int) = 3
and set (x:int) (value:int) = ()
static member StaticPropertyIndexed2
with get (x:int,y:int) = 3
and set (x:int,y:int) (value:int) = ()
//Type signature given by FSI:
type ObjWithStaticProperty =
class
static member StaticProperty : int
static member StaticPropertyIndexed1 : x:int -> int with get
static member StaticPropertyIndexed2 : x:int * y:int -> int with get
static member StaticProperty : int with set
static member StaticPropertyIndexed1 : x:int -> int with set
static member StaticPropertyIndexed2 : x:int * y:int -> int with set
end
But when I try to use one, I get an error:
> ObjWithStaticProperty.StaticPropertyIndexed2.[1,2] <- 3;;
ObjWithStaticProperty.StaticPropertyIndexed2.[1,2] <- 3;;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error FS1187: An indexer property must be given at least one argument
I tried a few different syntax variations and none worked. Also weird is that when I hover over set in VS2010 for one of the definitions in the type, I get info about ExtraTopLevelOperators.set.
If you wanted to recover the Type.Prop.[args] notation, then you can define a simple object to represent an indexable property with the Item property:
type IndexedProperty<'I, 'T>(getter, setter) =
member x.Item
with get (a:'I) : 'T = getter a
and set (a:'I) (v:'T) : unit = setter a v
type ObjWithStaticProperty =
static member StaticPropertyIndexed1 =
IndexedProperty((fun x -> 3), (fun x v -> ()))
ObjWithStaticProperty.StaticPropertyIndexed1.[0]
This returns a new instance of IndexedProperty every time, so it may be better to cache it. Anyway, I think this is quite nice trick and you can encapsulate some additional behavior into the property type.
A digression: I think that an elegant extension to F# would be to have first-class properties just like it has first-class events. (You could for example create properties that automatically support INotifyPropertyChange with just one line of code)
I believe that you call indexed properties using a different syntax (whether instance or static):
ObjWithStaticProperty.StaticPropertyIndexed2(1,2) <- 3
The only semi-exception to this is that an Item property on an instance x can be called via x.[...] (that is, Item is omitted and brackets are used around the arguments).
Related
I wrote a wrapper around List. I expect the internal list to keep state but it doesn't. What am I doing wrong? The methods are definitely executed but the internal list is always empty.
open System
open System.Collections.Generic
open NUnit.Framework
type MyList() =
member this.List = List<char>()
member this.AddX =
printfn "AddX called"
this.List.Add('X')
member this.CountX: int =
printfn "CountX called"
this.List.Count
[<Test>]
let TestX () =
let mylist = MyList()
mylist.AddX
mylist.AddX
Assert.AreEqual(2, mylist.CountX)
Tried putting a mutable keyword in different places (no success)
The problem is that every time you call the List member of MyList, it creates a new list, so the class isn't keeping internal state the way you want. (You can verify this by adding a printfn statement to the List method.)
To fix this problem, change the List member to be a value, which is initialized only once per class instance:
type MyList() =
member val List = List<char>()
...
Alternatively, you can use a let-bound value instead:
type MyList() =
let list = List<char>()
member this.AddX = list.Add('X')
member this.CountX = list.Count
Whilst attempting to learn the use of constructor constraints, I was hoping that something like the following was possible.
type Foo<'T when 'T : (new : int -> 'T)> = {Bar: 'T}
But this does not compile, simply returning the error
'new' constraints must take one argument of type 'unit' and return the constructed type
It seems as though the constraint should be called "parameterless constructor constraint" because I cant get any other form beyond this to compile.
type Foo<'T when 'T : (new : unit-> 'T)> = {Bar: 'T}
Can a constructor constraint be used to constrain the generic types constructor to have a specific signature other than unit ?
As noted in the comments, .NET support for constraints is limited and only supports (i) paremeter-less constructors and (ii) implementation of an interface.
F# follows the same restructions for ordinary generic type parameters, but it also has statically resolved type parameters written as ^T, which can have more expressive constraints. Those are not compiled to .NET constraints, but instead, are erased during the compilation, so there are other constraints on those.
An example akin to what you have using static memnber constraints would look something like this:
type Foo<'T> =
{ Bar : 'T }
static member inline Demo< ^S when ^S : (static member Create : int -> ^S)>() =
{ Bar = (^S : (static member Create : int -> ^S) (10)) }
The constrains need to be on an inline member (or inline function), so I'm defining a generic type Foo<'T> which has a static member Demo that can be called with any type that has a Create method taking an int. For example:
type Sample(n:int) =
member x.N = n
static member Create(n:int) = Sample(n)
We can now call Foo.Demo with Sample as the type argument and we get Foo<Sample> back, created using 10 as the initialization value:
let f = Foo<_>.Demo<Sample>()
Given a function defined as let get<'T> var1 var2 : 'T option what type signature should the given to a record field that function will be assigned to?
I've tried various permutations of type MyType = {AFunc<'T> : obj -> obj -> 'T option} but but can't find any variant that lets me introduce the type argument.
I can do this type MyType = {AFunc: obj -> obj -> obj option} and that will let me create the record {AFunc = get} but then can't apply the function because the type argument is missing.
There's a bit of ambiguity in your question. Do you want to be able to store get<'t> in a record for one particular 't per record, or do you want to have the record itself store a "generic" function like get<_>?
If the former, then TeaDrivenDev's answer will work.
If the latter, then there's no completely straightforward way to do it with F#'s type system: record fields cannot be generic values.
However, there's a reasonably clean workaround, which is to declare an interface type with a generic method and store an instance of the interface in your record, like this:
type OptionGetter = abstract Get<'t> : obj->obj->'t option
type MyType = { AFunc: OptionGetter }
let get<'t> var1 var2 : 't option = None // your real implementation here
let myRecord = { AFunc = { new OptionGetter with member this.Get v1 v2 = get v1 v2} }
let test : int Option = myRecord.AFunc.Get "test" 23.5
You have to make the record type itself generic; only then will 'T be defined and usable.
type MyType<'T> = { AFunc : obj -> obj -> 'T option }
Can someone clarify when to use typedefof<'T> vs. typeof<'T>?
Both typedefof<System.String> and typeof<System.String> return the same Type instance.
However, they return different instances and different information for System.Collections.Generic.List<_>.
Can I think of typedefof as a new and improved typeof? Should I just switch to always using typedefof? Or is it more subtle than that?
This ought to illustrate the difference. When you use typeof, the compiler infers type arguments and constructs a concrete type. In this case, the inferred type argument is System.Object:
let t1 = typeof<System.Collections.Generic.List<_>>
let t2 = typedefof<System.Collections.Generic.List<_>>
printfn "t1 is %s" t1.FullName
printfn "t2 is %s" t2.FullName
Output:
t1 is System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
t2 is System.Collections.Generic.List`1
Because typeof can only return a constructed type, typedefof is necessary if you need a type object representing a generic type definition.
typeof is used when you want to get the System.Type object for a given type. typedefof is used when you want to get the System.Type that represents the type definition for a generic type. As an example that uses both, suppose you had a type called Generic<'a>, and you wanted to create a function that returned the System.Type object for the Generic of any given type.
type Generic<'a> = Value of 'a
let makeGenericOf<'a> () =
typedefof<Generic<_>>.MakeGenericType(typeof<'a>)
Here, you would use the typedefof function to get the type defintion, and typeof to get the type of 'a for constructing the generic Generic<'a> Type.
I really appreciate the answers from phoog, Aaron, and JLRishe. Here is what I have learned, based on their answers and my own experimentation.
There are two Type instances associated with generics.
There is a Type associated with a generic that has specific type parameters. For example, there is a Type associated with List<int> and a different Type associated with List<string>. This is what you get when you use typeof<>.
> typeof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.String]"
> typeof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Int32]"
There is a Type associated with the generic type definition itself. For example, there is a single Type associated with List<'T>, which is the same for List<int>, List<string>, and List<_>. This is what you get when you use typedefof<>.
> typedefof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
By the way, the Type class has an instance method to GetGenericTypeDefinition(). That means, the following two return the same instance:
> Object.ReferenceEquals(typeof<List<int>>.GetGenericTypeDefinition(), typedefof<List<int>>);;
val it : bool = true
What happens if you call typeof<List<_>>? You get back the Type definition for List<Object>, as phoog mentioned.
> typeof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Object]"
This is all helpful to understand. For example, suppose I need to know if an object is a generic list (of any type).
// does not give me the answer I naively expected
> o.GetType() = typeof<List<_>>;;
val it : bool = false
// does this reference point to a List<'T>?
> o.GetType().IsGenericType && o.GetType().GetGenericTypeDefinition() = typedefof<List<_>>;;
val it : bool = true
Additionally, if you want to late-bound instantiate a generic type, you can use the MakeGenericType(...) method which Aaron mentioned.
> let myList = typedefof<List<_>>.MakeGenericType(typeof<int>);;
val myList : Type = Microsoft.FSharp.Collections.FSharpList`1[System.Int32]
I have written a class in F# implementing an interface in order to build a C#-friendly interface for my F#-assembly.
I have written some of the properties as indexed properties. However, when I try to use the type from C#, I only get the synthetic get_PropertyName methods in intellisense and the compiler likewise complains in case I want to use the indexed properties like I would do for C# ones.
Code for reference:
type IMyInterfaceType =
abstract MyProperty : MyType1 with get
abstract MyIndexedProperty : MyType2 -> MyType3 with get
abstract MyTwoDimensionalIndexedProperty : (MyType4 * MyType5) -> MyType6 with get
type MyInterfaceType =
new () = { }
interface IMyInterfaceType with
member this.MyProperty with get () = new MyType1 ()
member this.MyIndexedProperty with get parameter = new MyType3 ()
member this.MyTwoDimensionalIndexedProperty with get pair = new MyType6 ()
When trying to access this class from C#, I only get methods
get_MyIndexedProperty(MyType2 parameter)
get_MyTwoDimensionalIndexedProperty(Tuple<MyType4, MyType5>)
instead of the indexed properties I had hoped for.
Am I doing something wrong or is this a known issue?
cheers
--Mathias.
Response to the original question:
Indexer properties in C# have special name Item thus to create indexer accessible from C# you must name your indexer property "Item", e.g.:
type X () =
member this.Item with get key = ....
Now it can be accessed both in F# using (x : X).[key] or in C# using x[key] .
Response to the updated question:
C# does not support indexed properties the way F# does. Instead using additional type is advised: https://msdn.microsoft.com/en-us/library/aa288464%28v=vs.71%29.aspx
So you can try to use something like this:
[<AbstractClass>]
type Indexer<'index, 'result> () =
abstract Get : 'index -> 'result
member this.Item with get key = this.Get key
type IMyInterfaceType =
abstract MyProperty : MyType1 with get
// F# indexed propetties
abstract MyIndexedProperty : MyType2 -> MyType3 with get
// C# workaround
abstract MyCSharpIndexedProperty : Indexer<MyType2, MyType3> with get
type MyInterfaceType () as this =
let proxy =
{ new Indexer<MyType2, MyType3> () with
member __.Get key = (this :> IMyInterfaceType).MyIndexedProperty key }
interface IMyInterfaceType with
member __.MyProperty with get () = new MyType1 ()
member __.MyIndexedProperty with get key = new MyType3 ()
member __.MyCSharpIndexedProperty with get () = proxy
And two dimensional property similarly by creating Indexer<'index1, 'index2, 'result> () = ...