I've looked into how to do this with optics, and you need to combine the two lenses using adjoin
, you can then get a traversal over the element with a particular index using elementOf
.
These can be combined as follows:
I'm going to start by writing out the imports and data declarations in full:
{-# Language TemplateHaskell #-}
{-# Language OverloadedLabels #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
import Optics
import Optics.Label
import Optics.IxTraversal
data Foo = Foo {
list1 :: [Char],
list2 :: [Char]
} deriving Show
makeFieldLabelsNoPrefix ''Foo
foo = Foo ['a', 'b', 'c'] ['d', 'e', 'f']
Then the actual function looks like:
setItem :: Int -> Char -> Foo -> Foo
setItem i c f = set (((#list1 `adjoin` #list2) % traversed) `elementOf` i) c f
Two things to be aware of is that correct usage of adjoin
relies on the targets of both lenses being disjoint; you shouldn't use it with the same lens (or lenses with overlapping targets) as different arguments.
And elementOf
traverses the nth element of the traversal; it doesn't respect the indices of an IxTraversal, it takes any old Traversal and uses the position of each element within that traversal as the index.