0

(this scenario doesnt seem to be answered by a previous thread, or at least not in way thats obvious to me, the crucial difference is the recursive requirement for higher order types)

(I've rewritten this question to give something that works...but is painful...rather than vague aspirations around constraints)

I have some code a bit like this (but many many more types)...

type TypeOfOperator = 
    static member typeOf(i : TypeOf<int>) = 1
    static member typeOf(i : TypeOf<string>) = 2
    static member typeOf(i : TypeOf<seq<int>>) = 3 + TypeOfOperator.typeOf(TypeOf<int>())
    static member typeOf(i : TypeOf<seq<string>>) = 3 + TypeOfOperator.typeOf(TypeOf<string>())
    static member typeOf(i : TypeOf<option<int>>) = 4 + TypeOfOperator.typeOf(TypeOf<int>())
    static member typeOf(i : TypeOf<option<string>>) = 4 + TypeOfOperator.typeOf(TypeOf<string>())

let x = TypeOfOperator.typeOf(TypeOf<int>())
let y = TypeOfOperator.typeOf(TypeOf<string>())
let z = TypeOfOperator.typeOf(TypeOf<seq<int>>())
let a = TypeOfOperator.typeOf(TypeOf<seq<string>>())

this code works!...and for atom types (like "int"), this construction is pretty inherent, BUT the formualtion of higher order types (e.g. seq< int >) is formulaic...i.e. a constant "+" some recursive invocation on the parameter type....yet this doesnt seem to be capturable in F#, I have to labouriously manually implement the code.

Ideally the method for option< > would/should be imlement once, something like....

static member typeOf<'a>(i : TypeOf<option<'a>>) = 4 + TypeOfOperator.typeOf(TypeOf<'a>())

i.e. written once...but this clearly doesnt compile.

Is there a way in f# to implement the above method/function?

MrD at KookerellaLtd
  • 2,412
  • 1
  • 15
  • 17

2 Answers2

0

Based on the typeOf<'a> function you showed in your near-answer, how about a different approach? Have you considered something like this:

open System

let typeOf<'a> =
    match Type.GetTypeCode(typeof<'a>) with
    | TypeCode.Int32 -> 1
    | TypeCode.String -> 2
    | _ -> 0

This should return what you're expecting:

typeOf<int> // 1
typeOf<string> // 2

If you need to support types that aren't enumerated in TypeCode, you can use the Type.GUID instead.

Aaron M. Eshbach
  • 6,380
  • 12
  • 22
  • But you wouldn't be able to bind an arbitrarily typed return value, and it wouldn't be known at compile time. – Asti Jul 25 '18 at 19:54
  • @Asti That's correct, if you wrote `typeOf` it would compile and you'd just get `0` at runtime. If getting a compiler error for an unsupported type is a requirement, then pattern-matching on the type metadata will not be a good solution. – Aaron M. Eshbach Jul 25 '18 at 19:58
  • this may work...to a degree..in fact this was where I sort of started...but I need to handle things like seq<'a>...where 'a is a anything...so I COULD take all the types I want to match and also do seq<> of them and option<> of them etc... – MrD at KookerellaLtd Jul 26 '18 at 09:09
0

I've got this to work, and it was based on the previous answer but did require an extra level of pain.

This is the actual code (so the return types arent ints any more...but hopefully other will get the jist)

type T = T with
    static member typeOf(T,i : TypeOf<string>) : IType<string> = stringType()
    static member typeOf(T,i : TypeOf<int>) : IType<int> = intType()
    static member inline typeOf(T,t : TypeOf<seq< ^N >>) : IType<seq< ^N >> = seqType ((^T or ^N) : (static member typeOf: ^T * TypeOf< ^N > -> _) T, TypeOf< ^N >())
    static member inline typeOf(T,t : TypeOf<Option< ^N >>) : IType<Option< ^N >> = optionType ((^T or ^N) : (static member typeOf: ^T * TypeOf< ^N > -> _) T, TypeOf< ^N >())
    static member inline typeOf() : IType< ^N > = ((^T or ^N) : (static member typeOf: ^T * TypeOf< ^N > -> _) T, TypeOf< ^N >())

the first 2 inlines are examples of how to handle recursive calls deconstructing the higher order types to get the params out and invoke methods based on that.

the final typeof is just a nicety to remove this phantom (?) T parameter thats used to bully the type inference engine into looking in the right places.

and we can now call...

let x = T.typeOf<seq<string>>()
MrD at KookerellaLtd
  • 2,412
  • 1
  • 15
  • 17
  • For anyone interested, better syntax for STRPs are on the roadmap to be implemented: https://github.com/fsharp/fslang-design/blob/a02a4667dedb7f98f33c17837ac9fd0ee8e079aa/RFCs/FS-1024-simplify-call-syntax-for-statically-resolved-member-constraints.md – Jwosty Jul 26 '18 at 21:28