Giter VIP home page Giter VIP logo

lens's Issues

Add some kind of lens based zipper?

I've been considering adding a zipper module for some time. I hacked up @jberryman's zippo library to use lens.

Issues:

Where to put it?

If it lived in Control.Lens.Zipper it would be odd if it wasn't exported from Control.Lens, (since I do re-export Control.Lens.* for everything except Control.Lens.Internal, but if it was exported from Control.Lens it would take a lot of useful names that I've deliberately avoided touching because they are popular in end user code.

Build classes for IndexedFunctor, IndexedFoldable and IndexedTraversable.

It would be nice to reduce the name clutter for the common 'traverse the polymorphic argument' case.

Problem:

For Traversable the traverse is a valid traversal, but if itraverse is a valid indexed traversal, then its not usable as the function that traverses the container with the index directly.

Can we implement a faster version of alongside?

We switched to a final encoding of Bazaar because it made cloning dramatically faster. Would a final encoding of Context have a similar performance impact? e.g.

newtype Store c d a = Store { runStore :: forall f. Functor f => (c -> f d) -> f a }

This might also speed up a number of Control.Lens.Plated combinators.

Add elements and elementsOf? (or something similar)

It is possible to traverse an indexed container using a predicate on the index.

elements :: TraversableWithIndex i t => (i -> Bool) -> IndexedTraversal i (t a) (t b) a b
elementsOf :: (i -> Bool) -> InxexedTraversal i a b c d -> IndexedTraversal i a b c d

Type operator constructors for Simple

I'm sure that this has been pondered before, but I'd like to open a discussion about it. Simple lenses / traversals / isos are pretty common, so what if they had nice type operators?

These seem good:

type a :-> b = Simple Lens a b
type a :*> b = Simple Traversal a b
type a :<-> b = Simple Iso a b
type a :..> b = Fold a b

I was considering (:%>) for Lens, but decided on (:->) for nicer appearance and consistency with fclabels.

Not sure if I'd need these:

type a :^> b = Simple Getter b
type a :<~ b = Simple Setter b

And these'd be a little silly (but would still save parens!):

type (:@->) a b i = SimpleIndexedLens i a b
-- etc

I dunno about you, but I like these smileys - each variety is just a different nose!

Add more cases to Magnify?

When it was limited to folds we had a few more instances. It would be nice to handle more cases gracefully.

class At?

Now that we have unordered-containers is it worth it to revisit the decision not to have a class for At?

Add (^%)

A tighter associativity version of % that can be interleaved with (^.).

Factored into a separate issue -- this was previously an offshoot of issue #17.

Parameterized makeClassy

Here is a possible approach for implementing makeClassy for parameterized containers. It works without any type-specific logic, as long as all the types are functional dependencies of the container type. This is something I sorely need in my code...

Sample code:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}

module Sample where

import Control.Lens hiding (Simple)

-- Unparameterized classy containers are easy (yey!)

data Trivial = Trivial { _trivialField :: Int }

makeClassy ''Trivial

trivialAdd :: HasTrivial container => Int -> container -> Int
trivialAdd int container = int + container ^. trivialField

data TrivialContainer = TrivialContainer { _trivialContainer :: Trivial }

makeLenses ''TrivialContainer

instance HasTrivial TrivialContainer where
  trivial = trivialContainer

trivialExample :: Int -> TrivialContainer -> Int
trivialExample = trivialAdd

-- Parameterized containers seem possible:

data Simple num = Simple { _simpleField :: num }

makeLensesFor [ ("_simpleField", "__simpleField") ] ''Simple

-- Functional dependency to be compatible with the general case below.
class HasSimple container num | container -> num where
  simple :: SimpleLens container (Simple num)

instance HasSimple (Simple num) num where
  simple = id

simpleField :: forall container num . HasSimple container num => SimpleLens container num
simpleField = simple . __simpleField

simpleAdd :: forall container num . (HasSimple container num, Num num) => num -> container -> num
simpleAdd num container = num + container ^. simpleField

data SimpleContainer = SimpleContainer { _simpleContainer :: Simple Int }

makeLenses ''SimpleContainer

instance HasSimple SimpleContainer Int where
  simple = simpleContainer

simpleExample :: Int -> SimpleContainer -> Int
simpleExample = simpleAdd

-- This even works for multiple types which become "phantom" for some fields,
-- as long as all types are functional dependencies of the container:

data Complex foo bar = Complex { _complexFoo :: foo, _complexBar :: bar }

