Оперировать значениями внутри структурно схожих типов в Haskell

Извините за мой крайне ограниченный Haskell-fu.

У меня есть серия data типов, определенных в разных модулях, которые структурированы одинаково:

-- in module Foo
data Foo = Foo [Param]

-- in module Bar
data Bar = Bar [Param]

-- * many more elsewhere

Я хотел бы иметь набор функций, которые работают со списком параметров, например, для добавления и удаления элементов из списка (возвращая новый Foo или Bar с другим списком параметров, в зависимости от ситуации).

Насколько я могу судить, даже если я создам класс типов и экземпляры для каждого типа, мне нужно будет каждый раз определять все эти функции, то есть:

-- in some imported module

class Parameterized a where
    addParam :: a -> Param -> a
    -- ... other functions

-- in module Foo

instance Parameterization Foo where
    addParam (Foo params) param = Foo (param:params)
    -- ... other functions

-- in module Bar

instance Parameterization Bar where
    -- this looks familiar...
    addParam (Bar params) param = Bar (param:params)
    -- ... other functions

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

Чтобы опровергнуть возможную аргументацию: да, я знаю, что мог бы просто иметь один набор функций (addParam и т. Д.), Который явно перечислял бы каждый конструктор и сопоставление с шаблоном, поместите параметры - но поскольку я строю это довольно модульно ( модули Foo и Bar довольно автономны), и я создаю прототип системы, в которой будут десятки этих типов, подробный централизованный список конструкторов типов кажется ... неправильным.

Вполне возможно (вероятно?), Что мой подход просто ошибочен и что это не совсем правильный способ структурировать иерархию типов, но поскольку у меня не может быть где-то одного типа data и добавить новый конструктор для тип в каждом из этих модулей (?) Я не понимаю, как добиться приятного ощущения «плагина» без необходимости каждый раз переопределять простые служебные функции. Любые добрые предложения с радостью принимаются.


person Bruce Williams    schedule 20.10.2009    source источник
comment
почему у вас есть эти типы Foo Bar, а не просто списки?   -  person yairchu    schedule 20.10.2009


Ответы (3)


Вы можете иметь реализации функций по умолчанию в классе типов, например

class Parameterized a where
    params :: a -> [Param]
    fromParams :: [Param] -> a
    addParam :: a -> Param -> a
    addParam x par = fromParams $ par : params x
    -- ... other functions

instance Parameterized Foo where
    params (Foo pars) = pars
    fromParams = Foo

Однако ваш дизайн действительно выглядит подозрительно.

person Alexey Romanov    schedule 20.10.2009

Вы можете использовать новый тип для Foo и Bar, а затем автоматически получить реализацию на основе базовой структуры (в данном случае списков) с помощью -XGenerializedNewtypeDeriving.

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

person Don Stewart    schedule 20.10.2009

На вашем примере сложно сказать, что было бы правильным.

Зачем вам нужны и Foo, и Bar, если они структурно похожи? Разве ты не можешь просто пойти с Foo?

Если по какой-то причине я не вижу, вам нужны и Foo, и Bar, вам придется использовать класс типа, но вы можете очистить код с помощью Template Haskell.

Что-то вроде

$(superDuper "Foo")

мог сгенерировать код

data Foo = Foo [Param]

instance Parameterization Foo where
    addParam (Foo params) param = Foo (param:params)

куда

superDuper :: String -> Q [Dec]
superDuper n =
  let name = mkName n
      dataD = DataD [] name [] [NormalC name [] ] -- correct constructor here
      instD = InstanceD [] ... -- add code here
  return [dataD, instD]

Это, по крайней мере, избавило бы от кодирования плиты котла.

person finnsson    schedule 20.10.2009
comment
Я уже некоторое время хотел попробовать Template Haskell; спасибо за этот пример. - person Bruce Williams; 22.10.2009