Open ekmett opened 8 years ago
Actually, it looks like the indexed machinery may be a pain point. It requires a bunch of extra dependencies due to conjoined
. So a logical subset would be Set{1..3,6..7}.
Unfortunately that means that there'd STILL be 3 packages in the end if we split out the indexed stuff, and the classes for TraverseWithIndex, etc. would still not be in such a smaller package =(
I agree that {1..3,6-7} could be quite a nice and compact subset. I think if you're reaching for the indexed stuff, then a dependency on lens is fine
No need to necessarily have a huge interface. How about exporting just the basic functions for defining and using lenses / prisms / isos / traversals / getters / folds. Then, this package can also have the dual purpose of being a more beginner friendly lens package.
@mgsloan If a combinator we provide today can be provided without incurring an extra set of dependencies I'd rather include it rather than try to figure out some half-assed difficulty rubrik to test everything on. Including it gives a hard and fast rule and could proceed apace without involving a committee for every line of code. Trying to pick out some magic "suggested" set just runs into style variations between users and gives no objective right answer.
I'll admit I'm somewhat uncomfortable about the fact that it wouldn't be able to easily supply the Indexed
machinery, since that means that such a 'core' package wouldn't be able to be used for packages like wreq
that take advantage of that power today.
It'd add additional pressure on the wrong side between doing the right thing and doing the thing with fewer imports. Right now once you opt into lens you might as well provide indices, etc. if they make sense.
Let's see what the extra dependencies are for Set 4.
class
( Choice p, Corepresentable p, Comonad (Corep p), Traversable (Corep p)
, Strong p, Representable p, Monad (Rep p), MonadFix (Rep p), Distributive (Rep p)
, Costrong p, ArrowLoop p, ArrowApply p, ArrowChoice p, Closed p
) => Conjoined p where
is the major source of difficulty.
Comonad
and Distributive
only really incur an extra comonad
and distributive
dependency.
I'd assumed that there was a Representable
dependency from adjunctions on the representation of the profunctor as well.
comonad
comes in transitively through bifunctors
so that isn't an extra cost.
But then bifunctors
already brings in template-haskell
so sets 6..7 aren't an additional dependency cost over set 3.
And profunctors
already depends on distributive
.
So there is no cost to including set 4 from conjoined
.
Not bad at all! Seem to me {1..4,6-7} is a good subset indeed.
If a combinator we provide today can be provided without incurring an extra set of dependencies I'd rather include it rather than try to figure out some half-assed difficulty rubrik to test everything on.
True. I think the main appeal of lens-core
would be to export prisms / isos from packages. The dual purpose of being more beginner friendly is.
So how about "Is it frequently used to define a member of the menagerie?" to determine inclusion? Then, if someone wants to make a easy-lens
package with minimal combinators, they are free to base it on this core package.
I still think I'd rather stick to "affects imports" as a test.
A lot of things like partsOf
seem like they are arbitrary right up until you realize you can't live without them and sure as hell can't reimplement it on the spot!
So let's turn to downsides:
at
, ix
, and contains
are pretty damn common in idiomatic lens
code. As are lenses that just happen to compose those things in. I'm not sure I want to endorse a worse vocabulary.lens
package, making stuff a lot more difficult to maintain.The use case I had for this request was much more minimal than what's being discussed right now. My issue is that libraries I maintain (examples: http-client, warp) often need to expose some way of configuring a value. There are three basic ways I can do that:
host request
and request { host = "foo" }
getHost request
and setHost "foo" request
request ^. host
and (host ~. "foo") request
(or, my preferred, set host "foo" request
Record syntax, as we all know, doesn't compose nicely, and is frankly an eye sore. Generated haddocks for these fields can be confusing too if you don't export data constructors. Providing getters and setters fixes some of this, but (1) doubles the API, and (2) still doesn't compose nicely. So I'd like to use lenses.
The issue I'm having: if I switch warp and http-client to a set of lenses right now, I'd end up with a few unsatisfactory results:
So my request is really minimal: a new package with just the functions view
, set
, and over
, with their operator versions. AFAICT, these can be provided with only depending on mtl
(and even that's probably not needed, I think just transformers
would do it). If this happened, I could import this package from all of my packages without being lynched, export them from my packages, non-users of lens would get no conflicts, and users of lens would be happier because more packages in the ecosystem support lens.
The only casualties I see in this scenario are:
All of the other bits of functionality that could come along are nice, but referencing the lynch bit again, even adding in distributive
would probably be a deal-breaker for my use case.
Said another way: I'm hoping by having a standard, minimal-depedency package that everyone agrees on for this basic functionality, we can get the general Haskell ecosystem to more widely embrace lenses as a replacement for record syntax.
Just to give you the heads-up as early as possible to avoid wasting more of your time: it looks like my use case in http-client is not going to be well served by lens after all (unless you see something we missed). Relevant discussion is at:
https://twitter.com/snoyberg/status/720522453945094144
I'd still say that providing a lens-core would be a good thing, and I'd consider exposing a lens-based API for other things in the future. But most likely this specific case will end up with a getter/setter API instead.
Getting Iso
and Prism
included would transitively include distributive
through profunctors
, and one of my most common requests has been a common core that allows them, too.
At this point in time, it sounds like the core that we'd accept factoring out would already be too big for you, which has been the issue with almost every request we've had along these lines.
Similarly Set{1..2} alone isn't enough to allow the template-haskell
for lens generation (despite transitively incurring a template-haskell
dependency!) which is one of the other most common requests.
Now that Data.Bifunctor
is in base
, you don't need template-haskell
for it anymore, which alleviates a lot of the pain involved. (Also, I checked, none of the typeclasses require Bifoldable
or Bitraversable
, which aren't in base
yet.)
This means lens-core can be {1-4} plus whatever parts of 8 are doable while just in base
.
Let's look at subsets of functionality provided by the lens package:
Set 1: Adds Lenses and Traversals. You don't need any dependencies. This could include the machinery to produce the lenses via TH.
Set 2 (contains Set 1): Adds Getters and Folds: You need to add a contravariant dependency.
Set 3 (contains Set 2): Adds Prisms and Isomorphisms: You need to incur a profunctors dependency. (And a bifunctors dependency for Reviews)
Set 4 (requires Set 2): Indexed lenses and traversals, which requires profunctors, so you might as well include indexed getters and folds.
Set 5 (includes Set 4) The ad hoc classes for things like At and Ix and Wrapped. This drags in all the dependencies. Orphans are not a valid solution. A lot of folks would like to be able to supply these instances but without a lens dependency, but that just doesn't fly.
Set 6: Template Haskell for generating lenses and traversals. However today these also generate isomorphisms in the right situations, so while this could be done badly with just a dependency on Set 1, this really incurs a dependency on Set 3 merging it with...
Set 7: Template Haskell for generating isomorphisms and prisms.
Set 8: The rest: There are a few smaller things for things like generics, uniplate, exceptions, etc. that only incur a couple of dependencies.
Set{1..4,6..7} could go into a package that only had a few small dependencies and if I had to pick a fault-line to cut at that would probably be the one, since including sets 1..3 means sets 6..7 are basically free for a template-haskell dependency and the end-user experience is much improved, since that is the stuff they are reaching for as often as not.
Set 4 on the other hand is THE reason why packages like
wreq
use lens not another lens-like package. It increases the internal size of the package but not the dependency set, if set 3 is already involved.It should in theory be possible to cleave off a lens-core that included Set{1..4,6..7} and most of the current combinators, in a way that drastically reduced the dependencies of the package. Unfortunately, this leaves a massive interface between lens and such a lens-core package, and would require a lot of engineering work to do.
Is it worth it?