It's not that uncommon to have data with self-references in it. This comes up in imperative programming for sure, but it also can show up in Haskell. For example, one can have IORefs or STRefs as fields in a data type that point to the data type itself (and one can use RecursiveDo syntax or mfix to "tie the knot" on construction).
I was wondering if it is possible to do something similar using lenses.
Say I have some state of type s and a lens from s to some data contained in that state. I'd like that contained data to itself have access to this lens, or in other words, I want the data within the state to be something like:
import Data.Lens
data Foo s = Foo
{ self :: Lens' s (Foo s)
, fooData :: Int
}
-- A silly `Show` instance so that we can print a few things
instance Show (Foo s) where
show Foo{..} = "Foo <lens> "<>show fooData
This is a little challenging to use, but it can be made to work with a Fix type like:
newtype Fix f = Fix { unFix :: f (Fix f) }
fixIso :: Iso' (Fix f) (f (Fix f))
fixIso = iso unFix Fix
Now, I can make the following value:
myFoo :: Foo (Fix Foo)
myFoo = Foo fixIso 2
Here, we have a value of type Foo (Fix Foo) with a lens from its state to itself.
I can also create a pair of Foos (by using :*: from Generics):
import GHC.Generics ((:*:)(..))
pairOfFoo :: (Foo :*: Foo) (Fix (Foo :*: Foo))
pairOfFoo = Foo (fixIso . _1) 2 :*: Foo (fixIso . _2) 4
This basically works, as in:
> pairOfFoo ^. _1
Foo <lens> 2
> pairOfFoo ^. _2
Foo <lens> 4
> Fix pairOfFoo ^. (self $ pairOfFoo ^. _1)
Foo <lens> 2
The bigger issue is that it feels like I should be able to create pairOfFoo compositionally from myFoo, but I don't see how to do it. That is, I want to write something like:
pairOf :: (Extendable x, Extendable y) => x (Fix x) -> y (Fix y) -> (x :*: y) (Fix (x :*: y))
pairOf x y = extend x _1 :*: extend y _2
pairOfFoo = pairOf (Foo fixIso 2) (Foo fixIso 4)
class Extendable x where
extend :: Lens' s' s -> x (Fix s) -> x (Fix s')
But this is where I'm stuck. I don't know how to make an instance Extendable Foo (or even if that's the right signature). I also think there should be an instance for (Extendable x, Extendable y) => Extendable (x :*: y) (or similar). Or, maybe there's another strategy altogether?
Problem Extension
Now, let's say we have a second data type defined as such:
data Bar s = Bar
{ barSelf :: Lens' s (Bar s)
, barFoo :: Lens' s (Foo s)
, barData :: String
}
It's impossible to have a value of type Bar (Fix Bar) because Bar does not actually contain a Foo. But, it's possible to make something like:
fooBar :: (Foo :*: Bar) (Fix (Foo :*: Bar))
fooBar = Foo (fixIso . _1) 2 :*: Bar (fixIso . _2) (fixIso . _1) "bar"
Additionally, it feels like it should be possible to have an instance Extendable Bar, so that we can use fooBar as an argument in pairOf. Is this instance possible?
I guess maybe you want:
It seems to work, and extends to more complicated compositions:
Full code: