Шаблон Haskell с именем поля записи в качестве переменной? - PullRequest
9 голосов
/ 12 декабря 2011

У меня есть следующий фрагмент кода, который реализует монаду. Я пытаюсь использовать его для упрощения настройки полей с более сложной логикой позже.

data Rec = Rec {
    alpha :: Int,
    beta  :: Double,
} deriving (Show)
defaultRec = Rec 0 0 0

data Record r = Record { runRecord :: Rec -> (Rec, r) }
instance Monad Record where
    return r = Record $ \s -> (s, r)
    a >>= b  = Record $ \s -> let (q, r) = runRecord a s in runRecord (b r) q

createRecord f = fst $ runRecord f defaultRec

changeAlpha x  = Record $ \s -> (s { alpha = x }, ())

Я бы использовал такой код:

myRecord = createRecord (changeAlpha 9)

Этот код работает, но я бы хотел использовать Template Haskell для упрощения функции changeAlpha. Было бы здорово иметь что-то вроде этого:

changeBeta x = $(makeChange beta) x

Теперь я дошел до этого:

changeBeta x = Record $ $([| \z -> \s -> (s { beta = z }, ()) |]) x

Но как только я изменю это на:

changeBeta f x = Record $ $([| \z -> \s -> (s { f = z }, ()) |]) x

Я получаю это:

TestTH.hs:21:49: `f' is not a (visible) constructor field name

Нет вариантов работы. Это возможно?

Ответы [ 3 ]

6 голосов
/ 12 декабря 2011

Проблема в том, что вы можете объединять только типы, выражения или списки объявлений.Метка поля записи не является ни одной из них, поэтому вам придется использовать комбинаторы TH, чтобы создать выражение типа Q Exp, а затем соединить его, хотя вы можете использовать скобки Оксфорда для остальных частей:

makeChange :: Name -> Q Exp
makeChange x = [|
    \z -> Record $ \s -> ( $(recUpdE [| s |] [fieldExp x [| z |]]), () ) |]

Чтобы использовать его, вам нужно будет указать название поля, которое вы хотите изменить:

changeBeta :: Double -> Record ()
changeBeta x = $(makeChange 'beta) x
4 голосов
/ 12 декабря 2011

Разве вы не заново изобретаете монаду и объективы State (T)?

http://hackage.haskell.org/packages/archive/data-lens/2.0.2/doc/html/Data-Lens-Strict.html

Существует код шаблона линз данных, который генерирует линзы для каждой «записи», начиная с _в виде.

1 голос
/ 12 декабря 2011

Я думаю f это просто имя; вам нужно его «снять в кавычки», так как это из среды, а не просто внутри кавычек [| |]. Я нашел пример этого здесь [ ссылка , см. Раздел «10.1.1 Выбор из кортежа»]. В кавычках используется $, поэтому я думаю, что ваш окончательный код будет выглядеть примерно так:

changeField f = [| \z s -> s { $(varE f) = z } |]

К сожалению, моя версия ghc (7.0.3), похоже, жалуется на $. (Это дает ошибку разбора.) Надеюсь, что этот вопрос получит больше внимания, мне кажется, это хорошо (хотя, возможно, удалите несвязанную часть монады).

...