Контекст
Если у нас есть
data Foo = Foo { x :: Maybe Int, y :: Maybe Text }
мы уже можем построить его в аппликативном стиле в аппликативном контексте (здесь IO), как
myfoo :: IO Foo
myfoo = Foo <$> getEnvInt "someX" <*> getEnvText "someY"
Проблема
Что, если кто-то предпочитает строить с явным написанием имен полей записи? Такие как:
myfoo = Foo { x = getEnvInt "someX", y = getEnvText "someY" }
Это не будет проверять тип. Одно решение
{-# LANGUAGE RecordWildCards #-}
myfoo = do
x <- getEnvInt "someX"
y <- getEnvText "someY"
return $ Foo {..}
Что неплохо. Но мне интересно (на данный момент только ради себя), может ли работать следующее:
data FooC f = FooC { x :: f Int, y :: f Text }
type Foo = FooC Maybe
myfoo :: IO Foo
myfoo = genericsMagic $ FooC
{ x = someEnvInt "someX"
, y = someEnvText "someY"
}
Я считаю, что это можно сделать с помощью простого сопоставления шаблонов GHC.Generics
, но это не будет обеспечивать безопасность типов, поэтому я искал более сильный подход. Я столкнулся с generics-sop
, который преобразует запись в разнородный список и поставляется с, казалось бы, удобной операцией hsequence
.
Точка, где я застрял
generics-sop
сохраняет тип аппликации в отдельном параметре типа своего разнородного списка, и это всегда I
(идентификация) при использовании сгенерированного преобразования. Поэтому мне нужно было бы отобразить список hlist и удалить I
из элементов, которые эффективно переместят аппликатив под I
в упомянутый параметр типа (это будет Comp IO Maybe
), чтобы я мог использовать hsequence
и, наконец, добавить обратно I
s, чтобы я мог скрытно вернуться к записи.
Но я не знаю, как написать сигнатуру типа для функции удаления/добавления I
, которая сообщает, что типы соответствующих элементов hlist последовательно изменяются, теряя/приобретая внешний тип. Это вообще возможно?
FooC { x = someEnvInt "someX" , y = someEnvText "someY" }
не будет компилироваться сам по себе. Если вы изменитеsomeEnv___
на подписьData.Functor.Compose IO Maybe ___
, у вас может быть шанс. Но на тот момент я не уверен, что это вообще стоило бы того... - person Alec   schedule 24.10.2016generics-sop
) приемлем. - person ron   schedule 24.10.2016(Applicative g, Applicative f) => FooC (Compose f g) -> f (FooC g)
(эта функция по сути простоsequence
) - затем измените типsomeEnvInt
наCompose IO Maybe Int
. Если вы хотите, вы можете выполнить «разложение», используя семейства типов, что избавит вас от изменения типаsomeEnvInt
, но я лично не думаю, что это стоит усилий. - person user2407038   schedule 24.10.2016