Подтвердить что ты не робот

Тип расширения и видимость элементов в F #

F # имеет функцию под названием "Расширение типа" , которая дает разработчику возможность распространять существующие типы. Существует два типа расширений: внутреннее расширение и дополнительное расширение. Первый из них аналогичен частичным типам на С#, а второй - чем-то похожим на расширение метода (но более мощным).

Чтобы использовать внутреннее расширение, мы должны поместить два объявления в один и тот же файл. В этом случае компилятор объединит два определения в один конечный тип (т.е. Это две "части" одного типа).

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

// SampleType.fs
// "Main" declaration
type SampleType(a: int) =
    let f1 = 42
    let func() = 42

    [<DefaultValue>]
    val mutable f2: int
    member private x.f3 = 42
    static member private f4 = 42

    member private this.someMethod() =
        // "Main" declaration has access to all values (a, f1 and func())
        // as well as to all members (f2, f3, f4)
        printf "a: %d, f1: %d, f2: %d, f3: %d, f4: %d, func(): %d"
            a f1 this.f2 this.f3 SampleType.f4 (func())

// "Partial" declaration
type SampleType with

    member private this.anotherMethod() =
        // But "partial" declaration has no access to values (a, f1 and func())
        // and following two lines won't compile
        //printf "a: %d" a
        //printf "f1: %d" f1
        //printf "func(): %d" (func())

        // But has access to private members (f2, f3 and f4)
        printf "f2: %d, f3: %d, f4: %d"
            this.f2 this.f3 SampleType.f4

Я прочитал спецификацию F #, но не нашел никаких идей, почему компилятор F # различает значения и объявления участников.

В разделе 8.6.1.3 раздела F # spec сказано, что "Функции и значения, определенные определениями экземпляров, лексически ограничены (и, следовательно, неявно частный) к определяемому объекту." . Частичное объявление имеет доступ ко всем частным членам (статические и экземпляры). Я предполагаю, что по спецификации "лексической области" авторы конкретно означают только "главную" декларацию, но это поведение кажется мне странным.

Вопрос в том, является ли это поведение преднамеренным и каким обоснованием оно стоит?

4b9b3361

Ответ 1

Это отличный вопрос! Как вы указали, в спецификации указано, что "локальные значения лексически ограничены объектом, определяемым", но, глядя на спецификацию F #, он фактически не определяет, что в этом случае означает лексическое охват.

Как показывает ваш пример, текущее поведение заключается в том, что лексическая область определения объекта - это просто определение основного типа (исключая внутренние расширения). Я не удивлен этим, но я вижу, что другая интерпретация тоже будет иметь смысл...

Я думаю, что хорошая причина для этого состоит в том, что два типа расширений должны вести себя одинаково (насколько это возможно), и вы должны иметь возможность реорганизовать свой код, используя его, чтобы использовать другой, как вам нужно. Эти два вида отличаются только тем, как они скомпилированы под обложкой. Это свойство будет разбито, если один вид разрешил доступ к лексической области, а другой - нет (потому что члены расширения технически не могут этого сделать).

Тем не менее, я думаю, что это может быть (по крайней мере) уточнено в спецификации. Лучший способ сообщить об этом - отправить электронное письмо на fsbugs в microsoft dot com.

Ответ 2

Я отправил этот вопрос на fsbugs в microsoft dot com и получил следующий ответ от Don Syme:

Привет, Сергей,

Да, поведение преднамеренное. Когда вы используете "let" в области класса, идентификатор имеет лексическую область над определением типа. Значение может даже не помещаться в поле - например, если значение не захватывается никакими методами, оно становится локальным для конструктора. Этот анализ выполняется локально для класса.

Я понимаю, что вы ожидаете, что функция будет работать как частичные классы в С#. Однако он просто не работает таким образом.

Я думаю, что термин "лексический охват" должен быть более четко определен в спецификации, потому что в противном случае текущее поведение было бы удивительным и для других разработчиков.

Большое спасибо Дону за его ответ!