Haskell: 从 Maybes 结构转换为 Hashmap

Haskell: convert from a structure of Maybes to a Hashmap

我经常出现一种模式。我有这样一个结构:

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 方法?

不是镜头,但至少它对列表理解很简单:

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

是的,这很快就会变得脆弱和笨重。但是第一个变体很简单。

我们可以让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 <b>ToJSON Foo</b>

然后您可以筛选出值为 Null:

的项目
import Data.Aeson(Value(Null, Object), toJSON)
import Data.HashMap.Strict(toList)

kvs :: Foo -> [(Text, Value)]
kvs foo
    | Object <b>obs</b> <- toJSON foo = filter ((Null /=) . snd) (toList obs)
    | otherwise = []

例如:

Prelude> kvs (Foo Nothing (Just True) (Just 14))
[("buz",Number 14.0),("bar",Bool True)]