makeLensesFor [ ("_complexFoo", "__complexFoo"), ("_complexBar", "__complexBar") ] ''Complex

-- The functional dependencies here are required for this approach to work.
class HasComplex container foo bar | container -> foo, container -> bar where
  complex :: SimpleLens container (Complex foo bar)

instance HasComplex (Complex foo bar) foo bar where
  complex = id

complexFoo :: forall container foo bar . HasComplex container foo bar => SimpleLens container foo
complexFoo = complex . __complexFoo

complexBar :: forall container foo bar . HasComplex container foo bar => SimpleLens container bar
complexBar = complex . __complexBar

complexAddFoo :: forall container foo bar . (HasComplex container foo bar, Num foo) => foo -> container -> foo
complexAddFoo foo container =
  foo + container ^. complexFoo

complexAddBar :: forall container foo bar . (HasComplex container foo bar, Num bar) => bar -> container -> bar
complexAddBar bar container =
  bar + container ^. complexBar

data ComplexContainer = ComplexContainer { _complexContainer :: Complex Int Int }

makeLenses ''ComplexContainer

instance HasComplex ComplexContainer Int Int where
  complex = complexContainer

complexExample :: Int -> ComplexContainer -> Int
complexExample int container = complexAddFoo 1 container + complexAddBar 1 container

Dodek reported a makeClassy example issue on 7.4.1

Using makeClassy on:

data Foo = Foo { _fooX, _fooY :: Int }

in GHC 7.4.1, reportedly yields:

