4

You can't implement multiple instances of a generic interface in F#. This is a bummer in my case, because I had a plan to implement an interface called ICastableTo<'T> that could be used for pattern matching:

type ICastableTo<'T> =
    /// <summary>
    /// Returns a casted version of the object
    /// </summary>
    member this.Value : 'T

...
(*x:obj*)
match x with
| :? ICastableTo<SomeType> as x -> doSomethingWith(x.Value)
| _ -> invalidOp("can't cast like that")

However, when I tried to actually use this, I face an issue because I can't implement mulitple version of the ICastableTo interface (see Implementing the same interface at different generic instantiations), yet some of my classes are actually castable to more than one type.

What would be my best option here? I could of course define an ICastable interface and use a "PossibleCasts" property that would expose all available casting delegates but that's not super beautiful and does not play that well with inheritance.


Here's what my code looks like:

type BindableFunction<'T,'V>(func : 'T -> 'V) =
    member val Parent : 'T = nothing with get, set
    interface Specializable with
        member SpecializeFor(x: obj) =
            match x with | :? ICastable<'T> as x -> Parent <- x.Value | _ -> invalidOp("")

and then I've my castable classes. For example, I've a TCell class that has a reference to a Cell (composition) and can therefore be casted into a Cell for the purpose of function binding, even if there's no inheritance link between the two types.

I think what I will end up doing is generating the "match x with | ICastable<'T>" to be non-generic (aka, I'll use ICastableOf_Cell and find the good interface using Reflection (get type by name) and then generate the code using Reflection.Emit.

The other option would be to give the BindableFunction two generic types, one being the value type, and the other one ICastableOf_Cell, which is maybe a better idea but would make my code more verbose at various places.

Community
  • 1
  • 1
FremyCompany
  • 2,430
  • 1
  • 14
  • 18
  • One quick question, why is your `x` argument and `object` here, instead of, for example, a generic type? – Patryk Ćwiek Jan 07 '14 at 14:25
  • The argument `x` isn't really an `object`, but it has a type which cannot be casted to a `SomeType` (even if at runtime, it will be the case). In other words, it's a class that inherits from the type of `x` that defines the conversion to `SomeType`, not the type of `x` directly. To use the 'match :?' pattern correctly, you then have to box your object because of another F# limitation, but this is not relevant here. – FremyCompany Jan 07 '14 at 14:40
  • 2
    What about separating conversion code from your type by [implementing a type converter](http://msdn.microsoft.com/en-us/library/ayybcxe5.aspx)? – Daniel Jan 07 '14 at 15:17
  • @Daniel Yes, this is a good idea, and actually what I will probably end up doing if there's no better solution. For now, I had a similar idea that involved using Reflection.Emit to build the type converters dynamically at runtime based on a known property of types that currently implement ICastableTo<...>, but it's not unfeasible to actually create a generic casting function and do the work there. This is still not as elegant as I would like to, but this is getting closer, thanks. – FremyCompany Jan 07 '14 at 15:36
  • 1
    @FremyCompany: Type conversion within a static type system is inherently _inelegant_. If you could provide more details on your use case, and maybe a few type definitions, perhaps other solutions will become apparent. – Daniel Jan 07 '14 at 15:39
  • @Daniel updated description – FremyCompany Jan 08 '14 at 09:36
  • A type converter seems like a good option for your scenario. A series of `if`/`elif` `CanConvertTo` will be no less readable than pattern matching. – Daniel Jan 08 '14 at 15:19

1 Answers1

0

Finally, I went for the following approach:

type BindableFunction<'T,'V>(convert: obj -> 'T, func : 'T -> 'V) =
    member val Parent : 'T = nothing with get, set
    interface Specializable with
        member SpecializeFor(x: obj) =
            match x with 
            | :? 'T as x -> (Parent <- x) 
            | _          -> (Parent <- convert(x))

This allows me to use unique interfaces per type, and therefore not use a generic interface.

let CastToCell = ref(fun(o:obj) -> match o with | :? ICellScope as o -> o.ICell.Cell | _ -> invalidOp("invalid scope conversion"))

with finally:

new BindableFunction<Cell,_>(!CastToCell,...)
FremyCompany
  • 2,430
  • 1
  • 14
  • 18