There are a few different ways to cook this up. Here's one:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Cat = Cat { _age :: Int }
deriving Show
makeLenses ''Cat
data Item
= I Int
| L [Item]
| C Cat
deriving Show
makePrisms ''Item
cat :: Cat
cat = Cat 3
l :: [Item]
l = [I 1, L [I 2, C cat], I 4]
l' :: [Item]
l' = set (ix 1 . _L . ix 1 . _C . age) 9 l
ghci> l'
[I 1,L [I 2,C (Cat {_age = 9})],I 4]
In Lil[0], this is how ordinary assignment syntax works. Implicitly defining a dictionary stored in a variable named "cat" with a field "age":
cat.age:3
# {"age":3}
Defining "l" as in the example in the article. We need the "list" operator to enlist nested values so that the "," operator doesn't concatenate them into a flat list:
l:1,(list 2,list cat),4
# (1,(2,{"age":3}),4)
Updating the "age" field in the nested dictionary. Lil's basic datatypes are immutable, so "l" is rebound to a new list containing a new dictionary, leaving any previous references undisturbed: