Skip to content

Instantly share code, notes, and snippets.

@mistivia
Last active April 23, 2025 07:15
Show Gist options
  • Select an option

  • Save mistivia/c7a805cf35e46e045a094a9bf2c362ed to your computer and use it in GitHub Desktop.

Select an option

Save mistivia/c7a805cf35e46e045a094a9bf2c362ed to your computer and use it in GitHub Desktop.
Simple yet usable lens-like utility functions to access nested record fileds in haskell
-- The Library
accessor getter setter f x = (setter newVal x, getter x) where newVal = f (getter x)
get accessor x = snd $ accessor id x
set accessor f x = fst $ accessor f x
composeAccessors ac1 ac2 modifier obj =
(newObj, value)
where
newObj = fst $ ac1 innerModifier obj
innerModifier innerObj = fst $ ac2 modifier innerObj
value = get ac2 (get ac1 obj)
composeFunctorAccesors ac1 ac2 modifier obj =
(newObj, value)
where
newObj = fst $ ac1 (fmap innerModifier) obj
innerModifier innerObj = fst $ ac2 modifier innerObj
value = fmap (get ac2) (get 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)
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
}
]
}
}
listAt idx = accessor (head . drop idx) (\n lst -> (take idx lst) ++ [n] ++ (drop (idx + 1) lst))
setList =
set (listAt 1 #. listAt 1) (const 5) [[1,2],[3,4]]
main = do
putStrLn . show $ setList
putStrLn . show $ alice
putStrLn . show $ get (name) alice
putStrLn . show $ get (address ##. city) alice
putStrLn . show $ get (address ##. zipInfos ##. self ##. code) alice
putStrLn . show $ set (address ##. zipInfos ##. self ##. code) (++ "uwu") alice
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment