Цитаты F#, массивы и самоидентификатор в конструкторах

Я думаю, что это известное ограничение F#, но я не смог найти хороших обходных путей…

Итак, вот код (я старался сделать его как можно проще, поэтому, вероятно, он выглядит бессмысленным):

[<ReflectedDefinition>]
type Human (makeAName: unit -> string) as self =
    let mutable cats : Cat array = [| |]
    do
        // get a cat
        cats <- Array.append cats [| new Cat (self, makeAName ()) |]
    member this.Cats = cats
and
 [<ReflectedDefinition>]
 Cat (owner : Human, name : string) = class end

Компилятор говорит:

ошибка FS0452: цитаты не могут содержать встроенный ассемблерный код или сопоставление шаблонов в массивах

На самом деле это комбинация as self и получателя свойств массива, которая все ломает.

Пункты здесь:

  • Я действительно хочу использовать массивы, потому что хочу, чтобы WebSharper переводил мои коллекции в массивы JavaSript.
  • Мне действительно нужен самоидентификатор в конструкторах.
  • Мне очень нужны классы (т.е. функциональный стиль не сработает).
  • Самоидентификаторы для каждого метода (member this.Foo) работают нормально.

Один обходной путь, который я могу придумать, — сделать конструкторы закрытыми и использовать статические методы для создания объектов. Таким образом, мне не нужно as self. Но это просто глупо.

Есть ли лучшие варианты?


Обновление:

Вот еще более простой пример:

[<ReflectedDefinition>]
type User (uid: int) as self =
    let ROOT_UID = 0
    member this.isRoot = (uid = ROOT_UID)

С as self я даже не могу определить константу класса. Ну, это вообще-то отдельный вопрос, но я задам его здесь: как мне определить константу класса в данном конкретном случае?



person kirelagin    schedule 28.03.2013    source источник


Ответы (1)


Я вовсе не считаю это глупостью. На самом деле мы предпочитаем статические методы конструктора для ясности, даже в коде, который не использует WebSharper. Во всей кодовой базе IntelliFactory мы редко, если вообще используем self.

Вы сталкиваетесь с двумя раздражающими ограничениями компилятора F# и кавычек. Как вы заметили, статические методы могут решить проблему self:

[<ReflectedDefinition>]
type Human private (cats: ref<Cat []>) =
    member this.Cats = !cats

    static member Create(makeAName: unit -> string) =
        let cats = ref [| |]
        let h = Human(cats)
        let cat = Cat(h, makeAName())
        cats := [| cat |]
        h

and [<ReflectedDefinition>] Cat (owner: Human, name: string) =
    class
    end

Есть много других способов добиться этого, например, вы можете избавиться от ref косвенности.

Во-вторых, вы часто получаете FS0452 в ReflectedDefinition коде с операциями с массивами, даже в простых статических методах. Обычно это можно решить, используя библиотечные функции вместо прямого доступа к массиву (Array.iter, Array.map).

Для второго примера вам действительно нужно это:

[<ReflectedDefinition>]
module Users =

    [<Literal>]     
    let ROOT_UID = 0

    type User(uid: int) =
        member this.isRoot = (uid = ROOT_UID)

Аннотация [<Literal>] позволит вам сопоставлять шаблоны с вашими константами, что может быть удобно, если их несколько.

Для ваших баллов:

  1. Я действительно хочу использовать массивы - это должно быть в порядке
  2. Мне очень нужен самоидентификатор - он никогда не нужен, как и конструкторы.
  3. Мне очень нужны классы (т.е. функциональный стиль не сработает) - точно не так
  4. Самоидентификаторы для каждого метода (член this.Foo) работают нормально — да, и полезны.
person t0yv0    schedule 28.03.2013
comment
Статические конструкторы — прекрасно. Говоря о втором примере, да, константа модуля действительно работает, но на самом деле это константа класса, поэтому я хочу добавить ее в класс. Наличие констант класса внутри классов (и отсутствие загрязнения пространства имен модулей) кажется разумным. - person kirelagin; 29.03.2013