Giter Site home page Giter Site logo

generic-lens's People

Contributors

amesgen avatar andreasabel avatar arianvp avatar arybczak avatar bodigrim avatar danwdart avatar emilypi avatar jonathanlking avatar kcsongor avatar kquick avatar lightandlight avatar lrworth avatar lunaris avatar lysxia avatar mpickering avatar nomeata avatar parsonsmatt avatar phadej avatar prillan avatar raehik avatar ryanglscott avatar tebello-thejane avatar tobyshaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

generic-lens's Issues

Need the old definition of HasField'

I noticed that the definition of HasField' has changed from a type alias to a type class. We were relying on the previous definition to define HasField' constraints in our functions, which now don't work due to overlapping instances.

I'd like to have the old type alias again if possible. If it is not possible to revert to the old definition of HasField', can we add the type alias back with a different name? For example HasField''?

Allow users to somehow reference non-exported constraints

There are some programs that I can't give a type signature, because the constraints I need to add to the signature are not exported by generic-lens. For example, with Data.Generics.Product.Typed:

theInt :: (Generic s, HasType Int s) => Lens' s Int
theInt = typed @Int

I can't finish this definition, because I can't reference ErrorUnlessOne and HasTotalTypePSym.

I think that just exporting these names would be unwieldy, so we could define a constraint synonym for them:

type HasTypeConstraints a s =
  ( ErrorUnlessOne a s (CollectTotalType a (Rep s))
  , GLens (HasTotalTypePSym a) (Rep s) (Rep s) a a
  )

 instance
  ( Generic s
  , HasTypeConstraints a s
  ) => HasType a s where
...

Then HasTypeConstraints can be exported along with HasType.

Now my definition becomes:

theInt :: (Generic s, HasTypeConstraints Int s, HasType Int s) => Lens' s Int
theInt = typed @Int

Which should now compile.


What do you think? A similar approach would follow for all the classes. I'm happy to do the work if you think this is useful.

Generic lens errors "leak"

An example below, with just test1 it compiles fine. If I uncomment test2 then I not only (correctly) get a type error about test2, but I get one about test1 as well! It unifies the field @"name" lenses together.

In a larger project this is really confusing, you're suddenly getting errors from random parts of a file which you haven't even changed. I've been encountering it a lot when using OverloadedLabels.

Is this a GHC problem or is it expected behaviour (and if it is expected, can anything be done? - it is very unhelpful!)

I should add this is using version 1.1.0 (but I've seen the same thing in all versions I've used previously).

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}
import Prelude
import GHC.Generics
import Control.Lens
import Data.Generics.Product

data Foo = Foo { name :: String } deriving (Show, Generic)

test1 :: Foo -> String
test1 foo = foo ^. field @"name"

-- test2 :: Foo -> Int
-- test2 bar = bar ^. field @"name"

../Test.hs:14:20: error:
• Couldn't match type ‘Int’ with ‘[Char]’
arising from a functional dependency between constraints:
‘generic-lens-1.1.0.0:Data.Generics.Product.Internal.GLens.GLens
(Data.Generics.Product.Fields.HasTotalFieldPSym "name")
(K1 R String)
(K1 R String)
[Char]
[Char]’
arising from a use of ‘field’ at ../Test.hs:14:20-32
‘generic-lens-1.1.0.0:Data.Generics.Product.Internal.GLens.GLens
(Data.Generics.Product.Fields.HasTotalFieldPSym "name")
(K1 R String)
(K1 R String)
Int
Int’
arising from a use of ‘field’ at ../Test.hs:17:20-32
• In the second argument of ‘(^.)’, namely ‘field @"name"’
In the expression: foo ^. field @"name"
In an equation for ‘test1’: test1 foo = foo ^. field @"name"
|
14 | test1 foo = foo ^. field @"name"
| ^^^^^^^^^^^^^

../Test.hs:17:20: error:
• Couldn't match type ‘[Char]’ with ‘Int’
arising from a use of ‘field’
• In the second argument of ‘(^.)’, namely ‘field @"name"’
In the expression: bar ^. field @"name"
In an equation for ‘test2’: test2 bar = bar ^. field @"name"
|
17 | test2 bar = bar ^. field @"name"

Type-changing lenses with nominal roles: a "field" without type inference

Follow up on #40, #45

It seems that for the field lens, the type parameter tagging is used only for type inference, and the above issues mention it breaking in the presence of nominal roles. What do you think of a HasField0 class that is a variant of HasField, without type inference tricks, so that it can still be used with more types? In fact, the HasField instance can then be expressed in terms of HasField0.


I have a quick proof of concept disabling type inference here

Lysxia@3b20812

So we can indeed use type-changing lenses with data families:

> import Data.Generics.Product
> import Data.Generics.Internal.VL.Lens 
> data family T a :: * -> *
> data instance T Bool b = C { f :: b } deriving (Show,Generic)
> field @"f" @_ @(T Bool Int) .~ 3 $ C ()     -- T Bool () -> T Bool Int
C {f = 3}

On a related note, have you considered using the following class instead of the "tagging trick" for unification of s and t:

