⬑
简易仿Lens库
看了一点Haskell里面lens的实现原理,决定试一下自己手搓一个简易版作为练习。不过因为对Rank-N Type一知半解,搓出来的东西好像跟正版不太一样。
看运行结果似乎是能用的,虽然没有什么实用价值,不过算是有所收获的练习。
代码不长:
-- The Library
accessor getter setter f x = (setter newVal x, getter x) where newVal = f (getter x)
view accessor obj = snd $ accessor id obj
over accessor f obj = fst $ accessor f obj
set accessor x obj = over accessor (const x) obj
composeAccessors ac1 ac2 modifier obj =
(newObj, value)
where
newObj = over ac1 (over ac2 modifier) obj
value = view ac2 (view ac1 obj)
composeFunctorAccesors ac1 ac2 modifier obj =
(newObj, value)
where
newObj = over ac1 (fmap $ over ac2 modifier) obj
value = fmap (view ac2) (view ac1 obj)
infixr 5 #.
(#.) = composeAccessors
infixr 5 ##.
(##.) :: (Functor f1, Functor f0) =>
((f1 a1 -> f1 a1) -> t -> (a2, f0 a1))
-> ((a3 -> a3) -> a1 -> (a1, b)) ->
(a3 -> a3) -> t -> (a2, f0 b)
(##.) = composeFunctorAccesors
self = accessor id (\n x -> n)
listAccessor idx =
accessor
(head . drop idx)
(\n lst ->
(take idx lst) ++ [n] ++ (drop (idx + 1) lst))
_0 = listAccessor 0
_1 = listAccessor 1
_2 = listAccessor 2
_3 = listAccessor 3
_4 = listAccessor 4
_5 = listAccessor 5
_6 = listAccessor 6
_7 = listAccessor 7
_8 = listAccessor 8
_9 = listAccessor 9
-- =====================
data Person = Person
{ _name :: String
, _address :: Maybe Address
} deriving (Show)
name = accessor _name (\n x -> x {_name = n})
address = accessor _address (\n x -> x {_address = n})
data Address = Address
{ _city :: String
, _zipInfos :: [Maybe ZipInfo]
} deriving (Show)
city = accessor _city (\n x -> x {_city = n})
zipInfos = accessor _zipInfos (\n x -> x {_zipInfos = n})
data ZipInfo = ZipInfo
{ _code :: String
, _extraInfo :: Maybe String
} deriving (Show)
code = accessor _code (\n x -> x {_code = n})
extraInfo = accessor _extraInfo (\n x -> x {_extraInfo = n})
alice = Person
{ _name = "Alice"
, _address = Just Address
{ _city = "Shanghai"
, _zipInfos =
[ Just ZipInfo
{ _code = "200000"
, _extraInfo = Nothing
}
, Just ZipInfo
{ _code = "200002"
, _extraInfo = Nothing
}
]
}
}
setList =
over (_1 #. _1) (const 5) [[1,2],[3,4]]
main = do
putStrLn . show $ setList
putStrLn . show $ alice
-- 单个字段
putStrLn . show $ view (name) alice
-- 嵌套字段
putStrLn . show $ view (address ##. city) alice
-- 两层functor,每次"##."会进行一次fmap,需要两次fmap
putStrLn . show $ view (address ##. zipInfos ##. self ##. code) alice
-- 测试set
putStrLn . show $ over (address ##. zipInfos ##. self ##. code) (++ "uwu") alice
putStrLn . show $ over (self##.self) succ [1,2,3]