Предположим, что у меня есть два типа данных Foo и Bar. Foo имеет поля x и y. Бар имеет поля x и z. Я хочу, чтобы иметь возможность написать функцию, которая принимает либо Foo, либо Bar в качестве параметра, извлекает значение x, выполняет некоторые вычисления на нем, а затем возвращает новый Foo или Bar с установленным значением x.
Вот один из подходов:
class HasX a where
getX :: a -> Int
setX :: a -> Int -> a
data Foo = Foo Int Int deriving Show
instance HasX Foo where
getX (Foo x _) = x
setX (Foo _ y) val = Foo val y
getY (Foo _ z) = z
setY (Foo x _) val = Foo x val
data Bar = Bar Int Int deriving Show
instance HasX Bar where
getX (Bar x _) = x
setX (Bar _ z) val = Bar val z
getZ (Bar _ z) = z
setZ (Bar x _) val = Bar x val
modifyX :: (HasX a) => a -> a
modifyX hasX = setX hasX $ getX hasX + 5
Проблема заключается в том, что все те геттеры и сеттеры очень больны, особенно если я заменяю Foo и Bar реальными типами данных, у которых много полей.
Синтаксис записи Haskell дает гораздо лучший способ определения этих записей. Но, если я попытаюсь определить записи, подобные этому
data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show
Я получу ошибку, говоря, что x определяется несколько раз. И я не вижу никакого способа сделать эту часть класса типа, чтобы я мог передать их для измененияX.
Есть ли хороший чистый способ решить эту проблему, или я придерживаюсь определения моих собственных геттеров и сеттеров? Иными словами, существует ли способ подключения функций, созданных синтаксисом записи, с помощью классов типов (как для геттеров, так и для сеттеров)?
ИЗМЕНИТЬ
Вот реальная проблема, которую я пытаюсь решить. Я пишу серию связанных программ, которые используют System.Console.GetOpt для анализа своих параметров командной строки. В этих программах будет много опций командной строки, но некоторые из них могут иметь дополнительные опции. Я хочу, чтобы каждая программа могла определять запись, содержащую все ее значения параметров. Затем я начинаю с значения записи по умолчанию, которое затем преобразуется через монаду StateT и GetOpt, чтобы получить окончательную запись, отражающую аргументы командной строки. Для одной программы этот подход работает очень хорошо, но я пытаюсь найти способ повторного использования кода во всех программах.