Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline plus operator in struct throws an exception (F#)

Why does the fail value throw an exception? fine value works. If I remove inline or if I convert 't into float then it works.

[<Struct>]
type Test<'t> =
  val x: 't
  val y: 't
  new (x,y) = { x = x; y = y }

  static member inline (+) ((x,y), a: _ Test) = 0
  static member inline (-) ((x,y), a: _ Test) = 0

let a = 1.,2.
let b = Test(1.,2.)
let fine = a - b
let fail = a + b

error message:

Unhandled Exception: System.TypeInitializationException: The type initializer fo r 'AdditionDynamicImplTable3' threw an exception. ---> System.NotSupportedExcep tion: Dynamic invocation of op_Addition involving coercions is not supported. at Microsoft.FSharp.Core.LanguagePrimitives.dyn@2445[a,b,c](Type aty, Type bt y, Unit unitVar0) at Microsoft.FSharp.Core.LanguagePrimitives.AdditionDynamicImplTable3..cctor () --- End of inner exception stack trace --- at Microsoft.FSharp.Core.LanguagePrimitives.AdditionDynamic[T1,T2,TResult](T1 x, T2 y) at .$Program.main@() in C:\Users\olsv\Docume nts\Visual Studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\Program. fs:line 14 Press any key to continue . . .

like image 920
Oldrich Svec Avatar asked Apr 19 '13 10:04

Oldrich Svec


1 Answers

This looks like a bug in the compiler - or I am missing something (please report it at fsbugs at microsoft dot com). For some reason, the compiler fails to inline the call to the + operator (it seems to work for - and / and for custom operators like +., but fails for + and *).

This means that the compiler actually generates something like:

// Inlined - returns zero directly
fine = 0; 

// Failed to inline - calls a version which used dynamic lookup
fail = LanguagePrimitives.AdditionDynamic
         <Tuple<double, double>, Test.Test<double>, double>(a, b);

The AdditionDynamic method uses some internal table to find an implementation of + for the two types at runtime. Although you could register your type there, it would not really be useful, because the invocation would be slow.

I do not really have any nice workaround for this - if you need the operator only for some basic numeric types (float, int, etc.) then the easiest option might be to just avoid using inline here and define (an overloaded) operator for the specific types:

 static member (+) ((x:float,y:float), a: float Test) = x + y + a.x + a.y 

You can also try the trick with global operator and a helper type that implements the different overloads, but I'm not sure if that's going to help: see, for example, this past question.

like image 88
Tomas Petricek Avatar answered Sep 19 '22 18:09

Tomas Petricek