I'm trying to build an Attribute that validates a certain instance of a type.
In order to do this I have to cast the ObjectInstance to that type.
And I need to set the attribute on the member of that type.
So we need to resort to the and keyword for the circular definition.
However in the following case I get the error that
A custom attribute must invoke an object constructor
On the line marked below.
namespace Test
open System
open System.ComponentModel.DataAnnotations
[<AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)>]
type MyAttribute() =
class
inherit ValidationAttribute ()
override this.IsValid (value: Object, validationContext: ValidationContext) =
match validationContext.ObjectInstance with
| :? MyClass as item ->
// TODO more validation
ValidationResult.Success
| _ ->
new ValidationResult("No no no")
end
and MyClass(someValue) =
[<Required>]
[<Range(1, 7)>]
//vvvvvvvvvvvvvvv
[<MyAttribute>]
//^^^^^^^^^^^^^^^
member this.SomeValue : int = someValue
I tried manually invoking the constructor, such as:
[<MyAttribute()>]
// or
[<new MyAttribute()>]
But none of them are accepted by the system.
Can an F# guru help me out here?
Interesting one. It seems that the type inference is really not getting that right. The correct syntax to use here is [<MyAttribute()>], but despite you using the and keyword, the MyAttribute class is not yet known.
Here is a workaround: First check that the object to validate is really of the right type, then use reflection to invoke a validation method:
[<AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)>]
type MyAttribute() =
inherit ValidationAttribute ()
override this.IsValid (value: Object, validationContext: ValidationContext) =
let t = validationContext.ObjectInstance.GetType()
if t.FullName = "Test.MyClass" then
let p = t.GetMethod("IsValid")
if p.Invoke(validationContext.ObjectInstance, [| |]) |> unbox<bool> then
ValidationResult.Success
else
ValidationResult("failed")
else
new ValidationResult("No no no")
type MyClass(someValue: int) =
[<Required>]
[<Range(1, 7)>]
[<MyAttribute()>]
member this.SomeValue = someValue
member this.IsValid() = someValue <= 7
Edit: to make that slightly cleaner, you could add an interface, that you use in your validation attribute, and later implement in your class.
type IIsValid =
abstract member IsValid: unit -> bool
Your IsValid method then becomes
override this.IsValid (value: Object, validationContext: ValidationContext) =
match validationContext.ObjectInstance with
| :? IIsValid as i ->
if i.IsValid() then
ValidationResult.Success
else
ValidationResult("failed")
| _ ->
ValidationResult("No no no")
in your class, this looks like:
type MyClass(someValue: int) =
[<Required>]
[<Range(1, 7)>]
[<MyAttribute()>]
member this.SomeValue = someValue
interface IIsValid with
member this.IsValid() = someValue <= 7
One solution would be to first describe your types in a signature files.
Since the attribute is specified in the signature file, you don't need to add it again in the implementation file:
Foo.fsi:
namespace Foo
open System
[<AttributeUsage(AttributeTargets.Property)>]
type MyAttribute =
inherit System.Attribute
new : unit -> MyAttribute
member Foo : unit -> MyClass
and MyClass =
new : someValue : int -> MyClass
[<MyAttribute()>]
member SomeValue : int
Foo.fs:
namespace Foo
open System
[<AttributeUsage(AttributeTargets.Property)>]
type MyAttribute() =
inherit Attribute()
member this.Foo () =
new MyClass(1)
and MyClass(someValue) =
// [<MyAttribute()>] -> specified in the fsi, still appears in compiled code
member this.SomeValue : int = someValue
See https://msdn.microsoft.com/en-us/library/dd233196.aspx for reference
One thing that you can do to get rid of mutual recursion is to break up MyClass definition into two and use type augmentation to add the members you want to mark with the attribute.
type MyClass(someValue: int) =
member internal this.InternalSomeValue = someValue
type MyAttribute() =
inherit ValidationAttribute()
(* you can refer to MyClass here *)
type MyClass with
[<MyAttribute()>]
member this.SomeValue = this.InternalSomeValue
That's closer to what you're asking for, but I like the interface idea better.
Related
Given
type A() =
member val Prop: int = 0 with get, set
There are multiple ways to create an instance
let a0 = A() // Prop = 0
let a1 = A(Prop = 1)
let a2 = A()
a2.Prop <- 2
Now we want to enhance our class and allow passing the prop value in the constructor, but without losing the parameterless constructor we already have
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) = A1(Prop = prop) //Error
However this is an error
This is not a valid object construction expression. Explicit object
constructors must either call an alternate constructor or initialize
all fields of the object and specify a call to a super class
constructor.
which doesn't seem correct as the new constructor is actually calling an alternate constructor.
There are alternatives/workarounds to achieve the result, for example:
type A2() =
member val Prop: int = 0 with get, set
static member Create(prop: int) = A(Prop = prop)
let a21 = A2.Create(1)
type A3(?prop: int) as this =
do if prop.IsSome then this.Prop <- prop.Value
member val Prop: int = 0 with get, set
let a31 = A3(1)
however the A1 version seems the cleanest and there are no apparent reasons why it cannot be valid (it is very similar to the A2 static member)
Can someone explain why the A1 syntax cannot be valid?
The primary constructor of your object should generally take all the parameters required for constructing a valid object. As a general rule I'd make the secondary constructor parameterless and the primary take all the parameters you care about.
If you must have a parameterless primary constructor but still want to assign properties in secondary constructors you can use the then keyword for side-effectful construction.
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) as this =
A1()
then
this.Prop <- prop
I don't know if it cannot be valid if somebody decides the compiler should handle it, but I thought you might be interested in how we normally solve this case.
type A4(prop: int) =
member val Prop = prop with get, set
new() = A4(0)
let a4a = A4() // 0
let a4b = A4(3) // 3
let a4c = A4(Prop=7) // 7
Following this, it's useful to know how to make private constructors.
type A4 private (prop: int) =
member val Prop = prop with get, set
private new () = A4(0)
This example is of course rather useless code, but it shows where to put the private keyword. As you probably already understand, you can use one or more private constructors, typically with many parameters, as helpers for public constructors that have fewer parameters.
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 }
I have an type that is implementing IEnumerable<T> interface, all is ok:
open System
type Bar() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () = null
But things goes wrong if type inherits IEnumerable interface implementation via the base type:
open System
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
type Bar() =
inherit Foo()
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () = null
Code above produces the type inference errors:
The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct type to override any given virtual method
The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct number of method type parameters. The required signature is 'GetEnumerator : unit -> Collections.Generic.IEnumerator<int>'.
Am I doing something wrong or this is an F# compiler bug?
Microsoft (R) F# 2.0 Interactive build 4.0.30319.1
Update more canonical example:
type IFoo = abstract Bar : obj list
type IFoo<'a> = abstract Bar : 'a list
inherit IFoo
/* ok */
type Foo = interface IFoo with member x.Bar = []
interface IFoo<Foo> with member x.Bar = []
/* fail */
type FooBase = interface IFoo with member x.Bar = []
type FooDerived = interface IFoo<Foo> with member x.Bar = [] // <---
inherit FooBase
/*
error FS0017: The member 'get_Bar : unit -> 'a list' does not
have the correct type to override any given virtual method.
*/
The compiler cannot infer the correct type from your "null"-implementation. Try
open System
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
type Bar() =
inherit Foo()
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () : Collections.Generic.IEnumerator<int> = null
UPDATE:
The reason is, that the type of the GetEnumerator method implemented by the Bar type is ambigous as IEnumerable<'a> implements/inherits the non-generic IEnumerable which also specifies a (non-generic) GetEnumerator method. So, how should the compiler infer, which method exactly you are trying to implement if all he gets is null? Therefore we need a type annotation in this case.
This is not a bug, this is just an type inference fail because of F# may implement inherited interface members in the derived interface implementation declaration:
type IA = abstract A : int
type IB = inherit IA
type IC = inherit IB
type Baz =
interface IC with
member x.A = 1
So in my example I should specify the correct return type explicitly because member x.GetEnumerator() in derived Bar type may match both IEnumerable.GetEnumerator() and IEnumerable<T>.GetEnumerator().
Why do Bind1 and Bind2 have different signatures?
type T() =
let bind(v, f) = v
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
fsi reports them as
type T =
class
new : unit -> T
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : (obj * obj -> obj)
end
This came up when I was playing with some computation expressions and couldn't figure out why I was getting an error message about Bind not being defined. Bind1-style didn't work, Bind2 did, and I couldn't figure out why.
Given the same objects, they do return the same result:
> q.Bind1(1:>obj,3:>obj);;
val it : obj = 1
> q.Bind2(1:>obj,3:>obj);;
val it : obj = 1
>
Using Microsoft F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.7.4, compiling for .NET Framework Version v4.0.21006
Bind1 is a get property that returns a function while bind2 is a function. You can see the get accessor if you evaluate bind1 and bind2 from an instance.
> let t = new T();;
val t : T
> t.Bind1;;
val it : (obj * obj -> obj) = <fun:get_Bind1#3>
> t.Bind2;;
val it : ('a * 'b -> 'a) = <fun:it#10>
You wrote the shorthand of
member self.Bind1
with get() = bind
Using reflector you can see in Bind1 where obj comes from and the function object.
internal class get_Bind1#7 : FSharpFunc<Tuple<object, object>, object>
{
// Fields
public T self;
// Methods
internal get_Bind1#7(T self)
{
this.self = self;
}
public override object Invoke(Tuple<object, object> tupledArg)
{
object v = tupledArg.get_Item1();
object f = tupledArg.get_Item2();
return this.self.bind<object, object>(v, f);
}
}
Along with what kvb said you can add type annotation to the class to avoid the generic objects.
type T<'a, 'b>() =
let bind(v:'a, f:'b) = (v:'a)
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
type T<'a,'b> =
class
new : unit -> T<'a,'b>
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : ('a * 'b -> 'a)
end
To elaborate on Erik's answer, because it is impossible to have generic properties on .NET objects, F# has to pick non-generic types for v and f, which default to obj. You could choose other specific types and use a type annotation to give Bind1 a different (but still non-generic) signature.
Any ideas on how I could add type annotation to fix this error?
I'm getting a red squiggly under Foo.Bar in getFooBar and the following error message for it.
Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
[<AllowNullLiteralAttribute>]
type Test(foo : Test, bar : int) =
let getFooBar(test : Test) =
test.Foo.Bar
member this.Foo with get() = foo
member this.Bar with get() = bar
I do not understand why this is needed but simply annotate the result of Foo seems to work:
[<AllowNullLiteralAttribute>]
type Test(foo : Test, bar : int) =
let getFooBar(test : Test) =
(test.Foo : Test).Bar
member this.Foo with get() = foo
member this.Bar with get() = bar
You can also just annotate the type of the Foo property:
type Test(foo, bar : int) =
let getFooBar(test : Test) =
test.Foo.Bar
member this.Foo with get() : Test = foo
member this.Bar with get() = bar
Just tried making an anonymous function and it works.
let getFooBar(test : Test) =
test.Foo |> fun (x:Test) -> x.Bar
Is there a better way?