6

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?

Community
  • 1
  • 1
Irene Knapp
  • 145
  • 8
  • Just for anybody wondering, what I did was to use the suggestion below; the definition needed to be `lens (\(AnyFile file) -> file) (\_ value -> AnyFile value)`. – Irene Knapp Aug 05 '13 at 15:46

1 Answers1

7

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.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • Hmm... I did not know about the lens function. Thank you! The docs are rather, er, large. :) I'm trying to make something work now, and will accept the answer if it does. :) – Irene Knapp Aug 05 '13 at 15:39
  • 1
    The type of lens, at least in my version, appears to be `Functor f => (s -> a) -> (s -> b -> t) -> (a -> f b) -> s -> f t`. Okay. Looks like some sort of continuation-passing thing, I can probably figure it out... – Irene Knapp Aug 05 '13 at 15:40
  • Oh I see. Right. I only need the first two parameters because the remaining ones are part of the lens itself... Maybe the docs can explain what the `s` is to me. – Irene Knapp Aug 05 '13 at 15:41
  • Your example helped and I got it working! Yay! Sorry for all the comments, if that spammed you; I am new to Stack Overflow as you can see. :) – Irene Knapp Aug 05 '13 at 15:45
  • 2
    @IreneKnapp: Don't worry about it. I'm glad you got it working. – Tikhon Jelvis Aug 05 '13 at 15:47