Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the F# compiler fail with this infix operator?

Tags:

f#

I have a class and a record defined like so:

namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member (+) (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type internal Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = myRecord1 + myRecord2 //Does not compile
            0

This fails to compile with:

The member or object constructor 'op_Addition' is not public. Private members may only be accessed from within the declaring type. Protected members may only be accessed from an extending type and cannot be accessed from inner lambda expressions.

Both types are internal. If I explicitly set the + operator to public, that doesn't help either:

static member public (+) (left : MyRecord, right : MyRecord) : MyRecord

What does work is just forgoing use of an operator and using a static method:

namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member Add (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type internal Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = MyRecord.Add(myRecord1, myRecord2) //Does compile
            0

Why does the F# compiler struggle with using an operator in this case when using a named member work just fine?

Changing both types to be public instead of internal resolves the compilation error, too.

I'm using Visual Studio 2012 with F# 3.0 targeting .NET Framework 3.5.

like image 412
vcsjones Avatar asked Jul 04 '13 17:07

vcsjones


1 Answers

I do not know why F# compiler has this problem. This is probably connected to the way operators are handled in F# or maybe how accessibility is handled. You must remember that in this language not everything is what it seems. Some "object oriented" features have been achieved by making some sacrifices. Maybe this is one of them.

But. I know how to resolve this :). Do not make your types internal in implementation file. Instead use Signatures. Define file Foo.fsi like this:

namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }

    [<Class>]
    type Bar =
        member Baz : unit -> int

and Foo.fs like this:

namespace Foo
    type MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member (+) (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = myRecord1 + myRecord2 //Compiles
            0

This makes your code valid and MyRecord internal.

like image 61
Grzegorz W Avatar answered Oct 13 '22 05:10

Grzegorz W