Компилятор F # требует ссылки на проект, но метод является приватным

Компилятор F # дает ошибку, говоря, что я должен добавить ссылку на проект, потому что тип, который я использую, имеет аргумент метода, который живет в этом проекте. Но этот метод является приватным!

У меня есть следующая структура проекта:

Программа → Библиотека → SubLibrary

SubLibrary содержит следующее:

namespace SubLibrary

type Widget = { Value: int }

Библиотека содержит следующее:

namespace Library

open SubLibrary

type Banana =
    { Value: int }

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

Программа содержит следующее:

open Library

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

    let banana = { Value = 42 }
    0

Я получаю эту ошибку:

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'

Но метод TakeWidget является закрытым!

Я попытался изменить Banana на класс, а не на запись, но это не имело значения.

В качестве эксперимента я создал версию библиотеки С#, называемую библиотекой:

using SubLibrary;

namespace CLibrary {
    public class CBanana {
        int m_value;

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

        private void TakeWidget(Widget w) {
        }
    }
}

Затем я изменил программу на использование CBanana вместо Banana:

open Library

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

    let banana = CBanana 42
    0

Теперь я не получаю сообщение об ошибке. На самом деле, с С# я могу сделать этот метод общедоступным, и пока я не пытаюсь скомпилировать его, нет ошибки.

Почему компилятор настаивает на добавлении ссылки на SubLibrary? Конечно, я мог бы просто пойти и сделать то, что он мне сказал, для спокойной жизни, но SubLibrary - это частная деталь реализации библиотеки, которая не должна подвергаться программе.

Ответ 1

Собственно, когда я пытался с классом вместо записи, он сделал трюк (F # 3.1):

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

Вы можете обойти его и с записью - вам нужно переместить частный член в отдельный модуль и получить его как расширение типа:

type Banana = { Value: int }

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

Компилятор не будет жаловаться на недостающую зависимость, пока не откроется модуль Ext.

У меня нет хорошей идеи, почему компилятор жаловался в первую очередь. Вероятно, один из его причуд. Я не мог найти ничего серьезного подозрительного в сгенерированном ИЛ (кроме удивительного факта, что компилятор F # отмечает как частные, так и внутренние члены как внутренние в ИЛ - это не имело никакого значения здесь).

Ответ 2

Действительно, для меня это выглядит как ошибка, которую я поднял здесь, https://github.com/Microsoft/visualfsharp/issues/86. Таким образом, вы сможете отслеживать отзывы оттуда.