20

Is there a way to do somehow overload a function?

Let's take those 3 functions:

// Returns StringPropertyInfo
let stringProperty (expr:Expr<'a -> string>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr

// Returns DatePropertyInfo
let dateProperty (expr:Expr<'a -> System.DateTime>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr

// Returns BytePropertyInfo
let byteProperty (expr:Expr<'a -> System.Byte>) (cfg:EntityInfo<'a>) =
     cfg.Property expr

is there a way to merge them all into just:

let property expr cfg = ....

if not, whats the most neat way to accomplish something similar?

ebb
  • 9,297
  • 18
  • 72
  • 123

4 Answers4

18

If you want to use an approach based on discriminated unions, then I think the declaration is more suitable (as you don't need to manipulate with quotations). Slight modification of the type suggested by Alex is:

type PropertyInfo<'a> =
  | String of Expr<'a -> string>
  | Date of Expr<'a -> System.DateTime>
  | ...

Then you'd write something like:

let property (pi:PropertyInfo<'a>) (cfg:EntityInfo<'a>) =
  match pi with
  | String e -> cfg.Property e
  | ...

cfg |> property (String <@ fun e -> e.Foo @>)

Another option would be to implement property as a static member of a type, in which case you can use usual overloading (similar to C#). Something like:

type EF = 
  static member property (expr:Expr<'a -> string>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr
  static member property (expr:Expr<'a -> System.DateTime>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr
  static member property (expr:Expr<'a -> System.Byte>) (cfg:EntityInfo<'a>) =
     cfg.Property expr

Then you'd write:

cfg |> EF.property <@ e -> e.Foo @>

Finally, you could also make it a bit simpler (but less safer) by making the function fully generic and doing dynamic type test (to decide what return type is used). Something like:

let property<'a, 'r> (e:Expr<'a -> 'r>) (cfg:EntityInfo<'a>) =
  if typeof<'r> = typeof<string> then
    // ...
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Exactly! - However it seems like you cant define generics in a discriminated union? Atleast `| String of Expr<'a -> string>` throws a compiler error saying `The type parameter 'a' is not defined` – ebb Apr 21 '11 at 14:24
  • @ebb: That was my mistake - the updated answer should work (the type `PropertyInfo` needs to be itself generic - so that it can have a type variable `'a` inside). – Tomas Petricek Apr 21 '11 at 14:26
  • 1
    I see.. however it looks like the discriminated union is not a viable option since those I want to overload will return different objects. – ebb Apr 21 '11 at 14:49
12

Short answer, no. There's another question dealing with this here.

You can however overload methods in classes etc. otherwise the .NET integration wouldn't work too well. This is one possible solution albeit not in my eyes, a pretty one.

You could put them in different sub-modules.

Perhaps you could make a generic function and decide the return type through one of the parameters. I'm too much of a newbie when it comes to the type inference to say for certain though.

Community
  • 1
  • 1
Skurmedel
  • 21,515
  • 5
  • 53
  • 66
7

i think a discriminated union is your friend in this instance, you would have something like

type PropertyInfo = 
| StringPropertyInfo of string
| DatePropertyInfo of System.DateTime
| BytePropertyInfo of byte

and then would match on it and do appropriate action returning the union in one function...

Alex
  • 2,342
  • 1
  • 18
  • 30
  • I don't think this will be easy to use. Using this discriminated union, you'd have to work with `Expr<'a -> PropertyInfo>`, but the `Property` method expects a different quotation - so the OP would have to do some pre-processing of quotations to get it working. However, wrapping the whole quotation in a discriminated union should be easy to use. – Tomas Petricek Apr 21 '11 at 14:18
4

One semi-ugly way to get what you're looking for would be to generalize it a bit and use:

let inline property expr cfg =
    (^t : (member Property : Expr<'a -> 'b> -> 'c)(cfg, expr))

However, this can be applied to values of types other than EntityInfo<'a>, as long as they have a Property member with the right signature.

kvb
  • 54,864
  • 2
  • 91
  • 133