Question

I am using Edward Kmett's lens library for the first time, and finding it rather nice, but I ran into a snag...

The question at [1] explains that existential quantifiers disrupt makeLenses. I really rather want to use an existential with lenses in some fashion.

As background, I have the class:

class (TextShow file, Eq file, Ord file, Typeable file) => File file where
  fromAnyFile :: AnyFile -> Maybe file
  fileType :: Simple Lens file FileType
  path :: Simple Lens file Text.Text
  provenance :: Simple Lens file Provenance

For the actual question, I want to have the type:

data AnyFile = forall file . File file => AnyFile { _anyFileAnyFile :: File }

And I want to be able to write something along the lines of:

instance File AnyFile where
  fromAnyFile (AnyFile file) = cast file
  fileType (AnyFile file) = fileType . anyFile
  path (AnyFile file) = path . anyFile
  provenance (AnyFile file) = provenance . anyFile

This doesn't work, for the reason explained in [1]. If I ask GHC for debugging information by compiling with -ddump-splices, I get:

Haskell/Main.hs:1:1: Splicing declarations
    makeLenses ''AnyFile ======> Haskell/Main.hs:59:1-20

The splice itself is blank, which indicates to me that no declarations are produced by it. This part I expect and understand now that I have read [1].

What I would like to know is how I can do this - what might I do to work around the problem? What might I do to avoid swimming upstream on this? I would like to be able to access any part of my structures through a path of composed lenses, but because I have fields in other types with types such as Set AnyFile, I cannot do so unless I can access the content of AnyFile with a lens.

[1] Existential quantifier silently disrupts Template Haskell (makeLenses). Why?

Was it helpful?

Solution

In the worst case, you can always implement the lenses yourself without relying on Template Haskell at all.

For example, given a getter and a setter function for your type, you can create a lens using the lens function:

 lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b

I believe this may not be the most performant option, but it's certainly the easiest.

I don't know how to do this for your case (or with existential types in general), but here's a trivial example using a record:

data Foo = Foo { _field :: Int }
foo = lens _field (\ foo new -> foo { _field = new })

Hopefully this illustrates the idea well enough to apply to your code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top