0
let o1 = box SomeType()
let t = typeof<SomeType>

Is it possible to downcast (to SomeType) a boxed object (o1) using the Type information stored in other object (o1)?

The ultimate objective is to have a sort of dynamic invocation of functions. I'm storing functions with signature FSharpFunc<'Pre,'Post> in a Map:

// Lack of Covariance/Contravariance force me to define it as obj:
let functions = Map<string,obj>

let invoke f (pre : 'Pre when 'Pre : comparison) (post : 'Post when 'Post : comparison) =
    (unbox<FSharpFunc<'Pre,'Post>> f).Invoke(pre)

This dynamic invocation works whenever I pass the proper types objects in pre and post.

And know comes the issue. I also has the arguments of the invocation in a map of the form:

let data = Map<string,obj>
let conf = Map<string, Type>

where conf stores the type of each possible string key in data.

So given a function key and a proper configuration, I can retrieve the arguments from data in order to feed the function. But for these to work I should be able to downcast data values using conf Types.

I suspect that it is not possible and I'm aware that I am bypassing static type safety (I'm ok with that). In that case, Any workaround or alternative approach?

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
  • 2
    I think it would be easiest to just invoke the `Invoke` function with reflection. Then you don't even need to cast. – John Palmer Aug 25 '14 at 12:44
  • Yes, it might be the best option. I was just trying to stick to static typing as much as I could, hoping also a better performance. – jruizaranguren Aug 26 '14 at 05:50

1 Answers1

2

I'm not sure I understand what you are after here, so this is not a specific answer to your question, but rather a couple of suggestions that might help you.

Generally speaking it sounds like you want some sort of existential types. It sounds like

  • you have data of various types and

  • you have operations on that data and

  • you want to dynamically invoke those operations on the data.

To make such things safely, you should encapsulate the data (or ideally the type of the data) and the operations together rather than separately. At the point when you know the type of the data and the possible operations on the data, wrap them together so that other parts of your program cannot just take the data and try to unsafely perform arbitrary operations on the data. (To make such encapsulation general and safe, allowing type safe manipulation of data whose type is not know statically, you need something like first-class modules.)

As another suggestion, rather than boxing whole functions, you might rather want to box and unbox the domains and ranges of functions. Consider the following wrap and unwrap functions:

let wrap (a2b: 'a -> 'b) : obj -> obj =
  unbox<'a> >> a2b >> box<'b>

let unwrap (o2o: obj -> obj) : 'a -> 'b =
  box<'a> >> o2o >> unbox<'b>

The function map would have the signature

val functions: Map<string, obj -> obj>

and would store wrapped functions. To invoke a function from the map, you would unwrap the previously wrapped o2o function with the desired type:

(unwrap o2o : 'a when 'a: comparison -> 'b when 'b: comparison)

This is not type safe as such, but allows for flexible invocations.

  • Thank you for the wrap/unwrap suggestion. – jruizaranguren Aug 26 '14 at 11:54
  • It is more or less like you explained. I have a dynamically built undirected graph of operations indexed by string key, and also a dictionary of data (different for each execution). But data types can be reused and combined in different operations. I get the function signatures at startup using reflection (types and names of arguments) which would allow me to ensure type correctness out of the compiler. First-class modules in F#? – jruizaranguren Aug 26 '14 at 12:05
  • 1
    Note that existential types can be faithfully represented in F# via a "double negation" trick (though it's clunky). See http://stackoverflow.com/questions/16284680/expressing-existential-types-in-f. – kvb Aug 26 '14 at 16:04
  • @Vesa.A.J.K Also, if returned 'b type is not required, I can avoid generic arguments and let the compiler to infer the types by implementing invoke the following way (completing the solution): let invoke ( boxed : obj) ( f : obj -> obj) = unwrap f boxed |> box – jruizaranguren Aug 28 '14 at 06:24
  • @kvb I'm struggling with the application of existential types to this particular problem. If I understood correctly, May I be able to avoid working with (obj, obj->obj) by using existential type signatures?. Should I open a new question in order to dig in this approach? – jruizaranguren Aug 28 '14 at 06:33
  • In this second unsafe version of invoke, unwrap is not needed. let invoke ( boxed : obj) ( f : obj -> obj) = f boxed |> box – jruizaranguren Aug 28 '14 at 08:14
  • @jruizaranguren - I was just nitpicking Vesa's post, not suggesting you actually use existential types. It's not entirely clear to me what you're trying to do (or, more precisely, what the bigger picture issue that you're trying to solve is - why do you have these collections of arbitrarily co-mingled stuff?), so it's hard for me to suggest any particular solution. – kvb Aug 28 '14 at 14:12
  • @kvb I explained the context in the following SO question with no much success: http://stackoverflow.com/questions/24932086/specify-function-composition-through-declarative-maps-in-f With Vesa's suggestion and regular F# I think I can complete my design, but existential types appears like a nice thing to explore. – jruizaranguren Aug 28 '14 at 16:08