Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type does not have null as a proper value

Tags:

f#

For the sample program:

type public MyClass(reasonForLiving:string) =     member x.ReasonForLiving with get() = reasonForLiving  let classFactory () = MyClass("up to you") let live () =     let instance = classFactory()     if instance = null then raise(System.Exception("null is not living... that's why OO languages die from bugs"))     instance 

I get the error "The type 'MyClass' does not have null as a proper value" when I go to use this class as a return value of implicitly typed functions and compare it to null (b/c of compatibility requirements with C# dependency injection I cannot rely on F# option types).

I can easily fix this by changing the null check to:

if instance :> obj = null then 

However, I know ("feel") this is completely "wrong". Especially when I consider how MyClass is a reference type that shouldn't need to be boxed (speaking from a C# background).

I've read about "F# Value Restriction" and how it impacts type inference, but I can't seem to gleam how it applies to this scenario.

Q: Is there another way to do this?

Aside #1: I found a simpler method of getting the error...

type public MyClass(reasonForLiving:string) =     member x.ReasonForLiving with get() = reasonForLiving let nullMyClass : MyClass = null 

Aside #2: I did try System.Nullable without thinking... MyClass is a reference type and not a value type (struct) which Nullable<_> requires. So, just reassures me that I REALLY am dealing with a reference type and leaves me wondering why an object cast suddenly makes this work.

Update: For anyone interested, I used this as one solution for Common Service Locator with the three functions below. Each service requested must support null, so if the service class is defined in F#, you need to add the [<AllowNullLiteral>]:

let private getServiceLocator () =     try Some(Microsoft.Practices.ServiceLocation.ServiceLocator.Current)     with | _ -> None  let private getService serviceFactory =     let serviceLocator = getServiceLocator()     let service = match serviceLocator with                    | None -> serviceFactory()                   | _ ->                      match serviceLocator.Value.GetInstance<'a>() with                     | null -> serviceFactory()                     | svc -> svc     match service with     | null -> None     | _ -> Some(service)  let private getRequiredService serviceFactory =     let service = getService serviceFactory     match service with     | None -> raise(MissingServiceException(""))     | _ -> service.Value 
like image 701
Eric Swanson Avatar asked Jul 27 '12 22:07

Eric Swanson


1 Answers

Use the [<AllowNullLiteral>] attribute:

[<AllowNullLiteral>] type public MyClass(reasonForLiving:string) =     member x.ReasonForLiving with get() = reasonForLiving 

By default, F# types do not allow null (thank heavens!). This attribute is useful for interop with other .NET languages and allows assignment/comparison with null.

like image 140
Daniel Avatar answered Sep 21 '22 23:09

Daniel