我假设您实际上并不想要多个映射 - 也就是说,给定的键应该恰好对应一个值,而不是映射中的A1
值和a1s
映射中的另一个A2
值a2s
等。
此外,如果在单个值中存在多个特定类型的匹配项,例如,如果您有以下类型的值,您还没有说过要做什么:
data A6 = A6 A3 A4
并尝试检索或遍历类型的术语D
。下面,我假设您只想检索和/或遍历遇到的“第一个”(例如,仅D
in A3
,忽略 in A4
)。
无论如何,您可以使用Data
泛型和一些来自lens
's 的助手来做到这一点Data.Data.Lens
。
不需要特殊的数据类型。一个普通Map
的就足够了,用一个 sum 类型来表示你想要存储的值的集合:
data Dat = D_A1 A1 | D_A2 A2 | D_A3 A3 | D_A4 A4 | D_A5 A5 deriving (Data)
type Foo k dat = Map k dat
要通过键查找(可能是深度嵌套的)值,我们可以使用biplate
遍历 from lens
:
lookupFoo :: (Ord k, Typeable v, Data dat) => k -> Foo k dat -> Maybe v
lookupFoo k foo = do
dat <- Map.lookup k foo
firstOf biplate dat
在这里,biplate
递归遍历 term 中所有 typev
的子项dat
。查询返回第firstOf
一个匹配的术语,或者Nothing
如果没有找到术语。(该do
块在Maybe
monad 中运行。)
要执行索引遍历,我们还可以使用biplate
, 修改 usingtaking 1
以仅遍历第一个匹配项:
itraverseFoo :: (Applicative f, Typeable v, Data dat) => (k -> v -> f v) -> Foo k dat -> f (Foo k dat)
itraverseFoo f foo = Map.traverseWithKey f' foo
where f' k dat = taking 1 biplate (f k) dat
完整代码:
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ExplicitForAll #-}
import Control.Lens
import Control.Monad.Writer
import Data.Data
import Data.Data.Lens
import Data.Map (Map)
import qualified Data.Map as Map
data Dat = D_A1 A1 | D_A2 A2 | D_A3 A3 | D_A4 A4 | D_A5 A5 | D_A6 A6 deriving (Data)
type Foo k dat = Map k dat
lookupFoo :: (Ord k, Typeable v, Data dat) => k -> Foo k dat -> Maybe v
lookupFoo k foo = do
dat <- Map.lookup k foo
firstOf biplate dat
itraverseFoo :: (Applicative f, Typeable v, Data dat) => (k -> v -> f v) -> Foo k dat -> f (Foo k dat)
itraverseFoo f foo = Map.traverseWithKey f' foo
where f' k dat = taking 1 biplate (f k) dat
data A = A deriving (Data, Show)
data B = B deriving (Data, Show)
data C = C deriving (Data, Show)
data D = D deriving (Data, Show)
data E = E deriving (Data, Show)
data F = F deriving (Data, Show)
data G = G deriving (Data, Show)
data A1 = A1 A B C deriving (Data, Show)
data A2 = A2 A deriving (Data, Show)
data A3 = A3 B C D deriving (Data, Show)
data A4 = A4 D E F deriving (Data, Show)
data A5 = A5 A1 A4 G deriving (Data, Show)
data A6 = A6 A3 A4 deriving (Data, Show)
foo :: Foo String Dat
foo = Map.fromList [ ("a1", D_A1 (A1 A B C))
, ("a3", D_A3 (A3 B C D))
, ("a4", D_A4 (A4 D E F))
, ("a5", D_A5 (A5 (A1 A B C) (A4 D E F) G))
, ("a6", D_A6 (A6 (A3 B C D) (A4 D E F)))
]
find :: forall a k. k -> a -> Writer [k] a
find k a = tell [k] >> pure a
main = do
print $ (lookupFoo "a1" foo :: Maybe A1)
print $ (lookupFoo "a1" foo :: Maybe B)
print $ (lookupFoo "a5" foo :: Maybe A1)
print $ (lookupFoo "a5" foo :: Maybe B)
print $ execWriter (itraverseFoo (find @D) foo)