经过一番挖掘,我找到了三种解决方法。如果您想要最好的解决方法,请跳到底部直到
toMap
着陆。
截至 2019 年 5 月 5 日,无法像使用 Aeson/YAML 那样在 Dhall 中表示地图(尽管对本机toMap
函数的支持即将推出)。所以现在我们基本上必须使用同质记录列表。这有点笨拙,但至少你得到了原生解组。
如果我们想使用元组列表而不是映射,我们可以这样做:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
module Tuple where
import Dhall
import qualified Data.Text as T
data MyType = MyType { env :: [MyTuple] }
deriving (Generic, Show)
instance Interpret MyType
newtype MyTuple = MyTuple (T.Text, T.Text)
deriving (Interpret, Show)
-- input auto "{env = [{_1= \"HOME\", _2 = \"foo\"}] }" :: IO MyType
以上内容改编自这个答案,它展示了一种将 IP 地址解析为 4 元素元组的方法。
为了解析成 Map,我们可以这样做:
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
module MapA where
import Data.Map (Map)
import Data.Text (Text)
import Dhall
import qualified Data.Map
data MyType = MyType { env :: Map Text Text }
deriving (Generic, Show)
data KeyValue a = KeyValue { mapKey :: Text, mapValue :: a }
deriving (Generic, Show)
toMap :: [KeyValue a] -> Map Text a
toMap keyValues = Data.Map.fromList (map adapt keyValues)
where
adapt (KeyValue {..}) = (mapKey, mapValue)
instance Interpret MyType
instance Interpret a => Interpret (KeyValue a)
-- Wrap `Map` in a newtype if you want to avoid an orphan instance
instance Interpret a => Interpret (Map Text a) where
autoWith options = fmap toMap (autoWith options)
-- input auto "{env = [{mapKey = \"HOME\", mapValue = \"foo\"}] }" :: IO MapA.MyType
以上内容改编自此
评论。这个想法是使记录看起来像{ mapKey = X, mapValue = Y}
可解析的,然后将此类记录的任何列表转换为 Map。请注意我们如何支持任何值类型,而不仅仅是文本(所以如果我们愿意,我们可以env
在MyType
beMap Text Int
或其他东西中使用)。该解决方案只有 1 个类型变量a
用于映射中的值,但我想也可以使键更通用。
好的,经过一些调整,我得到了以下编译,它支持键和值都是通用的:
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
module MapKV where
import Data.Map (Map)
import Data.Text (Text)
import Dhall
import qualified Data.Map
data MyType = MyType { env :: Map Text Text }
deriving (Generic, Show)
data MyTypeInts = MyTypeInts { envInts :: Map Integer Integer }
deriving (Generic, Show)
data KeyValue k v = KeyValue { mapKey :: k, mapValue :: v }
deriving (Generic, Show)
toMap :: Ord k => [KeyValue k v] -> Map k v
toMap keyValues = Data.Map.fromList (map adapt keyValues)
where
adapt (KeyValue {..}) = (mapKey, mapValue)
instance Interpret MyType
instance Interpret MyTypeInts
instance (Interpret k, Interpret v) => Interpret (KeyValue k v)
-- Wrap `Map` in a newtype if you want to avoid an orphan instance
instance (Ord k, Interpret k, Interpret v) => Interpret (Map k v) where
autoWith options = fmap toMap (autoWith options)
-- input auto "{env = [{mapKey = +1, mapValue = \"foo\"}] }" :: IO MapKV.MyType
-- input auto "{envInts = [{mapKey = +1, mapValue = -22 }] }" :: IO MapKV.MyTypeInts
最后,这是一个避免使用新Env
类型包装器的孤儿实例的版本:
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
module MapKV where
import Data.Map (Map)
import Dhall
import qualified Data.Map
data MyType = MyType { env :: Env }
deriving (Generic, Show)
newtype Env = Env (Map Text Text)
deriving (Eq, Generic, Show)
data KeyValue k v = KeyValue { mapKey :: k, mapValue :: v }
deriving (Generic, Show)
toMap :: Ord k => [KeyValue k v] -> Map k v
toMap keyValues = Data.Map.fromList (map adapt keyValues)
where
adapt (KeyValue {..}) = (mapKey, mapValue)
instance Interpret MyType
instance (Interpret k, Interpret v) => Interpret (KeyValue k v)
instance Interpret Env where
autoWith options = fmap (Env . toMap) (autoWith options)
-- input auto "{env = [{mapKey = \"HOME\", mapValue = \"foo\"}] }" :: IO MapKV.MyType