kcsongor / generic-lens Goto Github PK
View Code? Open in Web Editor NEWGenerically derive traversals, lenses, and prisms.
Generically derive traversals, lenses, and prisms.
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''
?
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.
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"
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
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.
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.)
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"
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?
When composing two field
or position
Lens
es 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
| ^^^^^^^^^^^
@ElvishJerricco suggested that we test that the lenses themselves get inlined here.
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?
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.
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’
See #49
Would it be possible to relax the upper bounds on criterion in the benchmark stanza? This will allow Stackage to build the benchmarks.
deepTyped @Bool :: Lens' ((Bool, Int), Int) Bool
typed
looks only one layertypes
gives traversalThis 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.
Where on the roadmap is the PHP backend?
Not being able to run this lib within PHP is really blocking my workflow.
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?
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)
| ^^^
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.
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!
Was trying to use the the list
instance and was getting strange error messages. Finally noticed it is a pure profunctor lens. This is a bit unexpected when the rest (e.g., the
, field
, etc.) are van Laarhoven lenses.
Is it possible to export the IsInteresting type family from Data.Generics.Product.Types? It is interesting, if you forgive me the pun :-) I could use it to see if a type contains another type.
Perhaps rename it to something more descriptive, like HasSubType.
λ 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')
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.
I'd be thrilled to use this but I fear for performance a bit. Are there any benchmarks you could publish?
Below is the full output from the Stackage build server:
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
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
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
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
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
Sometimes it can be quite annoying that the internals are not available
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?
I forgot to merge this.
2ecef62
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.
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!
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?
/cc @mpickering
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).
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.
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
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?
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
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)
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?
Hi. The example code doesn't build for me. Here's a repo which reproduces the issue and also the errors are shown in the README https://github.com/k-bx/generic-lens-example
Thank you!
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.
Some of the recent changes that make type inference more clever also made the error messages worse.
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
λ> 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.
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)
| ^^^^^^^^^^^^^
Could OverloadedLabels
offer a lighter weight syntax to this brilliant API?
It could be convenient at times to convert type-safe field names into term level strings and type safe positions into term level integers, possibly for display or serialization.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.