
Arie Peterson writes:
There are two things one typically wants to do when working with a substructure of some larger data structure: (1) extract the substructure; and (2) change the larger structure by acting on the substructure. A 'Ref cx t' encodes both of these functions (for a substructure of type 't' and larger structure (context) of type 'cx').
data Ref cx t = Ref { select :: cx -> t , update :: (t -> t) -> cx -> cx }
A Ref is a bit like a typed and composable incarnation of apfelmus's indices, or a wrapper around Tillmann's change* functions, containing not only a setter but also the accompanying getter.
That's a neat idiom. I wonder how far one could usefully generalize it.
For example,
type Ref cx t = forall f. Functor f => (t -> f t) -> cx -> f cx
newtype Id a = Id { unId :: a }
instance Functor Id where fmap f = Id . f . unId
newtype K t a = K { unK :: t }
instance Functor (K t) where fmap = K . unK
select :: Ref cx t -> cx -> t
select ref = unK . ref K
update :: Ref cx t -> (t -> t) -> cx -> cx
update ref f = unId . ref (Id . f)
rfst :: Ref (a,b) a
rfst f (x,y) = fmap (\x' -> (x',y)) (f x)
In this implementation, composition of Refs is just function
composition.
select (rfst . rfst) :: ((a,b),c) -> a
--
David Menendez