How to generalize the f # option?

I get the result of some calculations in the form of 'a option when 'a :> IBaseType . There is a tree of types derived from IBaseType , and I really don’t know which type is this, but what’s important is that it is an option of a specific derived, and not a base type. Therefore, I want to increase it to the IBaseType option in order to process it further. Since the variant is a generic type, it cannot be executed directly (in F #), and I need to do the listing inside Option.map. Nothing complicated, the output type works as expected ... inference ok an intermediate cast option is also allowed as expected ... intermediate inference ok until the function is complete. At this point, for some reason, type inference decided that the original option should already be of type IBaseType option : inference not ok

The intermediate type was already resolved earlier, why did he decide to reassign the deduced type op ? Of course, this leads to runtime exceptions. It looks like a compiler error, but the basic rule is that there are no errors in the compiler.

So, in the end, it sounds very silly: I have no idea how to simply simplify a simple option. To make the image clearer: processResult takes an IBaseType option as an argument. And here is the source of the problematic function:

 (fun (x: obj) -> let op = x :?> _ option let upcastOp = op |> Option.map (fun y -> y :> IBaseType) upcastOp |> processResult) 

Any ideas how to deal with this?

+4
source share
3 answers

I do not know how the op type is called here. But I'm sure that if you cannot change the x type to the IBaseType option , as suggested by kvb, you really need to use reflection.

Alternative reflection based solution:

 let f (x:obj) = match x with | null -> None // x is None | _ -> match x.GetType().GetProperty("Value") with | null -> None // x is not an option | prop -> let v = prop.GetValue( x, null ) Some (v :?> IBaseType) 
+1
source

How do you create box objects in the first place? The simplest solution would be to put the IBaseType option instead of the #IBaseType option box to start with. If this is not possible for some reason, you may have to use reflection. The problem is that in this block of code:

 let op = x :?> _ option let upcastOp = op |> Option.map (fun y -> y :> IBaseType) 

the compiler knows that op is 'a option when 'a :> IBaseType for some 'a , but there is nothing that would allow the compiler to figure out what is actually 'a , because this type is not reflected in the final output of your function - the compiler should fix a specific type for 'a , and the best guess he can make is just the basic type of IBaseType . You will need to do something like this:

 type ResultProcessor = static member ProcessResult<'a when 'a :> IBaseType> (op:'a option) = let upcastOp = op |> Option.map (fun y -> y :> IBaseType) upcastOp |> processResult fun (x:obj) -> let ty = x.GetType() // must be option<something> let [| tyArg |] = ty.GetGenericArguments() typeof<ResultProcessor>.GetMethod("ProcessResult").MakeGenericMethod(tyArg).Invoke(null, [|x|]) 
+3
source

I support kvb solution. Now, in some tests that I did for similar code, for absolute performance, I found an advantage to avoid dynamic (unknown) method calls. Somehow, creating new instances of generic types is faster. For instance:

 [<AbstractClass>] type BaseResultProcessor() = abstract member ProcessResult : obj -> option<IBaseType> [<Sealed>] type ResultProcessor<'T when 'T :> IBaseType>() = inherit BaseResultProcessor() override this.ProcessResult(x: obj) = match x :?> option<'T> with | Some x -> Some (x :> IBaseType) | None -> None module Example = let run (x: obj) = let ty = x.GetType() let tyArg = ty.GetGenericArguments().[0] let p = typedefof<ResultProcessor<_>>.MakeGenericType(tyArg) |> Activator.CreateInstance :?> BaseResultProcessor p.ProcessResult(x) 

As for the problem, the following "intuitive" reasoning is not allowed in .NET:

 'T1 :> 'T2 -------------------------- option<'T1> :> option<'T2> 

I would say that this is typical of type systems - things that seem easy or intuitive are difficult or impossible to implement correctly once you consider their interactions with the type system as a whole.

+1
source

Source: https://habr.com/ru/post/1445832/


All Articles