Why does automatic generalization sometimes produce overly specific types?

Some usage operations do not compile when one side of the operator is of a known type and the other is not. One example is with units:

let inline multiplyWithFive x = 5. * x

type [<Measure>] myUnit
let test = multiplyWithFive 3.<myUnit> // Compiler error

5. * 3.<myUnit>is clearly a valid expression, so this is surprising, especially considering that functions are inlinemaximally generalized in other cases:

let inline multiply a b = a * b
let test = multiply 5. 3.<myUnit> // Valid

However, this is not limited to units. Say I'm making a type that supports asymmetric multiplication using float. This is incompatible with a function multiplyWithFivethat arbitrarily passes its parameter as float:

type BoxFloat =
    { Value : float }

    static member inline (*) (lhs : float, rhs : BoxFloat) =
        { Value = lhs * rhs.Value }

let boxThree = { Value = 3. }

let test2 = multiplyWithFive boxThree // Compiler error

Again, 5. * boxThreeis a valid expression. But automatic generalization does not seem to confirm this.

"" , . , , . , :

// Warning: This construct causes code to be less generic than indicated...
let inline multiplyWithFive (x : 'T) = 5. * x 

? , ?

+4
1

, , F # , , , float. , float :

let inline multiplyWithFive (x:float<_>) = 5. * x

type [<Measure>] myUnit
multiplyWithFive 3.         // Works fine without units
multiplyWithFive 3.<myUnit> // Works fine with units

, - , F # , , float float<_>. , :

// warning FS0077: Member constraints with the name 'op_Multiply' 
// are given special status by the F# compiler 
let inline amultiplyWithFive (x:^T) : ^R =
  (^T : (static member (*) : float * ^T -> ^R) (5.0, x))

// no warning since we're requiring operator **
let inline multiplyWithFive (x:^T) : ^R =
  (^T : (static member ( ** ) : float * ^T -> ^R) (5.0, x))

, - , memebrs. , , , , , .

+5

Source: https://habr.com/ru/post/1663645/


All Articles