Not exactly as general as you want but you could use unbox to cast a System.Object object to its most specific type. So, this code works fine

1
2
3
4
5
#light

{ for n in [0 .. 4] -> (box n) }
|> Seq.map unbox 
|> Seq.iter (printfn "%O")

Strangely enough, this one works as well although the runtime cast maps to a "mixed type" sequence.

1
2
3
4
{ for n in [0 .. 4] -> (box n) }
|> Seq.append { for n in [1.1 .. 1.0 .. 4.1] -> (box n) }
|> Seq.map unbox 
|> Seq.iter (printfn "%O")

Shouldn't this code throw a runtime exception because Seq.map effectiely creates a sequence of 4 flaot s and 4 integers? Don: Could you please clarify what is going on here?

Ralf Herbrich

By on 10/22/2007 1:16 AM ()

Above, the unbox has type System.Object -> 'a, where 'a is unconstrained and internal to the function (not appearing in types of args or result). Such 'a are free to be chosen and are defaulted to System.Object. So that sequence contains *boxed* int and float, which are both System.Object typed.

By on 10/22/2007 1:53 AM ()

Makes sense. But, if you remove the

1
|> Seq.iter (printfn "%O")

from the Seq chains, it does throw an exception (or at least fails, since I only tried it in the REPL). I'd hazard a guess that even though the unboxing is introducing a contradiction, the F# compiler is optimizing a lot of it away and not creating that interim 'illegal' structure.

The fact that it is a Seq vs. a list/array/... doesn't seem to make a difference. The same holds when the sequence comprehension is changed to a list comprehension.

Regards,

Z.

By on 10/22/2007 2:37 AM ()

Are you sure the error wasn't a compiler error for 'value type restriction' since values can't have polymorphic types and it would have infered the type seq<'_a> which causes an error since there was no other context available to constrain the type variable '_a.

As long as there is nothing to force it to unbox to either float, int or some other type more specific then object then there won't be any 'illegal' structure created.

By on 10/22/2007 4:52 AM ()

I double-checked the error message, and you're right. It has to do with type constraint, not int/float conflict.

So, I guess I'll go back to my original question. I apparently misunderstood unbox's operation. The few tests I ran with it all had this same problem and required hard-coded casts to work right. Since I'm working with instances that come as obj's, a version of unbox that, instead of performing polymorphic restriction, simply restricted to the "lowest" class would have been perfect.

I guess the best solution would have been some form of pattern matching with polymorphic variants (a la OCaml). Other than that, I don't reall see a dynamic casting solution that can be used without knowing the type names beforehand.

Thanks for the explanations,

Z.

By on 10/22/2007 5:25 PM ()

Hi, regarding the original question -
it is not possible to cast a value of type obj to a type stored as a value of Type. I'm (unfortunatelly) not OCaml expert, so I don't know how polymorphic variants work, but I think there would be some problems with allowing something like this in F#...

Though there are of course situations where it would be useful. For example, I can imagine you would like to do something like this (I needed this some time ago too :-)):

1
2
3
4
5
let foo (val:'a) = // .. some polymorphic function

let typ = // .. some 'Type' value
let o = // .. some 'obj' value
// call foo with 'Type' as a type argument and with 'o' as a argument

This can be done dynamically using System.Reflection classes, but it is not very straightforward and it relies on a lot of implementation details in the F# compiler, though... if you really need it it can help. The biggest issue is that you have to know how F# function declarations are mapped to .NET type system (which is what you're working with in System.Reflection).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
#light
module SomeModule

let foo (a:'a) = 
  printf "Foo <%s> %A\n" (typeof<'a>.Name) a

open System.Reflection

let o = (box 42)
let ty = typeof<int>

let meth = Assembly.GetExecutingAssembly().GetType("SomeModule").GetMethod("foo")
let mi = meth.MakeGenericMethod([| ty |])
mi.Invoke(null, [| o |])

printf "Done!"
By on 10/22/2007 5:57 PM ()

Thank you, everyone, for your answers.

Tomas, your explanation is detailed and informative, as always. I suspected that I'd end up having to use Reflection, so I really appreciate the code you've provided.

I'm reading through the Microsoft.FSharp.Reflection API more carefully, to get a better idea of the way F# interfacts with the rest of .NET.

Regards,

Z.

By on 10/24/2007 11:53 AM ()

to Zakaluka: What you are trying to do doesn't make sense on its own. Presumably the elements of x all have a different type, but if you downcast them all to their dynamic type what is the type of the resultant seq going to be? It can only be seq<Object> anyway. Maybe you could provide a different example that has more context.

By on 10/24/2007 4:27 PM ()

Basically, I am loading plugins of different types from different assemblies for use in a single application. These plugins are grouped by type as they are brought in.

So, when an host application calls into the plugin manager and asks for all plugins of a certain type, I would like to send back a seq<OptionsWindow> or seq<Toolbar> instead of just a seq<obj>.

But, I'm thinking that the operation of that function may change. I've learned a lot about reflection from this discussion which has helped me pretty much finish the library.

As soon as I'm done (some testing and documentation left), I'll post it up somewhere. At that time, if people are interested in it, I'd love to hear comments/suggestions/critiques/etc.

Regards,

z.

EDIT: Sorry, didn't mean my response to sound so abrupt.

By on 10/25/2007 12:10 PM ()

If the types of the plugin groups are known statically (like OptionsWindow and Toolbar) then you could use a function like this:

1
2
3
4
5
6
let pluginList : seq<object> = ...

let selectPlugins<'a> () = pluginList |> Seq.choose (function :? 'a as x -> Some x | _ -> None)

let toolbarPlugins = selectPlugins<Toolbar> ()

Where pluginList is the list of all plugin objects and selectPlugins selects only the plugins that implement or extend the specified type 'a.

Perhaps this helps.

By on 10/25/2007 10:49 PM ()

You know, you may not believe this, but the function you posted sparked a cascade of ideas inside me :). I spent most of the night doing a 30% redesign of the library, and got rid of a lot of redundant stuff.

You are right, the host application will know the plugin types (at least the interfaces) statically. So, such a function would work great. In fact, I'd like to add a similar version of that function to the library with your permission.

I've also removed a lot of useless information that the plug-in manager was keeping around for no reason (types, certain names (strings), etc). At the very least, the library will run faster now as less information is being passed around.

Thanks and regards,

z.

By on 10/26/2007 6:02 PM ()

Sure, go right ahead. I'm glad I could be so much help.

I think you can pretty much assume all code posted on this forum is free to use.

Best of luck,

Greg

By on 10/29/2007 3:28 AM ()

Regarding the 'value restriction' issues & unbox behavior - the type of unbox function is obj -> 'a, which means that the target type of the conversion will be inferred by the F# compiler from the context. For this reason the following causes 'value restriction':

1
2
3
4
5
 
let sth = 
  { for n in [0 .. 4] -> (box n) }
  |> Seq.append { for n in [1.1 .. 1.0 .. 4.1] -> (box n) }
  |> Seq.map unbox 

Because the type of sth is inferre as seq<'a>, which isn't possible (a value must have a known type). As far as I understand it, in the example that contained printf "%O" (which has a type 'a -> unit) the F# compiler used a default type as a type argument - the actuall type argument wasn't restricted in any way, so the F# compiler just used obj. Though, I don't fully understand how exactly this works...

By on 10/22/2007 6:08 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper