В мире OO у меня есть класс (назовем его «Suggestor»), который реализует что-то, приближающееся к «Шагу стратегии», чтобы обеспечить различные реализации алгоритма во время выполнения. Как упражнение в обучении Haskell, я хочу переписать это.«Шаблон стратегии» в Haskell
Фактический прецедент довольно сложный, поэтому я рассмотрю более простой пример.
Предположим, у меня есть класс Suggester
, который принимает список правил и применяет каждое правило в качестве фильтра к списку результатов базы данных.
Каждое правило состоит из трех этапов: «Build Query», «Post Query Filter» и «Scorer». Мы по существу в конечном итоге с интерфейсом встречи следующие
buildQuery :: Query -> Query
postQueryFilter :: [Record] -> [Record]
scorer :: [Record] -> [(Record, Int)]
SUGGESTOR должен принять список правил, которые соответствуют этот интерфейс - динамически во время выполнения - и затем выполнять их в последовательности. buildQuery() должен сначала запускаться по всем правилам, а затем postQueryFilter, а затем и scorer. (т. е. я не могу просто составить функции для одного правила в одну функцию).
в Скале я просто сделать
// No state, so a singleton `object` instead of a class is ok
object Rule1 extends Rule {
def buildQuery ...
def postQueryFilter ...
def scorer ...
}
object Rule2 extends Rule { .... }
И тогда можно инициализировать службу, передав соответствующие правила через (Defined во время выполнения на основе пользовательского ввода).
val suggester = new Suggester(List(Rule1, Rule2, Rule3));
Если правила были единой функцией, это было бы просто - просто передайте список функций. Однако, поскольку каждое правило на самом деле является тремя функциями, мне нужно как-то сгруппировать их, поэтому у меня есть несколько реализаций, соответствующих интерфейсу.
Моей первой мыслью были классы классов, однако они, похоже, не соответствуют моим потребностям - они ожидают переменную типа и обеспечивают, чтобы каждый из моих методов использовал ее, а это не так.
No parameters for class `Rule`
Моя вторая мысль была просто поместить каждый из них в модуле Haskell, но модули не являются «Первый класс» Я не могу передать их вокруг непосредственно (и они, конечно, не применять интерфейс).
В-третьих, я попытался создать тип записи, чтобы инкапсулировать функции
data Rule = Rule { buildQuery :: Query -> Query, .... etc }
А затем определяется экземпляр «Правило» для каждого. Когда это делается в каждом модуле, он инкапсулирует красиво и отлично работает, но чувствует себя взломанным, и я не уверен, что это подходящее использование записей в haskell?
tl; dr - Как я могу инкапсулировать группу функций, чтобы я мог передавать их как экземпляр чего-то, соответствующего интерфейсу, но на самом деле не использую переменную типа.
Или я полностью исхожу из-за неправильного мышления?
Каким образом делает это чувство как взломать? Я думаю, что это вполне приемлемый способ использования записи. – YellPika
Записи Haskell выглядят так, будто они предназначены для хранения данных, а не функций. Является ли приемлемым идиоматический Haskell обходить функции как часть записи? Я, возможно, ослеплен сходством между записями и классами дел и слишком большим весом для идеи OO, что для этого есть конкретная конструкция. –
Функции - это данные. I.e., они являются первоклассными данными. Они могут быть переданы, возвращены и сохранены. Когда это больше не кажется странным, вы будете хорошо на пути к просветлению ОО. Функции OO обычно не являются первоклассными данными, поэтому мы переносим функции в объекты, как в шаблонах Command и Strategy. –