Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# compiler requires project reference, but method is private

The F# compiler gives an error saying I must add a project reference because the type I'm using has a method argument that lives in that project. But this method is private!

I have the following project structure:

Program -> Library -> SubLibrary

SubLibrary contains this:

namespace SubLibrary

type Widget = { Value: int }

Library contains this:

namespace Library

open SubLibrary

type Banana =
    { Value: int }

    member private x.TakeWidget (w: Widget) = ()

Program contains this:

open Library

[<EntryPoint>]
let main argv = 
    printfn "%A" argv

    let banana = { Value = 42 }
    0

I get this error:

error FS0074:
The type referenced through 'SubLibrary.Widget' is defined in an assembly that is not referenced.
You must add a reference to assembly 'SubLibrary'

But the TakeWidget method is private!

I tried changing Banana into a class, rather than a record, but that made no difference.

As an experiment, I created a C# version of Library, called CLibrary:

using SubLibrary;

namespace CLibrary {
    public class CBanana {
        int m_value;

        public CBanana(int value) {
            m_value = value;
        }

        private void TakeWidget(Widget w) {
        }
    }
}

Then I changed Program to use CBanana instead of Banana:

open Library

[<EntryPoint>]
let main argv = 
    printfn "%A" argv

    let banana = CBanana 42
    0

Now I don't get an error. In fact, with C# I can make that method public, and so long as I don't try to compile a call to it, there is no error.

Why is the compiler insisting I add a reference to SubLibrary? Sure, I could just go ahead and do what it tells me to do, for a quiet life, but SubLibrary is a private implementation detail of Library, which should not be exposed to Program.

like image 605
bananasareyellow Avatar asked Nov 28 '14 11:11

bananasareyellow


2 Answers

Actually, when I tried with a class instead of record, it did the trick (F# 3.1):

type BananaClass (value:int) = 
    member private x.TakeWidget (w: Widget) = ()
    member x.Value = value

You can work around it with a record as well - you need to move the private member into a separate module and have it as a type augmentation:

type Banana = { Value: int }

module Ext =
    type Banana with
        member x.TakeWidget (w: Widget) = ()

Compiler won't complain about the missing dependency until you open the Ext module.

I don't have a good idea why the compiler was complaining in the first place. Probably one of its quirks. I couldn't find anything seriously suspicious in the generated IL (other than a surprising fact that F# compiler marks both private and internal members as internal in the IL - this turned out to be of no consequence here).

like image 174
scrwtp Avatar answered Nov 15 '22 16:11

scrwtp


Indeed this to me looks like an error I have raised an issue here, https://github.com/Microsoft/visualfsharp/issues/86. So you'll be able to track feedback from there.

like image 23
Colin Bull Avatar answered Nov 15 '22 17:11

Colin Bull