#haskell #haskell-lens
#haskell #haskell-объектив
Вопрос:
У меня часто возникает шаблон. У меня есть структура, подобная этой:
data Foo = Foo
{ foo :: Maybe Text
, bar :: Maybe Text
, buz :: Maybe Text
}
Я хочу преобразовать его в карту с метками-значениями, не отбрасывая никаких значений:
myMap :: [(Text, Text)]
например: myMap = [("foo", "val1"), ("bar", "val2")]
Какой самый элегантный способ сделать это? Я пробовал использовать такие шаблоны, как if isJust ... then fromJust ... else Nothing
, а затем использовать catMaybes
, но это не очень элегантно, IMO.
На практике моя структура более неоднородна:
data Foo = Foo
{ foo :: Maybe Text
, bar :: Maybe Bool
, buz :: Maybe Int
}
И я хочу преобразовать его в:
myMap :: [(Text, Value)]
Есть ли для этого способ Lens?
Комментарии:
1. Сигнатура типа, такая как
myMap :: [(Text, Value)]
, по-видимому, подразумевает, что все поля имеют один и тот же тип. Списки должны быть однородными.2. @jpmarinier извините, я забыл упомянуть, что это значение от Aeson: hackage.haskell.org/package/aeson-1.5.4.1/docs /…
3. @cdupont: вы можете позволить Haskell автоматически создавать экземпляр для
ToJson
, а затем использовать его для генерации пар ключ-значение.
Ответ №1:
Мы можем позволить Haskell автоматически создавать экземпляр ToJson
:
{-# LANGUAGE DeriveGeneric #-}
import Data.Text(Text)
import Data.Aeson(ToJSON)
import GHC.Generics(Generic)
data Foo = Foo
{ foo :: Maybe Text
, bar :: Maybe Bool
, buz :: Maybe Int
} deriving Generic
instance ToJSON Foo
Затем вы можете отфильтровать элементы, значение которых равно Null
:
import Data.Aeson(Value(Null, Object), toJSON)
import Data.HashMap.Strict(toList)
kvs :: Foo -> [(Text, Value)]
kvs foo
| Object obs <- toJSON foo = filter ((Null /=) . snd) (toList obs)
| otherwise = []
Например:
Prelude> kvs (Foo Nothing (Just True) (Just 14))
[("buz",Number 14.0),("bar",Bool True)]
Комментарии:
1. Большое спасибо, я думаю, что это лучшее решение.
Ответ №2:
Не объектив, но, по крайней мере, это просто с пониманием списка:
quux :: Foo -> [(String, Text)]
quux v = [ (a,b) | (a, Just b) <- zip ["foo", "bar", "buz"]
$ map ($ v) [foo, bar, buz]]
или ваш второй запрос,
quux2 :: Foo -> [(String, Value)]
quux2 v = [ (a,b) | (a, Just b) <- zip ["foo", "bar", "buz"]
$ map ($ v)
[(TextValue <$>) . foo,
(BoolValue <$>) . bar, (IntValue <$>) . buz]]
предположим,
TextValue :: Text -> Value
BoolValue :: Bool -> Value
IntValue :: Int -> Value
Да, это быстро становится хрупким и громоздким. Но первый вариант достаточно прост.
Комментарии:
1. Большое спасибо, мне нравится понимание списка. Хотя я всегда забываю их использовать…