class Unify (a :: k) (b :: k)
instance {-# OVERLAPPING #-} (gb ~ g b, Unify f g) => Unify (f a) gb
instance (a ~ b) => Unify a b

This unifies just the type constructor, which is not as good as the tagging trick in general, but is often good enough for Rep to reduce and let the other constraints do their job. It might also work well with the type family example of #45 if for some reason you can otherwise infer the parameter a but not the type constructor.

Together with my proposal above that could be just another variant of field to export.

Type error when composing a polymorphic lens

I get a type error when composing a lens defined with field for a polymorphic type twice in an expression.
The module below compiles with 1.0.0.1 but not with 1.0.0.2, both on GHC 8.4.3.

{-# LANGUAGE DataKinds, DeriveGeneric, TypeApplications #-}
module Lib (example) where
import Data.Generics.Product (field)
import Data.Generics.Internal.VL.Lens (set)
import GHC.Generics (Generic)

data Foo a = Foo { bar :: Bar a } deriving Generic
data Bar a = Bar { x :: a, y :: a } deriving Generic

example :: Foo ()
example =
  set (field @"bar" . field @"x") ()
  . set (field @"bar" . field @"y") ()
  $ Foo{ bar = Bar{ x = (), y = () } }

The error is:

src/Lib.hs:12:8: error:
    • Couldn't match type ‘generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.ReplaceArgs
                             (Foo ())
                             (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.Unify
                                (Bar
                                   (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.P
                                      0
                                      ()
                                      'generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.PTag))
                                a0)’
                     with ‘generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.ReplaceArgs
                             (Foo ())
                             (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.Unify
                                (Bar
                                   (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.P
                                      0
                                      ()
                                      'generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.PTag))
                                b1)’
        arising from a use of ‘field’
      NB: ‘generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.ReplaceArgs’ is a non-injective type family
      The type variables ‘a0’, ‘b1’ are ambiguous
    • In the first argument of ‘(.)’, namely ‘field @"bar"’
      In the first argument of ‘set’, namely
        ‘(field @"bar" . field @"x")’
      In the first argument of ‘(.)’, namely
        ‘set (field @"bar" . field @"x") ()’
   |
12 |   set (field @"bar" . field @"x") ()
   |        ^^^^^^^^^^^^

src/Lib.hs:12:8: error:
    • Couldn't match type ‘generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.Infer
                             (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.ReplaceArgs
                                (Foo ())
                                (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.Unify
                                   (Bar
                                      (generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.P
                                         0
                                         ()
                                         'generic-lens-1.0.0.2:Data.Generics.Internal.Families.Changing.PTag))
                                   b1))
                             a'0
                             b0’
                     with ‘Foo ()’
        arising from a use of ‘field’
      The type variables ‘b1’, ‘a'0’, ‘b0’ are ambiguous
    • In the first argument of ‘(.)’, namely ‘field @"bar"’
      In the first argument of ‘set’, namely
        ‘(field @"bar" . field @"x")’
      In the first argument of ‘(.)’, namely
        ‘set (field @"bar" . field @"x") ()’
   |
12 |   set (field @"bar" . field @"x") ()
   |        ^^^^^^^^^^^^

(And the same thing for line 13.)

Getting support

For record types, need a way to express new getting fields not already in the type. These are computed on access. For example something like:

data Person = Person
  { firstname :: String
  , lastname :: String
  } deriving Generic

instance Getter "fullName" Record String where
  getter = to (\r -> (r ^. the @"field1") ++ " " ++ (r ^. the @"field2"))

...
let r = Person {..}
let fullName = r ^. the @"fullName"

First-class record construction.

One of the things that often annoy me in Haskell is that record construction is not a first-class element. For example, imagine this simple record construction:

john = Person { name = "John", age = 42 }

There are two problems with this definition:

  • { name = "John", age = 42 } cannot be assigned to a variable nor reused anywhere else.

  • It cannot be composed with other record constructors to get the union of all the fields. If you use lenses, you can compose record updates (through setters) but not record construction AFAIK.

Given the record/hlist isomorphism it should not be too hard to implement a first-class record "builder". Internally it can be expressed as an hlist (with convenient user-facing types and operators to set and compose fields) and record construction would consist of reordering the elements of the list and converting to the record representation.

This feature would allow for a form of composition that goes beyond the subtype/supertype relation. I have often needed it, but I have always thought this was impossible to achieve in Haskell. Now I realized it might be doable, and generic-lens already has the necessary infrastructure. What do you think of the idea?

Lens when composed seem to fail to support type changing

When composing two field or position Lenses to obtain a type-changing Lens, the compiler generates an error. It seems that the error shows up when the value being changed is a record (not a tuple) and the result of the composition is not a simple Lens' s t.

For details, see test case and compiler errors below:

Test case:

{-# LANGUAGE DataKinds, DeriveGeneric, TypeApplications #-}

import Control.Lens
import Data.Generics.Product.Fields
import Data.Generics.Product.Positions
import GHC.Generics

data Foo a b = Foo { x1 :: a, x2 :: b } deriving (Generic, Show)

data Bar a b = Bar { x3 :: Foo a b, x4 :: Int } deriving (Generic, Show)

tup :: ((Int, Char), Int)
tup = ((1, 'a'), 2)
tup2, tup3, tup4 :: ((Char, Char), Int)
tup2 = tup & _1 . _1 %~ toEnum  -- Works.
tup3 = tup & x %~ toEnum  -- Works also with type annotation.
  where x :: Lens ((Int, Char), Int) ((Char, Char), Int) Int Char
        x = _1 . _1
-- Works.
tup4 = tup & position @1 . position @1 %~ toEnum

foo :: Foo Int Char
foo = Foo 1 'a'
foo2, foo3 :: Foo Char Char
foo2 = foo & field @"x1" %~ toEnum  -- Works when there's just one 'field'.
foo3 = foo & position @1 %~ toEnum -- Works when there's just one 'position'.

bar :: Bar Int Char
bar = Bar (Foo 1 'a') 2
bar2, bar3, bar4 :: Bar Char Char
-- Doesn't work, error at first 'field' (Couldn't match type ‘Int’ with ‘Char’ arising from a use of ‘field’).
bar2 = bar & field @"x3" . field @"x1" %~ toEnum
-- Type annotation doesn't help.
bar3 = bar & l %~ toEnum
  where l :: Lens (Bar Int Char) (Bar Char Char) Int Char
        l = field @"x3" . field @"x1"
-- Doesn't work, error at first 'position' (Couldn't match type ‘Int’ with ‘Char’ arising from a use of ‘position’).
bar4 = bar & position @1 . position @1 %~ toEnum
-- Works if we stick to simple Lens' (modify to the same type).
bar5 :: Bar Int Char
bar5 = bar & field @"x3" . field @"x1" %~ (+1)

main :: IO ()
main = print bar5

Compiler errors:

/home/jchia/hs/test/app/Main3.hs:32:14: error:
    • Couldn't match type ‘Int’ with ‘Char’
        arising from a use of ‘field’
    • In the first argument of ‘(.)’, namely ‘field @"x3"’
      In the first argument of ‘(%~)’, namely ‘field @"x3" . field @"x1"’
      In the second argument of ‘(&)’, namely
        ‘field @"x3" . field @"x1" %~ toEnum’
   |
32 | bar2 = bar & field @"x3" . field @"x1" %~ toEnum
   |              ^^^^^^^^^^^

/home/jchia/hs/test/app/Main3.hs:36:13: error:
    • Couldn't match type ‘Int’ with ‘Char’
        arising from a use of ‘field’
    • In the first argument of ‘(.)’, namely ‘field @"x3"’
      In the expression: field @"x3" . field @"x1"
      In an equation for ‘l’: l = field @"x3" . field @"x1"
   |
36 |         l = field @"x3" . field @"x1"
   |             ^^^^^^^^^^^

/home/jchia/hs/test/app/Main3.hs:36:27: error:
    • Couldn't match type ‘Char’ with ‘Int’
        arising from a use of ‘field’
    • In the second argument of ‘(.)’, namely ‘field @"x1"’
      In the expression: field @"x3" . field @"x1"
      In an equation for ‘l’: l = field @"x3" . field @"x1"
   |
36 |         l = field @"x3" . field @"x1"
   |                           ^^^^^^^^^^^

/home/jchia/hs/test/app/Main3.hs:38:14: error:
    • Couldn't match type ‘Int’ with ‘Char’
        arising from a use of ‘position’
    • In the first argument of ‘(.)’, namely ‘position @1’
      In the first argument of ‘(%~)’, namely ‘position @1 . position @1’
      In the second argument of ‘(&)’, namely
        ‘position @1 . position @1 %~ toEnum’
   |
38 | bar4 = bar & position @1 . position @1 %~ toEnum
   |              ^^^^^^^^^^^

Expose a RowToList?

We talked about this before but I think you said you had it currently specifically just to drag out labels right? Could you expose a GRowToList in this library if it's not too much of a hassle?

Switching from 0.5.1.0 to 1.0.0.1 makes GHC hang forever

If I switch to 1.0.0.1 on a medium-sized code base that was using 0.5.1.0 the compilation does not terminate.

Unfortunately I cannot be more specific on the code that is causing non-termination. I tried commenting out functions one by one to locate the incriminated lines, and I found that the only way to make it compile is to remove all the functions that use generic-lens.

It happens in GHC whatever the -O level, and in GHCI too. If I build with -v I can see it gets stuck during the Renamer/typechecker phase.

I have already used 1.0.0.1 in small "toy" projects and I haven't had any issue. So I cannot exclude a bug in my code. However, whatever the reason of the problem, it is clearly related to generic-lens, so I think it is worth reporting it.

Bug when used with type families

There is a bug when combined with type families:

data IType (a :: Nat) = IType
  deriving Generic

data BadTest a = BadTest {name1 :: IType (2*a)}
  deriving Generic

data OKTest a = OKTest {name2 :: IType a}
  deriving Generic

fails = view (field @"name1")

ok = view (field @"name2")

Couldn't match type ‘generic-lens-1.0.0.1:Data.Generics.Internal.Families.Changing.ReplaceArgs
(BadTest a)
(generic-lens-1.0.0.1:Data.Generics.Internal.Families.Changing.Unify
(2
* generic-lens-1.0.0.1:Data.Generics.Internal.Families.Changing.P
0
a
'generic-lens-1.0.0.1:Data.Generics.Internal.Families.Changing.PTag)
(2 * a)
generic-lens-1.0.0.1:Data.Generics.Internal.Families.Changing.++ '[])’
with ‘BadTest a’
arising from a use of ‘field’

Deep typed

deepTyped @Bool :: Lens' ((Bool, Int), Int) Bool
  • typed looks only one layer
  • types gives traversal

This is partly related to #71, it wants typed to generalise to Iso when possible; here I want types to generalise to Lens (and why not also Iso) if possible. Although I'm ok with using a new combinator too.

Roadmap required

Where on the roadmap is the PHP backend?
Not being able to run this lib within PHP is really blocking my workflow.

Unable to traverse with `types` if `Text` is present

Hi, I was toying around with types traversal and observed a change of behaviour when I start using Text in my records.

Consider this MWE (imports in cabal : text, lens, generic-lens, generic-deriving, with lts-12.20 and ghc-8.4.4):

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}
module Main (main) where
import           Control.Lens.Fold
import           Data.Generics.Product.Types
import           Data.Text
import           GHC.Generics

newtype Name = Name Text deriving (Eq, Show, Generic)

data Description = N None | S Sirname | M Multiple deriving (Eq, Show, Generic)

data None = None deriving (Eq, Show, Generic)

data Sirname = Sirname {
    honorific :: Text,
    name      :: Name
} deriving (Eq, Show, Generic)

data Multiple = Multiple {
    descr  :: Text,
    titles :: [(Text, [Description])]
} deriving (Eq, Show, Generic )


main :: IO ()
main = 
    print $ toListOf (types @Name) person



person :: Description
person = M $ Multiple "" [
    ( "born", [N None] ),
    ( "created", [S $ Sirname "sir" (Name "John") ] ),
    ( "created", [S $ Sirname "duke of" (Name "Duchy") ] )
    ]

The compilation fails with

    • No instance for (Data.Generics.Product.Types.HasTypes'
                         (Data.Generics.Product.Types.Snd
                            (Data.Generics.Product.Types.InterestingOr
                               (Data.Generics.Product.Types.InterestingOr
                                  (Data.Generics.Product.Types.Interesting'
                                     (Rep Text) Name '[Text, Sirname, None, Description])
                                  (M1
                                     S
                                     ('MetaSel
                                        ('Just "name")
                                        'NoSourceUnpackedness
                                        'NoSourceStrictness
                                        'DecidedLazy)
                                     (Rec0 Name))
                                  Name)
                               (M1
                                  C
                                  ('MetaCons "M" 'PrefixI 'False)
                                  (S1
                                     ('MetaSel
                                        'Nothing
                                        'NoSourceUnpackedness
                                        'NoSourceStrictness
                                        'DecidedLazy)
                                     (Rec0 Multiple)))
                               Name))
                         Description
                         Name)
        arising from a use of ‘types’
    • In the first argument of ‘toListOf’, namely ‘(types @Name)’
      In the second argument of ‘($)’, namely
        ‘toListOf (types @Name) person’
      In the expression: print $ toListOf (types @Name) person

However, if I replace all mentions of Text by String, the compilation goes well and the output is correct.

How do I circumvent this problem?

HasField type resolution error

I was just upgrading from 0.4.1 to 0.5.1 and I got an error with code using generic-lens and data-default, that had previously been ok. GHC gives:

Overlapping instances for HasField "field1" s0 Record1 a0 Int
        arising from a use of ‘field’

and

Ambiguous type variable ‘s0’ arising from a use of ‘def’
      prevents the constraint ‘(Default s0)’ from being solved.
      

(Full error below)

The error goes away if I type annotate, but I have a lot of def's, and it would kind of defeat the code clarity I'm achieving with combining default and generic-lens 0.4.1.

Thanks for the library!

Test case:

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# OPTIONS_GHC -Wall #-}

module Main where

import Control.Lens
import Data.Default
import Data.Generics.Product
import GHC.Generics

data Record1 = Record1
    { field1 :: Int
    , field2 :: Double
    } deriving (Generic)

instance Default Record1 where
    def = Record1 0 0.0

f :: Record1 -> Int
f r = r ^. field @"field1"

main :: IO ()
main = do
    print $ f def
    print $ f ( field @"field1" .~ 1 $ (def :: Record1))
    print $ f ( field @"field1" .~ 2 $ Record1 0 0.0)
    -- This errors with a type resolution problem
    -- print $ f ( field @"field1" .~ (1 :: Int) $ def)

full error:

    • Overlapping instances for HasField "field1" s0 Record1 a0 Int
        arising from a use of ‘field’
      Matching instances:
        instance [safe] (Generic s,
                         Data.Generics.Product.Fields.ErrorUnless
                           field
                           s
                           (generic-lens-0.5.1.0:Data.Generics.Internal.Families.Collect.CollectField
                              field (Rep s)),
                         Generic t,
                         s'
                         ~
                         generic-lens-0.5.1.0:Data.Generics.Internal.Families.Changing.Proxied
                           s,
                         Generic s',
                         generic-lens-0.5.1.0:Data.Generics.Product.Internal.Fields.GHasField'
                           field (Rep s) a,
                         generic-lens-0.5.1.0:Data.Generics.Product.Internal.Fields.GHasField'
                           field (Rep s') a',
                         generic-lens-0.5.1.0:Data.Generics.Product.Internal.Fields.GHasField
                           field (Rep s) (Rep t) a b,
                         '(t, b)
                         ~
                         generic-lens-0.5.1.0:Data.Generics.Internal.Families.Changing.Infer
                           s'
                           a'
                           a
                           (generic-lens-0.5.1.0:Data.Generics.Internal.Families.Changing.PickTv
                              a' b)) =>
                        HasField field s t a b
          -- Defined in ‘Data.Generics.Product.Fields’
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
      (The choice depends on the instantiation of ‘s0, a0’
       To pick the first instance above, use IncoherentInstances
       when compiling the other instance declarations)
    • In the first argument of ‘(.~)’, namely ‘field @"field1"’
      In the expression: field @"field1" .~ (1 :: Int)
      In the first argument of ‘f’, namely
        ‘(field @"field1" .~ (1 :: Int) $ def)’
   |
28 |     print $ f ( field @"field1" .~ (1 :: Int) $ def)
   |                 ^^^^^^^^^^^^^^^

/Users/tonyday/haskell/gll-example/app/simple.hs:28:49: error:
    • Ambiguous type variable ‘s0’ arising from a use of ‘def’
      prevents the constraint ‘(Default s0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘s0’ should be.
      These potential instances exist:
        instance [safe] Default a => Default (IO a)
          -- Defined in ‘data-default-class-0.1.2.0:Data.Default.Class’
        instance [safe] Default Ordering
          -- Defined in ‘data-default-class-0.1.2.0:Data.Default.Class’
        instance [safe] Default Integer
          -- Defined in ‘data-default-class-0.1.2.0:Data.Default.Class’
        ...plus 15 others
        ...plus 47 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the second argument of ‘($)’, namely ‘def’
      In the first argument of ‘f’, namely
        ‘(field @"field1" .~ (1 :: Int) $ def)’
      In the second argument of ‘($)’, namely
        ‘f (field @"field1" .~ (1 :: Int) $ def)’
   |
28 |     print $ f ( field @"field1" .~ (1 :: Int) $ def)
   |                                                 ^^^

Expose a convenient constraint for "lacking" a type

I've got a gist for typed exception handling using generic-lens and classy prisms: https://gist.github.com/parsonsmatt/880fbf79eaad6ed863786c6c02f8ddc9

The important bits:

data FooErr = FooErr Int
    deriving (Show, Generic)

data BarErr = BarErr String
    deriving (Generic, Show)

foo :: AsType FooErr e => Either e Int
foo = Left (review (_Typed @FooErr) (FooErr 3))

bar :: AsType BarErr e => Either e Int
bar = Left (review (_Typed @BarErr) (BarErr "hello"))

foobar :: (AsType BarErr e, AsType FooErr e) => Either e Int
foobar = bar *> foo *> bar

plucked :: _ => Either (Either FooErr e) Int
plucked = foobar

So the plucked function has a type wildcard, for two reasons: a) the type families are not exposed at all, and b) the inferred constraint is kind of huge. I would like to be able to write something like:

plucked :: (AsType BarErr e, Lacking FooErr e) => Either (Either FooErr e) Int

I think that's what the inferred constraints roughly mean.

Add source links to documentation

It's quite useful to see the source code of a given identifier. Also, the documentation was uploaded manually. Did Hackage fail to build it? There are differences between the default settings of a local build and those of Hackage that are a bit distracting sometimes (like the "Contents" link at the top not linking to the homepage).

Thanks for this awesome package!

Positions for non-records

λ D.G.P.Positions C.Lens> over (position @0) succ ('a', 'b', 'c')

<interactive>:7:7: error:
    • The type (Char, Char,
                Char) does not contain a field at position 0
    • In the first argument of ‘over’, namely ‘(position @0)’
      In the expression: over (position @0) succ ('a', 'b', 'c')
      In an equation for ‘it’:
          it = over (position @0) succ ('a', 'b', 'c')

Field and Constructor instances in Data.Generic.Labels

Can you explain why these are incoherent?

I think if it works as expected, it does only by accident. If any of the s ~ t or a ~ b doesn't hold, then Field name s t a b is picked, but otherwise arbitrary instance from Field name s s a a and Field name s t a b will be picked. Having

instance {-# OVERLAPPABLE #-} HasField name s t a b => Field name s t a b where
  fieldLens = field @name

instance HasField' name s a => Field name s s a a where
  fieldLens = field' @name

or potentially

instance HasField name s t a b => Field name s t a b where
  fieldLens = field @name

instance {-# OVERLAPPING #-} HasField' name s a => Field name s s a a where
  fieldLens = field' @name

would make instance choice deterministic.

Can you also please explain how these instances differ? Why can't just the one for more general case be used? Unfortunately I can't seem to be able to decipher this from their definitions.

Benchmark

I'd be thrilled to use this but I fear for performance a bit. Are there any benchmarks you could publish?

Test suite failures with 1.0.0.0

Below is the full output from the Stackage build server:

> /tmp/stackage-build11/generic-lens-1.0.0.0$ ghc -clear-package-db -global-package-db -package-db=/var/stackage/work/builds/nightly/pkgdb Setup > /tmp/stackage-build11/generic-lens-1.0.0.0$ ./Setup configure --enable-tests --package-db=clear --package-db=global --package-db=/var/stackage/work/builds/nightly/pkgdb --libdir=/var/stackage/work/builds/nightly/lib --bindir=/var/stackage/work/builds/nightly/bin --datadir=/var/stackage/work/builds/nightly/share --libexecdir=/var/stackage/work/builds/nightly/libexec --sysconfdir=/var/stackage/work/builds/nightly/etc --docdir=/var/stackage/work/builds/nightly/doc/generic-lens-1.0.0.0 --htmldir=/var/stackage/work/builds/nightly/doc/generic-lens-1.0.0.0 --haddockdir=/var/stackage/work/builds/nightly/doc/generic-lens-1.0.0.0 Configuring generic-lens-1.0.0.0... > /tmp/stackage-build11/generic-lens-1.0.0.0$ ghc -clear-package-db -global-package-db -package-db=/var/stackage/work/builds/nightly/pkgdb Setup > /tmp/stackage-build11/generic-lens-1.0.0.0$ ./Setup build Preprocessing test suite 'examples-doctests' for generic-lens-1.0.0.0.. Building test suite 'examples-doctests' for generic-lens-1.0.0.0.. [1 of 1] Compiling Main ( examples/doctest.hs, dist/build/examples-doctests/examples-doctests-tmp/Main.o ) Linking dist/build/examples-doctests/examples-doctests ... Preprocessing test suite 'doctests' for generic-lens-1.0.0.0.. Building test suite 'doctests' for generic-lens-1.0.0.0.. [1 of 1] Compiling Main ( test/doctest.hs, dist/build/doctests/doctests-tmp/Main.o ) Linking dist/build/doctests/doctests ... Preprocessing library for generic-lens-1.0.0.0.. Building library for generic-lens-1.0.0.0.. [ 1 of 36] Compiling Data.Generics.Internal.Families.Changing ( src/Data/Generics/Internal/Families/Changing.hs, dist/build/Data/Generics/Internal/Families/Changing.o ) [ 2 of 36] Compiling Data.Generics.Internal.GenericN ( src/Data/Generics/Internal/GenericN.hs, dist/build/Data/Generics/Internal/GenericN.o ) [ 3 of 36] Compiling Data.Generics.Internal.Profunctor.Iso ( src/Data/Generics/Internal/Profunctor/Iso.hs, dist/build/Data/Generics/Internal/Profunctor/Iso.o ) [ 4 of 36] Compiling Data.Generics.Internal.Profunctor.Lens ( src/Data/Generics/Internal/Profunctor/Lens.hs, dist/build/Data/Generics/Internal/Profunctor/Lens.o ) [ 5 of 36] Compiling Data.Generics.Internal.Profunctor.Prism ( src/Data/Generics/Internal/Profunctor/Prism.hs, dist/build/Data/Generics/Internal/Profunctor/Prism.o ) [ 6 of 36] Compiling Data.Generics.Internal.VL.Iso ( src/Data/Generics/Internal/VL/Iso.hs, dist/build/Data/Generics/Internal/VL/Iso.o ) [ 7 of 36] Compiling Data.Generics.Internal.VL.Lens ( src/Data/Generics/Internal/VL/Lens.hs, dist/build/Data/Generics/Internal/VL/Lens.o ) [ 8 of 36] Compiling Data.Generics.Internal.VL.Prism ( src/Data/Generics/Internal/VL/Prism.hs, dist/build/Data/Generics/Internal/VL/Prism.o ) [ 9 of 36] Compiling Data.Generics.Internal.VL.Traversal ( src/Data/Generics/Internal/VL/Traversal.hs, dist/build/Data/Generics/Internal/VL/Traversal.o ) [10 of 36] Compiling Data.Generics.Internal.Void ( src/Data/Generics/Internal/Void.hs, dist/build/Data/Generics/Internal/Void.o ) [11 of 36] Compiling Data.Generics.Product.Internal.Constraints ( src/Data/Generics/Product/Internal/Constraints.hs, dist/build/Data/Generics/Product/Internal/Constraints.o ) [12 of 36] Compiling Data.Generics.Product.Constraints ( src/Data/Generics/Product/Constraints.hs, dist/build/Data/Generics/Product/Constraints.o ) [13 of 36] Compiling Data.Generics.Product.Internal.List ( src/Data/Generics/Product/Internal/List.hs, dist/build/Data/Generics/Product/Internal/List.o )

src/Data/Generics/Product/Internal/List.hs:44:1: warning: [-Wunused-imports]
The import of ‘Data.Semigroup’ is redundant
except perhaps to import instances from ‘Data.Semigroup’
To import instances alone, use: import Data.Semigroup()
|
44 | import Data.Semigroup
| ^^^^^^^^^^^^^^^^^^^^^
[14 of 36] Compiling Data.Generics.Product.Internal.Keyed ( src/Data/Generics/Product/Internal/Keyed.hs, dist/build/Data/Generics/Product/Internal/Keyed.o )
[15 of 36] Compiling Data.Generics.Internal.Families.Has ( src/Data/Generics/Internal/Families/Has.hs, dist/build/Data/Generics/Internal/Families/Has.o )
[16 of 36] Compiling Data.Generics.Internal.Families.Collect ( src/Data/Generics/Internal/Families/Collect.hs, dist/build/Data/Generics/Internal/Families/Collect.o )
[17 of 36] Compiling Data.Generics.Internal.Families ( src/Data/Generics/Internal/Families.hs, dist/build/Data/Generics/Internal/Families.o )
[18 of 36] Compiling Data.Generics.Product.Fields ( src/Data/Generics/Product/Fields.hs, dist/build/Data/Generics/Product/Fields.o )
[19 of 36] Compiling Data.Generics.Product.Internal.Positions ( src/Data/Generics/Product/Internal/Positions.hs, dist/build/Data/Generics/Product/Internal/Positions.o )
[20 of 36] Compiling Data.Generics.Product.Internal.Subtype ( src/Data/Generics/Product/Internal/Subtype.hs, dist/build/Data/Generics/Product/Internal/Subtype.o )
[21 of 36] Compiling Data.Generics.Product.List ( src/Data/Generics/Product/List.hs, dist/build/Data/Generics/Product/List.o )
[22 of 36] Compiling Data.Generics.Product.Param ( src/Data/Generics/Product/Param.hs, dist/build/Data/Generics/Product/Param.o )
[23 of 36] Compiling Data.Generics.Product.Positions ( src/Data/Generics/Product/Positions.hs, dist/build/Data/Generics/Product/Positions.o )
[24 of 36] Compiling Data.Generics.Product.Subtype ( src/Data/Generics/Product/Subtype.hs, dist/build/Data/Generics/Product/Subtype.o )
[25 of 36] Compiling Data.Generics.Product.Typed ( src/Data/Generics/Product/Typed.hs, dist/build/Data/Generics/Product/Typed.o )
[26 of 36] Compiling Data.Generics.Product.Any ( src/Data/Generics/Product/Any.hs, dist/build/Data/Generics/Product/Any.o )
[27 of 36] Compiling Data.Generics.Product.Types ( src/Data/Generics/Product/Types.hs, dist/build/Data/Generics/Product/Types.o )
[28 of 36] Compiling Data.Generics.Product ( src/Data/Generics/Product.hs, dist/build/Data/Generics/Product.o )
[29 of 36] Compiling Data.Generics.Sum.Internal.Constructors ( src/Data/Generics/Sum/Internal/Constructors.hs, dist/build/Data/Generics/Sum/Internal/Constructors.o )
[30 of 36] Compiling Data.Generics.Sum.Constructors ( src/Data/Generics/Sum/Constructors.hs, dist/build/Data/Generics/Sum/Constructors.o )
[31 of 36] Compiling Data.Generics.Sum.Internal.Typed ( src/Data/Generics/Sum/Internal/Typed.hs, dist/build/Data/Generics/Sum/Internal/Typed.o )
[32 of 36] Compiling Data.Generics.Sum.Internal.Subtype ( src/Data/Generics/Sum/Internal/Subtype.hs, dist/build/Data/Generics/Sum/Internal/Subtype.o )
[33 of 36] Compiling Data.Generics.Sum.Subtype ( src/Data/Generics/Sum/Subtype.hs, dist/build/Data/Generics/Sum/Subtype.o )
[34 of 36] Compiling Data.Generics.Sum.Typed ( src/Data/Generics/Sum/Typed.hs, dist/build/Data/Generics/Sum/Typed.o )
[35 of 36] Compiling Data.Generics.Sum.Any ( src/Data/Generics/Sum/Any.hs, dist/build/Data/Generics/Sum/Any.o )
[36 of 36] Compiling Data.Generics.Sum ( src/Data/Generics/Sum.hs, dist/build/Data/Generics/Sum.o )
Preprocessing test suite 'generic-lens-bifunctor' for generic-lens-1.0.0.0..
Building test suite 'generic-lens-bifunctor' for generic-lens-1.0.0.0..
[1 of 1] Compiling Main ( test/Bifunctor.hs, dist/build/generic-lens-bifunctor/generic-lens-bifunctor-tmp/Main.o )
Linking dist/build/generic-lens-bifunctor/generic-lens-bifunctor ...
Preprocessing test suite 'generic-lens-test' for generic-lens-1.0.0.0..
Building test suite 'generic-lens-test' for generic-lens-1.0.0.0..
[1 of 5] Compiling Test24 ( test/Test24.hs, dist/build/generic-lens-test/generic-lens-test-tmp/Test24.o )
[2 of 5] Compiling Test25 ( test/Test25.hs, dist/build/generic-lens-test/generic-lens-test-tmp/Test25.o )
[3 of 5] Compiling Test40 ( test/Test40.hs, dist/build/generic-lens-test/generic-lens-test-tmp/Test40.o )
[4 of 5] Compiling Util ( test/Util.hs, dist/build/generic-lens-test/generic-lens-test-tmp/Util.o )
[5 of 5] Compiling Main ( test/Spec.hs, dist/build/generic-lens-test/generic-lens-test-tmp/Main.o )
Linking dist/build/generic-lens-test/generic-lens-test ...
Preprocessing test suite 'generic-lens-syb-tree' for generic-lens-1.0.0.0..
Building test suite 'generic-lens-syb-tree' for generic-lens-1.0.0.0..
[1 of 1] Compiling Main ( test/syb/Tree.hs, dist/build/generic-lens-syb-tree/generic-lens-syb-tree-tmp/Main.o )
Linking dist/build/generic-lens-syb-tree/generic-lens-syb-tree ...
Preprocessing test suite 'generic-lens-examples' for generic-lens-1.0.0.0..
Building test suite 'generic-lens-examples' for generic-lens-1.0.0.0..
[1 of 1] Compiling Main ( examples/Biscuits.hs, dist/build/generic-lens-examples/generic-lens-examples-tmp/Main.o )
Linking dist/build/generic-lens-examples/generic-lens-examples ...

/tmp/stackage-build11/generic-lens-1.0.0.0$ dist/build/generic-lens-examples/generic-lens-examples
hello
/tmp/stackage-build11/generic-lens-1.0.0.0$ dist/build/generic-lens-test/generic-lens-test

Cases: 14 Tried: 0 Errors: 0 Failures: 0
Cases: 14 Tried: 1 Errors: 0 Failures: 0
Cases: 14 Tried: 2 Errors: 0 Failures: 0
Cases: 14 Tried: 3 Errors: 0 Failures: 0

Failure in: 3

test/Util.hs:10
test/Spec.hs:222:7: subtypeLensManual === subtypeLensGeneric failed:
LHS:
$s$fSubtypeba_$csuper
$s$fSubtypeba_$csuper = subtypeLensGeneric

    subtypeLensManual
    subtypeLensManual = $s$fSubtypeba_$csuper
    
RHS:

Cases: 14 Tried: 4 Errors: 0 Failures: 1
Cases: 14 Tried: 5 Errors: 0 Failures: 1
Cases: 14 Tried: 6 Errors: 0 Failures: 1
Cases: 14 Tried: 7 Errors: 0 Failures: 1

Failure in: 7

test/Util.hs:10
test/Spec.hs:226:7: sum1PrismManual === sum1PrismB failed:
LHS:
sum1PrismManual
sum1PrismManual
= \ @ p @ f $dChoice_ardO $dApplicative_ardP eta ->
let {
$dFunctor
$dFunctor = $p1Applicative $dApplicative_ardP } in
dimap
($p1Choice $dChoice_ardO)
(\ x ->
case x of wild_X4A {
__DEFAULT -> Left (pure $dApplicative_ardP wild_X4A);
B i -> Right i
})
(\ ds1 ->
case ds1 of {
Left x -> x;
Right y -> fmap $dFunctor B y
})
(right' $dChoice_ardO eta)

RHS:
    sum1PrismB
    sum1PrismB
      = \ @ p @ f $dChoice_ar97 $dApplicative_ar98 eta ->
          let {
            $dFunctor
            $dFunctor = $p1Applicative $dApplicative_ar98 } in
          dimap
            ($p1Choice $dChoice_ar97)
            (\ x ->
               join {
                 $j_sy30
                 $j_sy30 x1
                   = Left
                       (pure
                          $dApplicative_ar98
                          (case x1 of {
                             Left x1 ->
                               case x1 of {
                                 L1 ds_dv3r ->
                                   case ds_dv3r of {
                                     L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                     R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                                   };
                                 R1 ds_dv3y ->
                                   case ds_dv3y of {
                                     L1 ds_dv3z -> C (ds_dv3z `cast` <Co:51>);
                                     R1 ds_dv3C -> D (ds_dv3C `cast` <Co:51>)
                                   }
                               };
                             Right y ->
                               case y of {
                                 L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                 R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                               }
                           })) } in
               case x of {
                 A g1 -> jump $j_sy30 (Right (L1 (g1 `cast` <Co:54>)));
                 B g1 -> Right g1;
                 C g1 -> jump $j_sy30 (Left (R1 (L1 (g1 `cast` <Co:54>))));
                 D g1 -> jump $j_sy30 (Left (R1 (R1 (g1 `cast` <Co:54>))))
               })
            (\ ds1 ->
               case ds1 of {
                 Left x -> x;
                 Right y -> fmap $dFunctor B y
               })
            (right' $dChoice_ar97 eta)

Cases: 14 Tried: 8 Errors: 0 Failures: 2
Cases: 14 Tried: 9 Errors: 0 Failures: 2
Cases: 14 Tried: 10 Errors: 0 Failures: 2
Cases: 14 Tried: 11 Errors: 0 Failures: 2

Failure in: 11

test/Util.hs:10
test/Spec.hs:230:7: sum1PrismManualChar === sum1TypePrismChar failed:
LHS:
sum1PrismManualChar
sum1PrismManualChar
= \ @ p @ f $dChoice_arda $dApplicative_ardb eta ->
let {
$dFunctor
$dFunctor = $p1Applicative $dApplicative_ardb } in
dimap
($p1Choice $dChoice_arda)
(\ x ->
case x of wild_X4B {
A i -> Right i;
B ds_duYQ -> Left (pure $dApplicative_ardb wild_X4B);
C ds_duYR -> Left (pure $dApplicative_ardb wild_X4B);
D ds_duYS -> Left (pure $dApplicative_ardb wild_X4B)
})
(\ ds1 ->
case ds1 of {
Left x -> x;
Right y -> fmap $dFunctor A y
})
(right' $dChoice_arda eta)

RHS:
    sum1TypePrismChar
    sum1TypePrismChar
      = \ @ p @ f $dChoice_ar8o $dApplicative_ar8p eta_B1 ->
          let {
            $dFunctor
            $dFunctor = $p1Applicative $dApplicative_ar8p } in
          dimap
            ($p1Choice $dChoice_ar8o)
            (\ x ->
               join {
                 $j_sy49
                 $j_sy49 x1
                   = Left
                       (pure
                          $dApplicative_ar8p
                          (case x1 of {
                             Left x1 ->
                               case x1 of {
                                 L1 ds_dv3r ->
                                   case ds_dv3r of {
                                     L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                     R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                                   };
                                 R1 ds_dv3y ->
                                   case ds_dv3y of {
                                     L1 ds_dv3z -> C (ds_dv3z `cast` <Co:51>);
                                     R1 ds_dv3C -> D (ds_dv3C `cast` <Co:51>)
                                   }
                               };
                             Right y ->
                               case y of {
                                 L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                 R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                               }
                           })) } in
               case x of {
                 A g1 -> Right g1;
                 B g1 -> jump $j_sy49 (Right (R1 (g1 `cast` <Co:54>)));
                 C g1 -> jump $j_sy49 (Left (R1 (L1 (g1 `cast` <Co:54>))));
                 D g1 -> jump $j_sy49 (Left (R1 (R1 (g1 `cast` <Co:54>))))
               })
            (\ ds1 ->
               case ds1 of {
                 Left x -> x;
                 Right y -> fmap $dFunctor A y
               })
            (right' $dChoice_ar8o eta_B1)

Cases: 14 Tried: 12 Errors: 0 Failures: 3
Cases: 14 Tried: 13 Errors: 0 Failures: 3

Failure in: 13

test/Util.hs:10
test/Spec.hs:232:7: sum1PrismManual === sum1TypePrism failed:
LHS:
sum1PrismManual
sum1PrismManual
= \ @ p @ f $dChoice_ardO $dApplicative_ardP eta ->
let {
$dFunctor
$dFunctor = $p1Applicative $dApplicative_ardP } in
dimap
($p1Choice $dChoice_ardO)
(\ x ->
case x of wild_X4A {
__DEFAULT -> Left (pure $dApplicative_ardP wild_X4A);
B i -> Right i
})
(\ ds1 ->
case ds1 of {
Left x -> x;
Right y -> fmap $dFunctor B y
})
(right' $dChoice_ardO eta)

RHS:
    sum1TypePrism
    sum1TypePrism
      = \ @ p @ f $dChoice_ar8A $dApplicative_ar8B eta_B1 ->
          let {
            $dFunctor
            $dFunctor = $p1Applicative $dApplicative_ar8B } in
          dimap
            ($p1Choice $dChoice_ar8A)
            (\ x ->
               join {
                 $j_sy3M
                 $j_sy3M x1
                   = Left
                       (pure
                          $dApplicative_ar8B
                          (case x1 of {
                             Left x1 ->
                               case x1 of {
                                 L1 ds_dv3r ->
                                   case ds_dv3r of {
                                     L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                     R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                                   };
                                 R1 ds_dv3y ->
                                   case ds_dv3y of {
                                     L1 ds_dv3z -> C (ds_dv3z `cast` <Co:51>);
                                     R1 ds_dv3C -> D (ds_dv3C `cast` <Co:51>)
                                   }
                               };
                             Right y ->
                               case y of {
                                 L1 ds_dv3s -> A (ds_dv3s `cast` <Co:51>);
                                 R1 ds_dv3v -> B (ds_dv3v `cast` <Co:51>)
                               }
                           })) } in
               case x of {
                 A g1 -> jump $j_sy3M (Right (L1 (g1 `cast` <Co:54>)));
                 B g1 -> Right g1;
                 C g1 -> jump $j_sy3M (Left (R1 (L1 (g1 `cast` <Co:54>))));
                 D g1 -> jump $j_sy3M (Left (R1 (R1 (g1 `cast` <Co:54>))))
               })
            (\ ds1 ->
               case ds1 of {
                 Left x -> x;
                 Right y -> fmap $dFunctor B y
               })
            (right' $dChoice_ar8A eta_B1)

Cases: 14 Tried: 14 Errors: 0 Failures: 4

Performance guarantees?

At the IFL talk, @kcsongor mentioned that these generic lenses optimize down to what one would write by hand. That would be amazing, but I cannot reproduce it. Here is my example:

{-# LANGUAGE RankNTypes, DeriveGeneric, TypeApplications, DataKinds #-}
module GenericLens where

import GHC.Generics
import Data.Generics.Product

data Record = MkRecord { fieldA :: Int
                       , fieldB :: Bool
                       } deriving Generic

type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s

fieldALensManual :: Lens' Record Int
fieldALensManual f (MkRecord a b) = (\a -> MkRecord a b) <$> f a

fieldALensGeneric :: Lens' Record Int
fieldALensGeneric = field @"fieldA"

I compile this with

ghc-8.2 -O2 GenericLens.hs  -ddump-simpl

and I get somewhat nice Core for the manual lens:

fieldALensManual
  = \ (@ (f_a5HR :: * -> *))
      ($dFunctor_a5HT :: Functor f_a5HR)
      (f1_a1Th :: Int -> f_a5HR Int)
      (ds_d6PM :: Record) ->
      case ds_d6PM of { MkRecord a_a1Ti b_a1Tj ->
      fmap
        @ f_a5HR
        $dFunctor_a5HT
        @ Int
        @ Record
        (\ (a1_a1Tk :: Int) -> GenericLens.MkRecord a1_a1Tk b_a1Tj)
        (f1_a1Th a_a1Ti)
      }

but very large Core for the generic lens, which I will not even paste here. But there is a lot of stuff about Generics still around.

Under what conditions, if any, do the generic lenses optimize to the “real” thing?

Better support for associated data types

Currently, associated data types don't work quite right. It's possible to derive Generic, but when using the lens/prism, type unification often fails. Consider the following simple case:

class MyClass a where
  data AssocData a

instance MyClass Int where
  data AssocData Int = SomeData
    { val :: Int
    } deriving (Generic)

Then, getting the val field throws an error:

λ> getField @"val" (SomeData 3)
<interactive>:1:1: error:
    • Couldn't match type ‘generic-lens-0.5.1.0:Data.Generics.Internal.Families.Changing.ReplaceArgs
                             (AssocData Int)
                             (generic-lens-0.5.1.0:Data.Generics.Internal.Families.Changing.Unify
                                b'0 Int)’
                     with ‘AssocData Int’
        arising from a use of ‘getField’
      The type variable ‘b'0’ is ambiguous
    • In the expression: getField @"val" (SomeData 3)
      In an equation for ‘it’: it = getField @"val" (SomeData 3)

The error also shows up when accessing by position, but there is no error when accessing by type. Also, the error shows up when using the prism associated with SomeData, but the prism associated with Int works fine.

Type error and missing Generic instances

Hey Csongor! Here's a minimal example of an issue I had when using Data.Generics.Product.Types

data Record = Record {field1 :: Word32, field2 :: Int}
    deriving (Generic, Show)

x = over (types @Int) (+1) (Record 0 0)

Gives

error:
    • No instance for (Data.Generics.Product.Types.HasTypes'
                         (Data.Generics.Product.Types.Snd
                            (Data.Generics.Product.Types.InterestingOr
                               (Data.Generics.Product.Types.Interesting'
                                  (Rep Word32) Int '[Word32, Record])
                               (M1
                                  S
                                  ('MetaSel
                                     ('Just "field2")
                                     'NoSourceUnpackedness
                                     'NoSourceStrictness
                                     'DecidedLazy)
                                  (Rec0 Int))
                               Int))
                         Record
                         Int)
        arising from a use of ‘types’
    • In the first argument of ‘over’, namely ‘(types @Int)’
      In the expression: over (types @Int) (+ 1) (Record 0 0)
      In an equation for ‘x’: x = over (types @Int) (+ 1) (Record 0 0)

So I came across this type of error in a huge record structure and it only became clear after digging into the implementation of Interesting' that the problem is Word32 not having a Generic instance (this of course can happen with any type that doesn't have such instance), and so the type family does not reduce as Rep Word32 does not reduce. I can't use standalone deriving for Word32 as it is an opaque datatype. So for now I have used a Data SYB approach instead.

It would be nice if there was a way to check that the data and all the recursive fields are Generic and hiding the implementation details by avoiding the non-reducing type families.

Appreciating the library!

OverloadedRecordLabels

Hi, I think your library is really cool.

I did some hacking this morning and came up with this:
duog@f3b74c3

Which allows one to write #age to mean (HasField "age" a s) => Lens' s a, which I think is pretty slick. Would you be interested in adding something like this?

I would like it if this library could support type-changing lenses and prisms, and provide isomorphsims when possible. Do you think that's feasible?

Couldn't match "...Internal.Contains.Contains field (Rep t)" with "Just a"

Consider this:

getName :: HasField "name" t a => t -> a
getName = getField @"name"

On the latest Stack LTS, with the current master version of generic-lens, I get

    • Couldn't match type ‘generic-lens-0.2.0.0:Data.Generics.Record.Internal.Contains.Contains
                             "name" (Rep t)’
                     with ‘'Just a’
        arising from a use of ‘getField’

and the Internal modules are all hidden so I can't even add that constraint myself (suboptimal as it is).
I end up having to replicate the instance constraints for HasField in my code. In my use-case this is a default implementation for a class function, so it's not terrible, but I don't understand why resolution fails (i.e. why HasField does not imply the Contains constraint).

Version of HasType with functional dependency?

It seems that HasType does not have a functional dependency on the field type. So something like this results in a "Overlapping instances" compile error:

data StoreM k v m = StoreM
  { get    :: k -> m (Maybe v)
  , set    :: k -> v -> m ()
  , delete :: k -> m ()
  }

doSomething :: forall k v r m. (HasType (StoreM k v m) r, MonadReader r m)
            => m ()
doSomething = do
  storeM <- asks (getTyped @(StoreM k v m))
  return ()

When I use a custom Has typeclass with the functional dependency, the compile error goes away:

class Has a s | s -> a where retrieve :: s -> a

doSomething' :: forall k v r m. (Has (StoreM k v m) r, MonadReader r m) => m ()
doSomething' = do
  storeM <- asks (retrieve @(StoreM k v m))
  return ()

I would rather derive Has instances from Generic so it would be nice to have a version of HasType with the s -> a functional dependency, if it is possible.

`super` error message is quite bad

Prelude Data.Generics.Product GHC.Generics> data T = T { t :: Int } deriving Generic
Prelude Data.Generics.Product GHC.Generics> super @(Int, Char) @T
<interactive>:19:1: error:
    • Could not deduce (generic-lens-0.5.1.0:Data.Generics.Product.Internal.Subtype.GUpcast
                          (M1
                             D
                             ('MetaData "T" "Ghci2" "interactive" 'False)
                             (C1
                                ('MetaCons "T" 'PrefixI 'True)
                                (S1
                                   ('MetaSel
                                      ('Just "t")
                                      'NoSourceUnpackedness
                                      'NoSourceStrictness
                                      'DecidedLazy)
                                   (Rec0 Int))))
                          (M1
                             S
                             ('MetaSel
                                'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy)
                             (Rec0 Int)))
        arising from a use of ‘super’
      from the context: Functor f
        bound by the inferred type of
                 it :: Functor f => ((Int, Char) -> f (Int, Char)) -> T -> f T
        at <interactive>:19:1-21
    • In the expression: super @(Int, Char) @T
      In an equation for ‘it’: it = super @(Int, Char) @T

can't get it on nixos: test suite failed.

I `nix-haskell ghc843 generic-lens'
and get error.

Preprocessing test suite 'examples-doctests' for generic-lens-1.0.0.1..
Building test suite 'examples-doctests' for generic-lens-1.0.0.1..
[1 of 1] Compiling Main             ( examples/doctest.hs, dist/build/examples-doctests/examples-doctests-tmp/Main.o )
Linking dist/build/examples-doctests/examples-doctests ...
running tests
Running 6 test suites...
Test suite generic-lens-bifunctor: RUNNING...
Test suite generic-lens-bifunctor: PASS
Test suite logged to:
dist/test/generic-lens-1.0.0.1-generic-lens-bifunctor.log
Test suite generic-lens-examples: RUNNING...
Test suite generic-lens-examples: PASS
Test suite logged to: dist/test/generic-lens-1.0.0.1-generic-lens-examples.log
Test suite generic-lens-test: RUNNING...
Test suite generic-lens-test: PASS
Test suite logged to: dist/test/generic-lens-1.0.0.1-generic-lens-test.log
Test suite generic-lens-syb-tree: RUNNING...
Test suite generic-lens-syb-tree: PASS
Test suite logged to: dist/test/generic-lens-1.0.0.1-generic-lens-syb-tree.log
Test suite doctests: RUNNING...
Test suite doctests: PASS
Test suite logged to: dist/test/generic-lens-1.0.0.1-doctests.log
Test suite examples-doctests: RUNNING...

examples/Biscuits.hs:12:1: error:
    Could not find module ‘Data.Generics.Product’
    Use -v to see a list of the files searched for.
   |
12 | import Data.Generics.Product
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

examples/Biscuits.hs:13:1: error:
    Could not find module ‘Data.Generics.Sum’
    Perhaps you meant
      Data.Generics (from syb-0.7)
      Data.Generics.Text (from syb-0.7)
    Use -v to see a list of the files searched for.
   |
13 | import Data.Generics.Sum
   | ^^^^^^^^^^^^^^^^^^^^^^^^
Test suite examples-doctests: FAIL
Test suite logged to: dist/test/generic-lens-1.0.0.1-examples-doctests.log
5 of 6 test suites (5 of 6 test cases) passed.
builder for '/nix/store/cpzaq3n7yzfgji973axggynsf90bj07r-generic-lens-1.0.0.1.drv' failed with exit code 1
cannot build derivation '/nix/store/6cpl1n2mscn1iv4flmxbszmgs6n1qaf2-ghc-8.4.3-with-packages.drv': 1 dependencies couldn't be built
error: build of '/nix/store/6cpl1n2mscn1iv4flmxbszmgs6n1qaf2-ghc-8.4.3-with-packages.drv' failed

I git clone it and stack build.
It says ghc841 is missing.
My nixos-unstable doesn't have ghc841.

Then I changed resolver to resolver: nightly-2018-08-09 (It's ghc843)
and stack test worked.

Should the resolver be updated? Or it's just an nixos issue?

Identity lenses using `typed`

This code fails:

{-# LANGUAGE FlexibleContexts, DeriveGeneric, TypeApplications #-}

module GenLens where

import GHC.Generics
import Control.Lens
import Data.Generics.Product

data Foobar = Foobar Int Char String
    deriving Generic

foo :: HasType Foobar ctx => ctx -> Char
foo ctx = case view (typed @Foobar) ctx of
    Foobar _ c _ -> c

bar :: IO ()
bar = do
    print (foo (Foobar 3 'a' "Hello"))

with this error message:

/home/matt/Projects/playground/src/GenLens.hs:18:12: error:
    • The type Foobar does not contain a value of type Foobar
    • In the first argument of ‘print’, namely
        ‘(foo (Foobar 3 'a' "Hello"))’
      In a stmt of a 'do' block: print (foo (Foobar 3 'a' "Hello"))
      In the expression: do print (foo (Foobar 3 'a' "Hello"))
   |
18 |     print (foo (Foobar 3 'a' "Hello"))
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^

I would expect that it would work out to the identity lens: id :: Lens' a a

Product Completeness Constraint

I often find myself using subtype in the following simplified way

fn :: ( Subtype a x
      , Subtype b x
      )
   => x -> (a,b)

Which works fine, but is a little unsafe if i want to ensure that all info in x is captured in either a or b.

I was wondering if there is an existing way, or if it would be possible to extend the constraints offered for product types with the existing machinery to constrain the product of two (or more) supertypes to be equivalent to a single subtype?

Something like:

fn :: ( Subtype a x
      , Subtype b x
      , Product (a,b) x
      )
   => x -> (a,b)

Bug in `IsList` isomorphism?

When I try to use the IsList class I get a message similar to this:

    • Couldn't match type ‘Data.Functor.Const.Const
                             (List rl) (List rl)’
                     with ‘List bs0’
      Expected type: Control.Lens.Getter.Getting (List rl) rec (List rl)
        Actual type: (List rl -> List bs0)
                     -> rec -> Data.Functor.Const.Const (List rl) rec
    • In the second argument of ‘(^.)’, namely ‘list’
      In the first argument of ‘printRecord'’, namely ‘(rec ^. list)’
      In the expression: printRecord' (rec ^. list)

I also tried running the commented-out example in the Data.Generics.Product.List module, and I got the same message. Is it a bug?

Product-type downcast (record extension)

Since we have an isomorphism between HLists and records, if we expose the List type we can easily implement a downcast function to extend records with an HList. However, since the isomorphism is based on position, we can only append fields at the end. Ideally we would like to convert from HLists to records by only looking at the names of the field, but this would require a substantial modification of the GIsList instances.

Do you think it is possible and worth the additional complexity? Record extension seems a very useful addition to me.

Clean up error messages

Some of the recent changes that make type inference more clever also made the error messages worse.

Should `projectSub` have type `sup -> Maybe sub`?

Is there a reason why the type of projectSub :: sup -> Either sup sub?
I could be missing something, but wouldn't the left part of the final value always be the input sup?
Using a Maybe will also be more consistent with AsType projectTyped :: s -> Maybe a

Should HasType a a typecheck?

λ> newtype K = K { getK :: Int } deriving (Generic)
λ> getTyped :: K -> K

<interactive>:82:1: error:
    • The type K does not contain a value of type K
    • In the expression: getTyped :: K -> K
      In an equation for ‘it’: it = getTyped :: K -> K

Considering K is a K would an instance work something like so?

import Control.Lens (iso)

instance {-# OVERLAPS #-} HasType a a where
  typed = iso id id
  getTyped = id
  setTyped = const 

I don't have any experience around overlapping instances or solving related issues, but I think this seems pretty clean.

Is it possible to support "monadic" field/typed records?

Merry Xmas! I hope this is the correct place to ask questions about generic-lens, but when you have time after the holidays, would you mind answering if generic-lens supports "monadic" fields?

Given the definition of Foo below

data Foo m s = Foo
  { foo1 :: m s
  , foo2 :: [s]
  } deriving G.Generic

The following compiles ok

modifyFoo1 :: Foo Identity Int -> Foo Identity Int
modifyFoo1 x = x & field @"foo1" .~ (pure 1)

But the following has the below compile error (I have also tried with NoMonomorphismRestriction)

modifyFoo2 :: Applicative m => Foo m Int -> Foo m Int
modifyFoo2 x = x & field @"foo1" .~ (pure 1)

GHC 8.2.2 compile error

error:
    • Couldn't match type ‘generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.Infer'
                             (Foo
                                m
                                (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.P
                                   0 Int))
                             (m (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.P
                                   0 Int))
                             (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.PSub
                                (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.Unify
                                   (m (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.P
                                         0 Int))
                                   (m Int)))
                             (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.PickTv
                                (m (generic-lens-0.5.0.0:Data.Generics.Internal.Families.Changing.P
                                      0 Int))
                                (m Int))’
                     with ‘'(Foo m Int, m Int)’
        arising from a use of ‘field’
    • In the first argument of ‘(.~)’, namely ‘field @"foo1"’
      In the second argument of ‘(&)’, namely ‘field @"foo1" .~ (pure 1)’
      In the expression: x & field @"foo1" .~ (pure 1)
    • Relevant bindings include
        x :: Foo m Int
          (bound at src/Glazier/React/Prototypes/Listing/Internal.hs:94:12)
        modifyFoo2 :: Foo m Int -> Foo m Int
          (bound at src/Glazier/React/Prototypes/Listing/Internal.hs:94:1)
   |
94 | modifyFoo2 x = x & field @"foo1" .~ (pure 1)
   |                    ^^^^^^^^^^^^^

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.