In the first error message the very general type "'b -> 'a" indicates that at the point of overload resolution the inferred types of the overloaded members are not yet fully known. Overload resolution is based largely on types, hence this is a problem. Adding type annotation information to the arguments of the overloads should resolve the issue, or change their order of declaration in the class so their types are inferred first. (This is just part of the tax of using type inference in the context of defining a set of recursively referential overloads, similar to the tax of putting type annotations on things to resolve the "dot" notation)

However your style of attempting to overload on curried members is unusual and will almost certainly lead to problems. Overload resolution only takes into account the arity and types of the first parameter of a curried member (i.e. the true parameter, and not the parameters of the residual function arising from a curried application).

Note that new F# definitions of overload sets are only permitted by name/arity-of-first-argument by default, and your curried members currently have arity "1", i.e. the first argument is not a syntactic tuple, which is how arity is measued, e.g.

  • "member x.M()" gets arity 0
  • "member x.M(a)" gets arity 1
  • "member x.M(a,b)" gets arity 2

etc. So defining overloads really is biased towards members that take their arguments as a syntactic tuple.

To permit overloads resolved at type-granularity you need to use OverloadID attributes - this helps the type checker match implementations to signatures, among other things. See [link:research.microsoft.com]

Cheers,

Don

By on 1/18/2007 4:00 PM ()

In the first error message the very general type "'b -> 'a" indicates that at the point of overload resolution the inferred types of the overloaded members are not yet fully known. Overload resolution is based largely on types, hence this is a problem. Adding type annotation information to the arguments of the overloads should resolve the issue, or change their order of declaration in the class so their types are inferred first. (This is just part of the tax of using type inference in the context of defining a set of recursively referential overloads, similar to the tax of putting type annotations on things to resolve the "dot" notation)

I did notice that moving the declaration around would increase/decrese the number of errors. Adding type annotations didnt really seem to help but as you suggest that's most likely my use of curried functions.

However your style of attempting to overload on curried members is unusual and will almost certainly lead to problems. Overload resolution only takes into account the arity and types of the first parameter of a curried member (i.e. the true parameter, and not the parameters of the residual function arising from a curried application).

That explains things perfectly. I've refactored the code to eliminate currying and it works properly now. I also see why the constf function worked as it's first argument types were different (int and float).

To permit overloads resolved at type-granularity you need to use OverloadID attributes - this helps the type checker match implementations to signatures, among other things. See [link:research.microsoft.com]

I'n not too clear about what you mean here. What I did find, however, is that I could overload my operators (which weren't shown before) if I added a unique OverloadID to each version. I'n not sure why I have to do this however - it doesnt really seem any different to overloaded members. Anyway this is what I did. Does it look right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    (** Addition.  The two input numbers are converted so that their ranges are compatible then extended by one bit.  The result therefore cannot overflow *)
    [<OverloadID("Add_ufixed_ufixed")>]
    static member (+) ((a : UFixed), (b : UFixed)) = 
      let high, low = UFixed.merge_ranges (a, b)
      let a, b = a.resize ((high + 1), low), b.resize ((high + 1), low)
      let s = UFixed.to_ufixed ((a.signal +: b.signal), (-(min a.low b.low)))
      s

    (** Addition with an integer operand on the right.  The integer operand is converted to the same fixed point format as the left operand *)
    [<OverloadID("Add_ufixed_int")>]
    static member (+) ((a : UFixed), (b : int)) = 
      assert (b >= 0);
      (a + UFixed.to_ufixed ((consti a.Width b), a.Fix))

    [<OverloadID("Add_int_ufixed")>]
    static member (+) ((a : int), (b : UFixed)) = b + a

Thanks for your help,

Andy

By on 1/18/2007 4:45 PM ()

Oops...spoke too soon. While ufixed + int works, int + ufixed doesnt:

hdfs/lib/test/test_fixed.ml(24,39): error: FS0001: This expression has type
UFixed
but is here used with type
int

By on 1/18/2007 4:54 PM ()

There is a restriction on overloaded infix operators such as (+) that basically means the type of the left-hand operand must be the type of the containing type. So TY+int is OK, but int+TY is not. That's just the way it is.

This restriction stems from the way operator overload constraints are propogated, which differs slightly from overload resolution (although it eventually boils down to overload resolution). We are in the process of reviewing this to decide if we want to extend the mechanism to allow the constraints to propogate arbitrary overload behaviour.

cheers,

don

By on 1/18/2007 5:33 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