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

17

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

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

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

// 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 сказал, что «Функции и значения, определенные определениями экземпляра, лексически ограничены (и, следовательно, неявно частные) для определяемого объекта.". Частичное объявление имеет доступ ко всем частным членам (статические и экземпляры). Я предполагаю, что по спецификации «лексической области» авторы конкретно означают только «главную» декларацию, но это поведение кажется мне странным.

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

    
задан Sergey Teplyakov 05.04.2013 в 10:47
источник

2 ответа

10

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

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

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

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

    
ответ дан Tomas Petricek 05.04.2013 в 13:35
  • Я получил ваше мнение о сходстве между двумя типами расширений типов. Но поведение внутренних и дополнительных расширений различно: дополнительные расширения могут иметь доступ только к открытой поверхности расширяемого типа, но внутренние расширения по-прежнему имеют доступ к закрытым и защищенным членам расширяющегося типа. Таким образом, у них была другая семантика, а не только то, как они составлялись под обложкой. –  Sergey Teplyakov 05.04.2013 в 15:24
  • @SergeyTeplyakov Хм, это хороший момент. Вы правы, что они отличаются в этом аспекте. Я полагаю, что ключевым моментом является определение «лексической области» в этом контексте (что явно отсутствует в спецификации F #). –  Tomas Petricek 05.04.2013 в 16:11
  • Спасибо, Томас. Я думаю, вы правы в лексической сфере. Я хочу сказать, что частичные типы на C # хорошо известны и широко используются для поддержки дизайнеров. Но с таким ограничением мы не можем рассматривать внутреннее расширение как полнофункциональное объявление частичного типа. И это означает, что мы не можем получить поддержку дизайнеров (для WinForms и WPF) до тех пор, пока внутренние расширения не будут иметь таких ограничений. –  Sergey Teplyakov 05.04.2013 в 16:35
3

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

  

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

     

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

     

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

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

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

    
ответ дан Sergey Teplyakov 13.04.2013 в 10:51