How about

1
2
3
4
5
let optType (t:System.Type) =
  if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> then
    Some(t.GetGenericArguments().[0])
  else
    None
By on 1/29/2011 3:34 PM ()

Thanks.

BTW - is if by design (and if so, why) that FSharpType.IsUnion returns true not only for the actual union type, but for all its nested types which are not unions?

E.g.:

1
2
3
4
5
6
7
8
9
10
11
 

type TTT = A of int | B of int

FSharpType.IsUnion( typeof<TTT>)  
// true, as expected

FSharpType.IsUnion( typeof<TTT>.GetNestedTypes().[1])
// also true, why? (CaseInfo's are same as parent)

(This was causing havoc with some recursive type interpretation.)
AFAICT, A union (by default) is implemented by a class that has a nested Tag class and field, and contains (non-union) classes which implement properties - so only the parent type should be returning true here.

As it is, I had to define these:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let (|UnionCases|_|) (t:System.Type) =
  if FSharpType.IsUnion(t) then
    Some <| FSharpType.GetUnionCases( t)
  else
    None

let (|UnionCaseSubTypePropertyType|_|) (t:System.Type) =
  if null <> t.DeclaringType && FSharpType.IsUnion(t.DeclaringType) then
      let p = t.GetProperty("Item")
      if p = null then None else Some p.PropertyType
  else
      None

Where I check for the second AP before the first one - not sure this is fool-proof.
Is there a better way to do this?

I'm also having an issue getting the type of each caseInfo.
What I'm looking for is something like a caseInfo.CaseType property, as we have caseInfo.Tag and caseInfo.Name. This doesn't work:

1
let types = caseInfos |> Array.map (fun ci -> System.Type.GetType( ci.Name)) |> Array.toList

because I assume it wants a fully-qualified name - I could make one, but it seems like a shame to parse a type string.

This works:

1
let types = ty.GetNestedTypes() |> Array.toList |> List.tail // skip first Tag class

But this isn't really optimal either...

Thanks

By on 1/31/2011 5:10 PM ()

I asked Don, who said:

This is by design. The essence is that “IsUnion(someValue.GetType())” returns true if the value is a union value. So this implies the behaviour below. IsFunction does the same thing for closure types.

1
Reflection.FSharpType.IsFunction((box id).GetType())
By on 2/1/2011 10:12 AM ()

Thanks for asking The Man :)

I'm not sure I see the analogy, however. OK, IsFunction can see that the reference is the copied closure at run-time desptite the boxing (loss of static type) - but it's still a cloure representing the same func, "id." (Right?)

In the case of the union, the union class is a much different beast than the sub-classes representing the various union cases. (The fact the the union cases are nested types of the union seem like just an implementation descion - this relationship could be maintained elsewhere, or in the future the CLR could have its own representation for DUs that don't involve sub-typing.)

Related to this is the interesting fact that custom attributes on union cases go on the "constructor methods" of the parent class, not the case class itself. E.g., for:

1
2
3
type Foo = 
      | [<Attr>] AAA of int 
      | BBB or int

the attribute goes not on the nested Foo+AAA class itself where I'd expect it, but on the static psuedo-constructor NewAAA() method on the parent Foo class. Again, the CaseInfo returned by FSharpType.GetUnionCases does not provide a CustomAttributes property - so unless I'm missing something, we must know something about this particular implementation detail of DU's in order to get our attribute out, mainly:

1
2
3
let caseAttributes (t:System.Type) =  // t is type of union case sub-type
     let m = ty.GetMethod("New"+t.Name, BindingFlags.Public |||  BindingFlags.Static)   
     if null = m then [||] else m.GetCustomAttributes(false)

Or search all the parent classes statics for one with the attribute <A title="Microsoft.FSharp.Core.CompilationMappingAttribute.CompilationMappingAttribute(SourceConstructFlags, int);

CTRL+Click to open in new tab." href="[link:www.aisto.com]</A>(<A title="Microsoft.FSharp.Core.SourceConstructFlags CTRL+Click to open in new tab." href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://FSharp.Core:4.0.0.0:b03f5f7f11d50a3a/Microsoft.FSharp.Core.SourceConstructFlags">SourceConstructFlags</A>.<A title="SourceConstructFlags Microsoft.FSharp.Core.SourceConstructFlags.UnionCase; CTRL+Click to open in new tab." href="http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://FSharp.Core:4.0.0.0:b03f5f7f11d50a3a/Microsoft.FSharp.Core.SourceConstructFlags/UnionCase">UnionCase</A>, N), where N=the case tag, which would probably take a little longer but maybe is safer? Seem right? - or am I missing a better way? thanks

By on 2/1/2011 6:30 PM ()

Sorry - Cut/paste re-factor error:

1
2
3
4
5
let caseAttributes (t:System.Type) =  // t is type of union case sub-type

   let m = t.BaseType.GetMethod("New"+t.Name, BindingFlags.Public |||  BindingFlags.Static)

   if null = m then [||] else m.GetCustomAttributes(false)
By on 2/2/2011 10:25 AM ()
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