Lens
はじめに
There are several implementations of note that are mostly compatible but differ in scope:
- lens-family-core
- fc-labels
- data-lens-light
- lens
WARNING: The lens
library is considered by
many Haskellers to be deeply pathological and introduces a needless amount of
complexity. Some care should taken when considering it's use, it is included
here for information only and not as endorsement for it's use. Consider
lens-family-core
or fclabels
instead.
lensライブラリは使うべき?
No. The lens
library is deeply problematic when considered in the context of
the rest of the Haskell ecosystem and should be avoided. While there are some
good ideas around the general ideas of lenses, the lens
library's
implementation contains an enormous amount of unidiomatic and over-engineered
Haskell code whose marginal utility is grossly outweighed by the sheer weight of
the entire edifice and the mental strain that it forces it on other developers
to deduce the types involved in even the simplest expressions.
lens is effectively a laboratory for a certain set of emerging ideas, it's idiosyncratic with respect to the rest of the ecosystem.
ファン・ラーホーフェンのレンズ
At it's core a lens is a form of coupled getter and setter functions as a value under an existential functor.
-- +---- a : Type of structure
-- | +-- b : Type of target
-- | |
type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
There are two derivations of van Laarhoven lenses, one that allows polymorphic update and one that is strictly monomorphic. Let's just consider the monomorphic variant first:
type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
newtype Const x a = Const { runConst :: x } deriving Functor
newtype Identity a = Identity { runIdentity :: a } deriving Functor
lens :: (a -> b) -> (a -> b -> a) -> Lens' a b
lens getter setter l a = setter a <$> l (getter a)
set :: Lens' a b -> b -> a -> a
set l b = runIdentity . l (const (Identity b))
get :: Lens' a b -> a -> b
get l = runConst . l Const
over :: Lens' a b -> (b -> b) -> a -> a
over l f a = set l (f (get l a)) a
infixl 1 &
infixr 4 .~
infixr 4 %~
infixr 8 ^.
(&) :: a -> (a -> b) -> b
(&) = flip ($)
(^.) = flip get
(.~) = set
(%~) = over
Such that we have:
s ^. (lens getter setter) -- getter s
s & (lens getter setter) .~ b -- setter s b
Law 1
get l (set l b a) = b
Law 2
set l (view l a) a = a
Law 3
set l b1 (set l b2 a) = set l b1 a
With composition identities:
x^.a.b ≡ x^.a^.b
a.b %~ f ≡ a %~ b %~ f
x ^. id ≡ x
id %~ f ≡ f
While this may look like a somewhat convoluted way of reinventing record update, consider the types of these
functions align very nicely such Lens themselves compose using the normal (.)
composition, although in the
reverse direction of function composition.
f :: a -> b
g :: b -> c
g . f :: a -> c
f :: Lens a b ~ (b -> f b) -> (a -> f a)
g :: Lens b c ~ (c -> f c) -> (b -> f b)
f . g :: Lens a c ~ (c -> f c) -> (a -> f a)
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
import Data.Functor
type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
newtype Const x a = Const { runConst :: x } deriving Functor
newtype Identity a = Identity { runIdentity :: a } deriving Functor
lens :: (s -> a) -> (s -> a -> s) -> Lens' s a
lens getter setter f a = fmap (setter a) (f (getter a))
set :: Lens' a b -> b -> a -> a
set l b = runIdentity . l (const (Identity b))
view :: Lens' a b -> a -> b
view l = runConst . l Const
over :: Lens' a b -> (b -> b) -> a -> a
over l f a = set l (f (view l a)) a
compose :: Lens' a b -> Lens' b c -> Lens' a c
compose l s = l . s
id' :: Lens' a a
id' = id
infixl 1 &
infixr 4 .~
infixr 4 %~
infixr 8 ^.
(^.) = flip view
(.~) = set
(%~) = over
(&) :: a -> (a -> b) -> b
(&) = flip ($)
(+~), (-~), (*~) :: Num b => Lens' a b -> b -> a -> a
f +~ b = f %~ (+b)
f -~ b = f %~ (subtract b)
f *~ b = f %~ (*b)
-- Usage
data Foo = Foo { _a :: Int } deriving Show
data Bar = Bar { _b :: Foo } deriving Show
a :: Lens' Foo Int
a = lens getter setter
where
getter :: Foo -> Int
getter = _a
setter :: Foo -> Int -> Foo
setter = (\f new -> f { _a = new })
b :: Lens' Bar Foo
b = lens getter setter
where
getter :: Bar -> Foo
getter = _b
setter :: Bar -> Foo -> Bar
setter = (\f new -> f { _b = new })
foo :: Foo
foo = Foo 3
bar :: Bar
bar = Bar foo
example1 = view a foo
example2 = set a 1 foo
example3 = over a (+1) foo
example4 = view (b `compose` a) bar
example1' = foo ^. a
example2' = foo & a .~ 1
example3' = foo & a %~ (+1)
example4' = bar ^. b . a
It turns out that these simple ideas lead to a very rich set of composite combinators that be used to perform a wide for working with substructure of complex data structures.
Combinator Description
view
View a single target or fold the targets of a monoidal quantity.
set
Replace target with a value and return updated structure.
over
Update targets with a function and return updated structure.
to
Construct a retrieval function from an arbitrary Haskell function.
traverse
Map each element of a structure to an action and collect results.
ix
Target the given index of a generic indexable structure.
toListOf
Return a list of the targets.
firstOf
Returns Just
the target of a prism or Nothing.
Certain patterns show up so frequently that they warrant their own operators, although they can be expressed textual terms as well.
Symbolic | Textual Equivalent | Description | ||||
---|---|---|---|---|---|---|
^. |
view |
Access value of target | ||||
.~ |
set |
Replace target x |
||||
%~ |
over |
Apply function to target | ||||
+~ |
over t (+n) |
Add to target | ||||
-~ |
over t (-n) |
Subtract to target | ||||
*~ |
over t (*n) |
Multiply to target | ||||
//~ |
over t (//n) |
Divide to target | ||||
^~ |
over t (^n) |
Integral power to target | ||||
^^~ |
over t (^^n) |
Fractional power to target | ||||
`` | ~`` | ``over t ( | p)`` | Logical or to target | ||
&&~ |
over t (&& p) |
Logical and to target | ||||
<>~ |
over t (<> n) |
Append to a monoidal target | ||||
?~ |
set t (Just x) |
Replace target with Just x |
||||
^? |
firstOf |
Return Just target or Nothing |
||||
^.. |
toListOf |
View list of targets |
Constructing the lens field types from an arbitrary datatype involves a bit of boilerplate code generation.
But compiles into simple calls which translate the fields of a record into functions involving the lens
function and logic for the getter and the setter.
import Control.Lens
data Foo = Foo { _field :: Int }
field :: Lens' Foo Int
field = lens getter setter
where
getter :: Foo -> Int
getter = _field
setter :: Foo -> Int -> Foo
setter = (\f new -> f { _field = new })
These are pure boilerplate, and Template Haskell can automatically generate these functions using
makeLenses
by introspecting the AST at compile-time.
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Foo = Foo { _field :: Int } deriving Show
makeLenses ''Foo
The simplest usage of lens is simply as a more compositional way of dealing with record access and updates, shown below in comparison with traditional record syntax:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Rec = MkRec { _foo :: Int , _bar :: Int } deriving Show
makeLenses ''Rec
x :: Rec
x = MkRec { _foo = 1024, _bar = 1024 }
get1 :: Int
get1 = (_foo x) + (_bar x)
get2 :: Int
get2 = (x ^. foo) + (x ^. bar)
get3 :: Int
get3 = (view foo x) + (view bar x)
set1 :: Rec
set1 = x { _foo = 1, _bar = 2 }
set2 :: Rec
set2 = x & (foo .~ 1) . (bar .~ 2)
set3 :: Rec
set3 = x & (set foo 1) . (set bar 2)
This pattern has great utility when it comes when dealing with complex and deeply nested structures:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RankNTypes #-}
import Control.Lens
import Control.Lens.TH
data Record1 = Record1
{ _a :: Int
, _b :: Maybe Record2
} deriving Show
data Record2 = Record2
{ _c :: String
, _d :: [Int]
} deriving Show
makeLenses ''Record1
makeLenses ''Record2
records :: [Record1]
records = [
Record1 {
_a = 1,
_b = Nothing
},
Record1 {
_a = 2,
_b = Just $ Record2 {
_c = "Picard",
_d = [1,2,3]
}
},
Record1 {
_a = 3,
_b = Just $ Record2 {
_c = "Riker",
_d = [4,5,6]
}
},
Record1 {
_a = 4,
_b = Just $ Record2 {
_c = "Data",
_d = [7,8,9]
}
}
]
-- Lens targets
ids = traverse.a
names = traverse.b._Just.c
nums = traverse.b._Just.d
listn n = traverse.b._Just.d.ix n
-- Modify to set all 'id' fields to 0
ex1 :: [Record1]
ex1 = set ids 0 records
-- Return a view of the concatenated 'd' fields for all nested records.
ex2 :: [Int]
ex2 = view nums records
-- [1,2,3,4,5,6,7,8,9]
-- Increment all 'id' fields by 1
ex3 :: [Record1]
ex3 = over ids (+1) records
-- Return a list of all 'c' fields.
ex4 :: [String]
ex4 = toListOf names records
-- ["Picard","Riker","Data"]
-- Return the the second element of all 'd' fields.
ex5 :: [Int]
ex5 = toListOf (listn 2) records
-- [3,6,9]
Lens also provides us with an optional dense slurry of operators that expand into combinations of the core combinators. Many of the operators do have a consistent naming scheme.
The sheer number of operators provided by lens is a polarizing for some, but all
of the operators can be written in terms of the textual functions (set
,
view
, over
, at
, ...) and some people prefer to use these instead.
If one buys into lens model, it can serve as a partial foundation to write logic over a wide variety of data structures and computations and subsume many of the existing patterns found in the Prelude.
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Lens
import Numeric.Lens
import Data.Complex.Lens
import Data.Complex
import qualified Data.Map as Map
l :: Num a => a
l = view _1 (100, 200)
-- 100
m :: Num a => (a, a, a)
m = (100,200,200) & _3 %~ (+100)
-- (100,200,300)
n :: Num a => [a]
n = [100,200,300] & traverse +~ 1
-- [101,201,301]
o :: Char
o = "frodo" ^?! ix 3
-- 'd'
p :: Num a => [a]
p = [[1,2,3], [4,5,6]] ^. traverse
-- [1,2,3,4,5,6]
q :: Num a => [a]
q = [1,2,3,4,5] ^. _tail
-- [2,3,4,5]
r :: Num a => [Maybe a]
r = [Just 1, Just 2, Just 3] & traverse._Just +~ 1
-- [Just 2, Just 3, Just 4]
s :: Maybe String
s = Map.fromList [("foo", "bar")] ^. at "foo"
-- Just "bar"
t :: Integral a => Maybe a
t = "1010110" ^? binary
-- Just 86
u :: Complex Float
u = (mkPolar 1 pi/2) & _phase +~ pi
-- 0.5 :+ 8.742278e-8
v :: [Integer]
v = [1..10] ^.. folded.filtered even
-- [2,4,6,8,10]
w :: [Integer]
w = [1, 2, 3, 4] & each . filtered even *~ 10
-- [1, 20, 3, 40]
x :: Num a => Maybe a
x = Left 3 ^? _Left
-- Just 3
See:
Lens family
The interface for lens-family
is very similar to lens
but with a smaller API and core.
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
import Lens.Family
import Lens.Family.TH
import Lens.Family.Stock
import Data.Traversable
data Record1 = Record1
{ _a :: Int
, _b :: Maybe Record2
} deriving Show
data Record2 = Record2
{ _c :: String
, _d :: [Int]
} deriving Show
mkLenses ''Record1
mkLenses ''Record2
records :: [Record1]
records = [
Record1 {
_a = 1,
_b = Nothing
},
Record1 {
_a = 2,
_b = Just $ Record2 {
_c = "Picard",
_d = [1,2,3]
}
},
Record1 {
_a = 3,
_b = Just $ Record2 {
_c = "Riker",
_d = [4,5,6]
}
},
Record1 {
_a = 4,
_b = Just $ Record2 {
_c = "Data",
_d = [7,8,9]
}
}
]
ids = traverse.a
names = traverse.b._Just.c
nums = traverse.b._Just.d
ex1 = set ids 0 records
ex2 = view nums records
ex3 = over ids (+1) records
ex4 = toListOf names records
多相更新
-- +---- a : Type of input structure
-- | +-- a' : Type of output structure
-- | |
type Lens a a' b b' = forall f. Functor f => (b -> f b') -> (a -> f a')
-- | |
-- | +-- b : Type of input target
-- +---- b' : Type of output target
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
import Data.Functor
type Lens a a' b b' = forall f. Functor f => (b -> f b') -> (a -> f a')
type Lens' a b = Lens a a b b
newtype Const x a = Const { runConst :: x } deriving Functor
newtype Identity a = Identity { runIdentity :: a } deriving Functor
lens :: (a -> b) -> (a -> b' -> a') -> Lens a a' b b'
lens getter setter f a = fmap (setter a) (f (getter a))
set :: Lens a a' b b' -> b' -> a -> a'
set l b = runIdentity . l (const (Identity b))
get :: Lens a a' b b' -> a -> b
get l = runConst . l Const
over :: Lens a a' b b' -> (b -> b') -> a -> a'
over l f a = set l (f (get l a)) a
compose :: Lens a a' b b' -> Lens b b' c c' -> Lens a a' c c'
compose l s = l . s
id' :: Lens a a a a
id' = id
infixl 1 &
infixr 4 .~
infixr 4 %~
infixr 8 ^.
(^.) = flip get
(.~) = set
(%~) = over
(&) :: a -> (a -> b) -> b
(&) = flip ($)
(+~), (-~), (*~) :: Num b => Lens a a b b -> b -> a -> a
f +~ b = f %~ (+b)
f -~ b = f %~ (subtract b)
f *~ b = f %~ (*b)
-- Monomorphic Update
data Foo = Foo { _a :: Int } deriving Show
data Bar = Bar { _b :: Foo } deriving Show
a :: Lens' Foo Int
a = lens getter setter
where
getter :: Foo -> Int
getter = _a
setter :: Foo -> Int -> Foo
setter = (\f new -> f { _a = new })
b :: Lens' Bar Foo
b = lens getter setter
where
getter :: Bar -> Foo
getter = _b
setter :: Bar -> Foo -> Bar
setter = (\f new -> f { _b = new })
-- Polymorphic Update
data Pair a b = Pair a b deriving Show
pair :: Pair Int Char
pair = Pair 1 'b'
_1 :: Lens (Pair a b) (Pair a' b) a a'
_1 f (Pair a b) = (\x -> Pair x b) <$> f a
_2 :: Lens (Pair a b) (Pair a b') b b'
_2 f (Pair a b) = (\x -> Pair a x) <$> f b
ex1 = pair ^. _1
ex2 = pair ^. _2
ex3 = pair & _1 .~ "a"
ex4 = pair & (_1 %~ (+1))
. (_2 .~ 1)
プリズム
type Prism a a' b b' = forall f. Applicative f => (b -> f b') -> (a -> f a')
Just as lenses allow us to manipulate product types, Prisms allow us to manipulate sum types allowing us to traverse and apply functions over branches of a sum type selectively.
The two libraries lens
and lens-family
disagree on how these structures are defined and which
constraints they carry but both are defined in terms of at least an Applicative instance. A prism instance in
the lens library is constructed via prism
for polymorphic lens ( those which may change a resulting type
parameter) and prism'
for those which are strictly monomorphic. Just as with the Lens instance
makePrisms
can be used to abstract away this boilerplate via Template Haskell.
import Control.Lens
data Value = I Int
| D Double
deriving Show
_I :: Prism' Value Int
_I = prism remit review
where
remit :: Int -> Value
remit a = I a
review :: Value -> Either Value Int
review (I a) = Right a
review a = Left a
_D :: Prism' Value Double
_D = prism remit review
where
remit :: Double -> Value
remit a = D a
review :: Value -> Either Value Double
review (D a) = Right a
review a = Left a
test1 :: Maybe Int
test1 = (I 42) ^? _I
test2 :: Value
test2 = 42 ^. re _I
test3 :: Value
test3 = over _I succ (I 2)
test4 :: Value
test4 = over _I succ (D 2.71)
_just :: Prism (Maybe a) (Maybe b) a b
_just = prism Just $ maybe (Left Nothing) Right
_nothing :: Prism' (Maybe a) ()
_nothing = prism' (const Nothing) $ maybe (Just ()) (const Nothing)
_left :: Prism (Either a c) (Either b c) a b
_left = prism Left $ either Right (Left . Right)
_right :: Prism (Either c a) (Either c b) a b
_right = prism Right $ either (Left . Left) Right
In keeping with the past examples, I'll try to derive Prisms from first principles although this is no easy
task as they typically are built on top of machinery in other libraries. This a (very) rough approximation of
how one might do it using lens-family-core
types.
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
import Data.Functor
import Data.Monoid
import Control.Applicative
import Data.Traversable
newtype Getting c a = Getting { unGetting :: c }
newtype Setting a = Setting { unSetting :: a }
type LensLike f s t a b = (a -> f b) -> s -> f t
type Lens a a' b b' = forall f. Functor f => LensLike f a a' b b'
type Lens' a b = Lens a a b b
type Prism a a' b b' = forall f. Applicative f => (b -> f b') -> (a -> f a')
type Prism' a b = Prism a a b b
type Setter a a' b b' = LensLike Setting a a' b b'
type Setter' a b = Setter a a b b
type Getter a c = forall r d b. (c -> Getting r d) -> a -> Getting r b
type FoldLike r a a' b b' = LensLike (Getting r) a a' b b'
instance Functor (Getting c) where
fmap _ (Getting c) = Getting c
instance Monoid c => Applicative (Getting c) where
pure _ = Getting mempty
Getting a <*> Getting b = Getting (a `mappend` b)
class Functor f => Phantom f where
coerce :: f a -> f b
instance Phantom (Getting c) where
coerce (Getting c) = Getting c
instance Functor Setting where
fmap f (Setting a) = Setting (f a)
instance Applicative Setting where
pure = Setting
Setting f <*> Setting a = Setting (f a)
lens :: (a -> b) -> (a -> b' -> a') -> Lens a a' b b'
lens getter setter f a = fmap (setter a) (f (getter a))
(.~) :: Setter a a' b b' -> b' -> a -> a'
l .~ b = l %~ const b
view :: FoldLike b a a' b b' -> a -> b
view l = unGetting . l Getting
over :: Setter a a' b b' -> (b -> b') -> a -> a'
over l = (l %~)
set :: Setter a a' b b' -> b' -> a -> a'
set = (.~)
(%~) :: Setter a a' b b' -> (b -> b') -> a -> a'
l %~ f = unSetting . l (Setting . f)
compose :: Lens a a' b b' -> Lens b b' c c' -> Lens a a' c c'
compose l s = l . s
id' :: Lens' a a
id' = id
infixl 1 &
infixr 4 .~
infixr 4 %~
infixr 8 ^.
(^.) :: a -> FoldLike b a a' b b' -> b
(^.) = flip view
(&) :: a -> (a -> b) -> b
(&) = flip ($)
(+~), (-~), (*~) :: Num b => Setter' a b -> b -> a -> a
f +~ b = f %~ (+b)
f -~ b = f %~ (subtract b)
f *~ b = f %~ (*b)
infixr 8 ^?
infixr 8 ^..
views :: FoldLike r a a' b b' -> (b -> r) -> a -> r
views l f = unGetting . l (Getting . f)
(^?) :: a -> FoldLike (First b) a a' b b' -> Maybe b
x ^? l = firstOf l x
(^..) :: a -> FoldLike [b] a a' b b' -> [b]
x ^.. l = toListOf l x
toListOf :: FoldLike [b] a a' b b' -> a -> [b]
toListOf l = views l (:[])
firstOf :: FoldLike (First b) a a' b b' -> a -> Maybe b
firstOf l = getFirst . views l (First . Just)
prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
prism rm rv f a =
case rv a of
Right x -> fmap rm (f x)
Left x -> pure x
prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' rm rv f a =
case rv a of
Just x -> fmap rm (f x)
Nothing -> pure a
_just :: Prism (Maybe a) (Maybe b) a b
_just = prism Just $ maybe (Left Nothing) Right
_nothing :: Prism' (Maybe a) ()
_nothing = prism' (const Nothing) $ maybe (Just ()) (const Nothing)
_right :: Prism (Either c a) (Either c b) a b
_right = prism Right $ either (Left . Left) Right
_left :: Prism (Either a c) (Either b c) a b
_left = prism Left $ either Right (Left . Right)
to :: (s -> a) -> Getter s a
to p f = coerce . f . p
pair :: (Int, Char)
pair = (1, 'b')
_1 :: Lens (a, b) (a', b) a a'
_1 f (a, b) = (\x -> (x, b)) <$> f a
_2 :: Lens (a, b) (a, b') b b'
_2 f (a, b) = (\x -> (a, x)) <$> f b
both :: Prism (a, a) (b, b) a b
both f (a, b) = (,) <$> f a <*> f b
ex1 = pair ^. _1
ex2 = pair ^. _2
ex3 = pair & _1 .~ "a"
ex4 = pair & (_1 %~ (+1))
. (_2 .~ 1)
ex5 = (1, 2) & both .~ 1
ex6 = Just 3 & _just +~ 1
ex7 = (Left 3) ^? _left
ex8 = over traverse (+1) [1..25]
data Value
= I Int
| D Double
deriving Show
_I :: Prism' Value Int
_I = prism remit review
where
remit :: Int -> Value
remit a = I a
review :: Value -> Either Value Int
review (I a) = Right a
review a = Left a
ex9 :: Maybe Int
ex9 = (I 42) ^? _I
ex10 :: Value
ex10 = over _I succ (I 2)
ex11 :: Value
ex11 = over _I succ (D 2.71)
状態モナドとzoom
Within the context of the state monad there are a particularly useful set of lens patterns.
use
- View a target from the state of the State monad.assign
- Replace the target within a State monad.zoom
- Modify a target of the state with a function and perform it on the global state of the State monad.
So for example if we wanted to write a little physics simulation of the random motion of particles in a box.
We can use the zoom
function to modify the state of our particles in each step of the simulation.
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad.State
import System.Random
data Vector = Vector
{ _x :: Double
, _y :: Double
} deriving (Show)
data Box = Box
{ _particles :: [Particle]
} deriving (Show)
data Particle = Particle
{ _pos :: Vector
, _vel :: Vector
} deriving (Show)
makeLenses ''Box
makeLenses ''Particle
makeLenses ''Vector
step :: StateT Box IO ()
step = zoom (particles.traverse) $ do
dx <- use (vel.x)
dy <- use (vel.y)
pos.x += dx
pos.y += dy
particle :: IO Particle
particle = do
vx <- randomIO
vy <- randomIO
return $ Particle (Vector 0 0) (Vector vx vy)
simulate :: IO Box
simulate = do
particles <- replicateM 5 particle
let simulation = replicateM 5 step
let box = Box particles
execStateT simulation box
main :: IO ()
main = simulate >>= print
This results in a final state like the following.
Box
{ _particles =
[ Particle
{ _pos =
Vector { _x = 3.268546939011934 , _y = 4.356638656040016 }
, _vel =
Vector { _x = 0.6537093878023869 , _y = 0.8713277312080032 }
}
, Particle
{ _pos =
Vector { _x = 0.5492296641559635 , _y = 0.27244422070641594 }
, _vel =
Vector { _x = 0.1098459328311927 , _y = 5.448884414128319e-2 }
}
, Particle
{ _pos =
Vector { _x = 3.961168796078436 , _y = 4.9317543172941765 }
, _vel =
Vector { _x = 0.7922337592156872 , _y = 0.9863508634588353 }
}
, Particle
{ _pos =
Vector { _x = 4.821390854065674 , _y = 1.6601909953629823 }
, _vel =
Vector { _x = 0.9642781708131349 , _y = 0.33203819907259646 }
}
, Particle
{ _pos =
Vector { _x = 2.6468253761062943 , _y = 2.161403445396069 }
, _vel =
Vector { _x = 0.5293650752212589 , _y = 0.4322806890792138 }
}
]
}
LensとAeson
One of the best showcases for lens is writing transformations over arbitrary JSON structures. For example consider some sample data related to Kiva loans.
{
"loans":[
{
"id":2930,
"terms":{
"local_payments":[
{
"due_date":"2007-02-08T08:00:00Z",
"amount":13.75
},
{
"due_date":"2007-03-08T08:00:00Z",
"amount":93.75
},
{
"due_date":"2007-04-08T07:00:00Z",
"amount":43.75
},
{
"due_date":"2007-05-08T07:00:00Z",
"amount":63.75
},
{
"due_date":"2007-06-08T07:00:00Z",
"amount":93.75
},
{
"due_date":"2007-07-08T05:00:00Z",
"amount": null
},
{
"due_date":"2007-07-08T07:00:00Z",
"amount":93.75
},
{
"due_date":"2007-08-08T07:00:00Z",
"amount":93.75
},
{
"due_date":"2007-09-08T07:00:00Z",
"amount":93.75
}
]
}
}
]
}
Then using Data.Aeson.Lens
we can traverse the structure using our lens combinators.
{-# LANGUAGE OverloadedStrings #-}
import Control.Lens
import Data.Aeson.Lens
import Data.Aeson (decode, Value)
import Data.ByteString.Lazy as BL
main :: IO ()
main = do
contents <- BL.readFile "kiva.json"
let Just json = decode contents :: Maybe Value
let vals :: [Double]
vals = json ^.. key "loans"
. values
. key "terms"
. key "local_payments"
. values
. key "amount"
. _Double
print vals
[13.75,93.75,43.75,63.75,93.75,93.75,93.75,93.75]