Main.hs:14:1:
    Illegal type variable name: `'
    When splicing a TH declaration:
      class HasFoo t_0
    where foo :: 
          fooX :: forall . 
          fooX = foo GHC.Base.. go_1
                   where go_1 _f_2 (Main.Foo __fooX'_3
                                             __fooY_4) = (\__fooX_5 -> Main.Foo __fooX_5 __fooY_4) Data.Functor.<$> _f_2 __fooX'_3
          fooY :: forall . 
          fooY = foo GHC.Base.. go_6
                   where go_6 _f_7 (Main.Foo __fooX_8
                                             __fooY'_9) = (\__fooY_10 -> Main.Foo __fooX_8 __fooY_10) Data.Functor.<$> _f_7 __fooY'_9

TODO: Confirm this behavior and fix it. =)

Problem with generalized Getting

When we re-generalized Getting we introduced a problem.

If you go to use (^.) on a traversal with constraints on each argument, like, say, dynamic then you now have to use an explicit type signature, because otherwise the b and d variables in Traversal a b c d are totally unconstrained.

We need to come up with a work around.

TH-generated lenses on sum types throw `error`

...as you're aware ;-) But I didn't see the issue of partial lenses addressed in the docs or tutorial, so I thought opening a ticket would be best, if only as a place to address the issue once.

I want to build additional abstractions on top of lenses, but I don't find the current behavior of lens w/r/t sum types to be acceptable. Here are my questions:

  1. Is there a way to support "partial lenses" with maybe a less general constraint, like Applicative or something (sorry, I'm still getting oriented to this lens flavor)
  2. If so are there any plans to do this here, and if not, why not?

I'm aware that the usual "lens laws" don't really permit lenses on types with multiple constructors, but that seems more of an argument for coming up with a better lens-like thing or better laws, rather than sort of ignoring the issue.

Monadic traversals of state?

Capturing the relevant discussion on monadic traversals from #36.

@orenbenkiki commented an hour ago
As for the monadic modification application - intuitively, one would expect
that the order would be (1) getting the original value (2) invoking the
modification function to do whatever changes it wants and (3) taking the
final result and setting the value to it. In fact, I am hard pressed to
think of any other possible order. I agree things might get muddier if the
operator/function modifies several lensed locations inside the container;
here one could argue either for "modify one location at a time", so the
modification function of the 2nd location would see the updated 1st
location, or a more transactional "modify everything at once", where all
the modification functions see the original values. I think the
one-at-a-time approach is more consistent with the way the state monad
works in general, so I'd go with that; I also suspect that this would be
the "natural" semantics that would fall out of any reasonable code, but I
of course I didn't delve into the relevant source files.

I'm not certain what would be the ideal notation for a monadic %= / %%=
modification function. Perhaps |%= and |%%= (mnemonic: pipe the state
through the modification)?

I could whip up some patch code for |%=, |%%=, withValue and withModified
if you'd like... I'll fork the GIT repository and do all this the proper
way (which I should have done from the start, sorry about that).

Thanks again,

Oren Ben-Kiki

@orenbenkiki commented an hour ago
I think I understand the concern but it seems to me this applies only to
operations which work on multiple locations - indexed ones, at that.

That is, I am hard pressed to think of a case where using the simple
single-location '%=' and '%%=' with a monadic modification function would
cause the issues you describe, even when applied to a list (e.g., "list .
element 2 |%= monadicChangeElement").

Perhaps I am missing something...?

Oren.

ekmett commented an hour ago
Consider _last, and someone coming along and appending to the list in the meantime.
Or even _head when someone comes in and inserts something in the meantime, so the thing you based your updated head on is now shifted downstream.

ekmett commented an hour ago
These issues manifest themselves in other languages too. Many languages like c++ have iterators, but inserting into the container while iterating may or may not be legal, and leads to all sorts of confusing semantics and places where elements may be missed in the traversal.

orenbenkiki commented 32 minutes ago
Ouch. You are right, doing "list . _last |%= functionThatAppendsToList" would have ill-defined semantics. It is a pity to give up this very useful ability because of this edge case... but if we'd rather be safe than sorry, this kills "|%=" and "|%%=". If we'd rather maximize usability, I'm not so sure (people could be warned off this edge case). I guess leaving it out for now would be safest, this can always be re-visited in the future if there is sufficient demand for it.

ekmett commented just now
Going to close out the issue and leave it as a record of the discussion.

Add (|>) = flip ($)

This will let us pipeline a lot of the makeLensesFor, etc. code more cleanly.

Generalize the signature of Getting and IndexedGetting

As it is, when you are given a lens/traversal that you plan to use to change the type of a field, it is hard to use any getting or fold combinators on it.

I think the best compromise is to keep Getter and Fold in their two argument form and add the other two args back to Getting and IndexedGetting.

Contractions for lens types

This is pretty unimportant, but it seems like it'd be good to have a conventional contraction for traversal. The main purpose of these is to provide suffixes for function names.

If we arrange the core type names by length:

Iso
Lens
Fold
Action
Getter
Setter
Traversal
Projection
MonadicFold

Towards the end, they become very unwieldy suffixes. However, thankfully, contractions are pretty straightforward:

Traversal   --> Trav
Projection  --> Proj
MonadicFold --> MFold

However, "Trav" seems weird. "Walk" came to mind, but unless that becomes convention, I'll stick to "Trav". Thoughts?

Allow indexing of a traversable and/or foldable by ordinal position.

-- allow them to index by ordinal position
indexed :: Traversable a b c d -> IndexedTraversable Int a b c d
indexed :: Foldable a b c d -> IndexedFoldable Int a b c d

This would let us 'reindex' another indexedtraversal by position in a result list as well, because every indexedtraversal is a traversal.

Can we come up with a model for embedding/projection pairs?

Embedding/projection pairs would be nice to have an analogue to:

data Embedding a b = Embedding (a -> b) (b -> Maybe a)

data Projection a b = Projection (a -> Maybe b) (b -> a)

with the ability to flip back and forth, and treat them like a a getter (or fold) and/or traversal/lens in the right situations?

Benchmark alongside

I suppose the first step in knowing if an alternative implementation is warranted for #43 is to actually do some benchmarking.

[00:15] edwardk: Context is slow
[00:16] edwardk: maybe my fast Context comonad?
[00:16] edwardk: newtype Store c d a = Store { runStore :: forall f. Functor f => (c -> f d) -> f a }
[00:16] edwardk: i could probably use that
[00:17] edwardk: that should give back the speed lost if i come up with the right implementation
[00:17] edwardk: i'll add an issue to explore it
[00:19] mgsloan: what if it was "alongside l r = lens (\(a, a') -> (view l a, view r a')) (\(a, a') (b, b') -> (set l b a, set r b' a'))" orso
[00:20] edwardk: viewing and setting separately is probably not a win
[00:20] mgsloan: for big products it might be
[00:21] edwardk: but if you wanted to concoct an alongside benchmark i'd be happy to listen to it, and try my final Store on it as well
[00:21] mgsloan: especially if there's not much depth to l / r
[00:21] edwardk: feel like adding another benchmark to the benchmarks folder and benchmark suites?
[00:21] mgsloan: the final Store sounds good :) not sure I get it
[00:22] mgsloan: sure, could do that

lens-core for lens providers

Presently, libraries that want to provide lenses to users can do so without incurring a dependency on lens. This is a very neat property, but could be a little bit obtuse documentation-wise.

A small amount of discussion here: diagrams/diagrams-lib#49 (comment)

Also, as soon as libraries want to provide anything more than lenses and traversals (which are definitely the most common), they definitely need to add a dependency. This could encourage library writers to use lenses instead of the more specific iso, or traversal instead of the more specific projection.

Anyway, I really don't feel very strongly about it - just putting down thoughts. Diagrams is fine with adding a lens dependency, and I don't personally anticipate needing lens-core.

$ instead of %?

I think it would make sense to use $ instead of % where the mnemonic is for applying a function, given that ($) is the function application operator, e.g. ($=) instead of (%=), ($) instead of (%), and so on (Control.Lens.Setter, Type, IndexedLens, and IndexedSetter seem to be affected). Is there a rationale for % besides it being an otherwise unused symbol?

If you agree I can prepare a patch.

(I'm guessing operators with more than one % like (%%=) and (%%) should also become ($$=) and ($$) in that case, rather than keeping one of the %s?)

"at" example doesn't work

Trying the example from the source comments:

$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :m + Control.Lens Data.Map.Lens Data.Map
Prelude Control.Lens Data.Map.Lens Data.Map> fromList [("hello",12)] ^. at "hello"

<interactive>:3:28:
    No instance for (At [Char] (Map [Char]))
      arising from a use of `at'
    Possible fix:
      add an instance declaration for (At [Char] (Map [Char]))
    In the second argument of `(^.)', namely `at "hello"'
    In the expression: fromList [("hello", 12)] ^. at "hello"
    In an equation for `it':
        it = fromList [("hello", 12)] ^. at "hello"
Prelude Control.Lens Data.Map.Lens Data.Map>
Leaving GHCi.
$ cabal list --installed --simple-output | grep lens
lens 2.6.1

BTW, what is the equivalent of "at" for accessing the n-th element of a list? I am sorely missing a "cheat sheet" that lists the common cases of using lenses, instead of hunting them across all the source files... In fact I was creating an HUnit test file that does nothing but list all these common cases, when I run into the above.

doc fix for fromWithin

15:08] edwardk: @messages
[15:08] lambdabot: nandsaid 1h 41m 20s ago: in the docs for ‘fromWithin’ you have fromWithin l = fromMaybe . within l; surely you mean fromJust? [15:08] edwardk: @tell nand yes
[15:08] lambdabot: Consider it noted.

Add `<<~`

We can't chain assignment with <~.

Replacer?

Setters are capable of replacing the target value(s) because they are capable of modifying the target value(s). Modification always allows replacement as a special case (via const and its ilk), but you cannot generate modification from solely replacement (if you have both replacement and retrieval, you can, but then you just end up with a Traversal).

This seems to hint at a supertype of Setter: Replacer (plus Indexed and/or Simple variants, of course). Essentially:

  • Getter a c is isomorphic to a -> c
  • Setter a b c d is isomorphic to (c -> d) -> a -> b
  • Replacer a b d is isomorphic to d -> a -> b

I've come up with a couple of encodings for Replacer that try to fit within the van Laarhoven model, and I'd like to generate some discussion of this. It's not critical to me, but it is an interesting hole in the current design space.

  • type Replacer a b d = forall f. (Settable f) => (forall c. c -> f d) -> a -> f b
  • type Replacer a b d = forall f. (Settable f) => (Void -> f d) -> a -> f b

droppingWhile doesn't satisfy its stated equivalence

While the documentation in Control.Lens.Fold states that

dropWhile p ≡ toListOf (droppingWhile p folded)

we see that

Prelude Control.Lens> toListOf (droppingWhile (<=3) folded) [1,6,1]
[6]
Prelude Control.Lens> dropWhile (<=3) [1,6,1]
[6,1]

lens-2.6.1 doesn't build on ghc 7.2.1

src/Control/Lens/WithIndex.hs:316:1:
    The multi-parameter class `TraversableWithIndex' cannot have generic methods
    In the class declaration for `TraversableWithIndex'

Feature request: Lenses generation without type signatures

In the following code snippet it would be convenient to generate lenses without explicit type signatures for the purpose of documentation:

data A = A { _a  Int, _b  Int }

makeLensesNoTypeSignatures ''A

So we can have nice haddocks:

-- | Lens 'a'
a  Lens A A Int Int

-- | Lens 'b'
b  Lens A A Int Int

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.