Предпочтительный метод для ссылки на суб-Snaplets - PullRequest
2 голосов
/ 13 января 2012

В Snap Framework Snaplets используются для встраивания функциональности в другие Snaplets через интерфейс на основе компонентов: Основное веб-приложение - это Snaplet, который ссылается на другие Snaplets через классические отношения has-a. и подчиненные Snaplets могут в свою очередь ссылаться на другие Snaplets.

При рассмотрении различных реализаций Snaplet я видел разные шаблоны, используемые для встраивания Snaplet в родительский Snaplet. В частности:

  • Вид справки. Реализация Snaplet предполагает наличие определенного вида связи с родительским Snaplet. Это обеспечивается с помощью справочного метода (см. Ниже).

    1. Простая ссылка:

      data MySnaplet = MySnaplet { subSnaplet :: Snaplet SubSnaplet }
      
    2. Относительная линза:

      data MySnaplet = MySnaplet { _subSnaplet :: Snaplet SubSnaplet }
      
      subSnaplet :: Lens MySnaplet SubSnaplet
      subSnaplet = lens _subSnaplet $ \ a b -> a { _subSnaplet = b }
      
  • Контрольный метод. Реализация Snaplet обеспечивает через свой интерфейс определенный способ доступа к данным Snaplet, и разные реализации Snaplet используют разные методы. Snaplet предполагает, что:

    1. Данные присутствуют в MonadState каждый раз, когда вызывается функция, которая управляет Snaplet.
    2. Данные присутствуют в MonadState и обернуты в Snaplet упаковщик.
    3. Существует экземпляр класса +, такой как instance HasSubSnaplet MySnaplet, который имеет функцию для получения данных Snaplet из MySnaplet, при условии, что MySnaplet находится в MonadState в точке вызова функции.
    4. Функция в 3. имеет тип MySnaplet -> Snaplet SubSnaplet.
    5. Существует экземпляр класса +, подобный 3., который обеспечивает Lens MySnaplet (Snaplet SubSnaplet).
    6. Для экземпляра класса + требуется Lens (Snaplet MySnaplet) (Snaplet SubSnaplet).
    7. Экземпляр класса + предполагает, что MySnaplet является "верхним Snaplet" приложения, и требует абсолютного объектива / эталона, так что MySnaplet должно быть b в MonadSnaplet.

На мой взгляд, ссылочный вид 1. имеет смысл, если Snaplet доступен только для чтения, и 2. имеет смысл, если Snaplet необходимо изменить.

Кроме того, наличие класса для метода имеет смысл, когда MySnaplet может иметь только один SubSnaplet и не более, а наличие абсолютной ссылки может иметь смысл для таких вещей, как базы данных, которые невозможно сконфигурировать как компонент, учитывая, что только верхний Snaplet имеет доступ к учетным данным, а что нет. Однако делать это предположение в качестве автора Snaplet может быть ошибочным, и использование относительных ссылок не будет иметь никаких недостатков.

Однако есть одна проблема: существующие Snaplets на Hackage не соответствуют этим предположениям, которые я делаю; все методы, описанные выше, используются на первый взгляд случайным образом и при любых обстоятельствах. Кроме того, я не вижу никаких преимуществ / недостатков по сравнению с некоторыми другими аспектами, описанными выше (например, требуется оболочка Snaplet или нет).

Мне кажется, что ссылочный вид 2. и один из методов 1, 2, 5 или 6 имеют наибольшее значение при всех обстоятельствах , и я не вижу причин, почему нет единого мнения об использовании только например (2, 1) постоянно.

Итак:

Как пишущий Snaplet, какой метод должен быть предпочтительным при написании нового Snaplet (при условии, что он имеет общее назначение), и

По какой причине все существующие Snaplets не используют один и тот же эталонный метод (даже в базовом пакете snap используется множество различных методов)?

1 Ответ

1 голос
/ 20 января 2012

TLDR; В большинстве случаев вы, вероятно, захотите использовать функцию with и относительные объективы.

Использование класса типов HasSubSnaplet является совершенно необязательным шаблоном, который может уменьшить шаблон «with subSnaplet» в ситуациях, когда не имеет смысла иметь более одного экземпляра SubSnaplet. Мы решили сделать это для оснастки Heist, которая поставляется в пакете оснастки, потому что это имеет смысл для оснастки Heist и предоставляет пользователям пример шаблона.

Поскольку класс типов является полностью необязательным и приблизительно ортогональным к выбору объектива, в оставшейся части этого ответа я сосредоточусь на том, что делать без класса типов.

Назначение API заключается в том, что вы используете линзы (а не "простые ссылки", которые обертывают вас в тип данных Snaplet) для доступа к вашему состоянию. Это связано с тем, что изменение состояния во время обработки запроса является фундаментальной возможностью, которую мы хотели предоставить Snaplets. По большей части, мы намеревались сделать Snaplet непрозрачной оболочкой, к которой конечному пользователю не нужно прикасаться, кроме как в типе состояния его Snaplet. Вот почему экземпляр MonadState возвращает вас непосредственно к вашему типу без оболочки Snaplet.

С учетом вышесказанного существует четыре основных шаблона доступа. Мы можем видеть это, глядя на класс MonadSnaplet .

with     :: Lens     v       (Snaplet v') -> m b v' a -> m b v a
withTop  :: Lens     b       (Snaplet v') -> m b v' a -> m b v a
with'    :: Lens (Snaplet v) (Snaplet v') -> m b v' a -> m b v a
withTop' :: Lens (Snaplet b) (Snaplet v') -> m b v' a -> m b v a

Шаблон линз, воплощенный в первых двух функциях, - это тип линз, которые генерируются для вас автоматически пакетом data-lens-template с TemplateHaskell и являются наиболее естественными для использования. Поэтому это рекомендуемый шаблон. (Отсюда и более короткое, не загрунтованное имя.) Разница между with и withTop заключается в том, что with использует относительную линзу, а withTop - абсолютную. Большую часть времени я использую относительные линзы. Но мы хотели разрешить использование абсолютных объективов, потому что я могу представить сложные приложения, в которых один моментал может потребоваться для чего-то, предоставленного другим механизмом, но который не является потомком текущего механизма.

Иногда возникают ситуации, когда вы хотите иметь возможность использовать личный объектив. Это требует Lens (Snaplet a) (Snaplet b). Таким образом, вторые две загрунтованные функции аналогичны первым двум, за исключением того, что они используют линзы такого типа.

...