Closed ekmett closed 8 years ago
:+1:
Also, @ekmett, welcome to the PureScript community. :smile:
Very interesting. I lack the sufficient lens-fu to comment meaningfully on this yet, but it seems conceptually simpler, which I like. Regarding indexed traversals, are there any possible solutions on the horizon? I ask because optic-ui
would probably make use of them, if they existed in purescript-lens
.
Also, I'm not sure if you've seen purescript-optic
, but that has more of the Traversal
machinery, as well as Iso
and a few other pieces.
On the other hand, optic-ui
would almost certainly benefit from the pure profunctor approach, since its UI
is almost certainly a Profunctor
, so it would get various combinators "for free" this way.
And to echo @jdegoes, welcome!
The problem from the standpoint of indexed traversals is that when I use p a (f b) -> s -> f t
as an indexed optic, then when I compose them with (.)
then unification forces p to be (->)
the outer traversal so you only get the inner traversal's index.
On the other hand, here we only have one parameter p
, not two like the p
and f
so I can't use unification to force things to behave that way and typeclasses aren't smart enough to figure out what to do.
The nice thing about this form of Iso
is that iso
is just dimap
.
I put this together as a starting point, but as the gif indicates, I really could use some help.
https://github.com/paf31/purescript-profunctor-lenses/tree/master
In particular, I'm not sure if the Traversal
approach is sound.
Very cool idea.
My only concern is, what does this change for the end user? If someone wants to define a lens, do they have to depend on profunctor
now in order to do that? Or can they still define their lenses without an explicit dependency?
Also, the indexed stuff isn't really a concern with this idea since we can't implement those ideas well enough as it stands now. At least, not using type classes. The Representable
family falls apart after a while, so we'd have to think up Indexable
from a different approach anyway.
Perhaps @zrho's Wander
representation in profunctor-lenses
is a way to get indexed traversals?
profunctor-lenses
is now up-to-date with lens
and optics
thanks to @zrho's latest PR. We might want to consider whether we want to merge back to lens
. I'm totally fine maintaining profunctor-lenses
as a separate repo if you'd rather not though.
For me, the application to optic-ui
is enough reason to prefer profunctor-lenses
for the particular use case where you have a Profunctor
already and want composition operators "for free", but I think both have their use cases (purescript-lens
is very good as a lightweight lens implementation, for example, with optics
if a user needs the heavier machinery).
You can't really get indexed traversals without introducing without some notion of type equalities. You want (->) work work as an instance of the indexed abstraction for any index, but Indexed i
to only work for i
, so even MPTCS + FDs isn't enough.
Ah, this is great :+1:
@ekmett I'm not sure I fully understand the need for type equalities, but would something like this help?
https://github.com/paf31/purescript-leibniz/blob/master/docs/Data/Leibniz.md
Sadly it doesn't help.
What sort of type equalities are needed?
In the normal encoding of lens
I use something like:
class Profunctor p => Indexable i p where
indexed:: p a b -> i -> a -> b
instance Indexable i (->) where
indexed = const
newtype Indexed i a b = Indexed { runIndexed :: i -> a -> b }
instance (i ~ j) => Indexable i (Indexed j) where
indexed = runIndexed
Notice how once we know we're interested in the Indexed case that we gain information that i is j, but that the (->) case works with all choices of i.
Now you can use this inside of an optic.
type IndexedTraversal i s t a b = forall p f. (Indexable i p, Applicative f) => p a (f b) -> s -> f t
The issue is we need to gain information about type equality as we walk up to the superclass in the Indexed
case.
Closing since https://github.com/purescript-contrib/purescript-profunctor-lenses exists.
In the
lens
library in Haskell we're encumbered by trying to interoperate withbase
and with a historical codebase. One sign of that is our representation for lenses iswhich is the "van Laarhoven" representation, so that it can compose directly with the function
traverse
and folks can define lenses without incurring a dependency on the package at all.On the other hand, a formulation of lenses based on just profunctors:
has a large number of theoretical advantages.
Notably:
from
combinator that reverses anIso
can reverse aPrism
, and you can then justget
from it as normal rather than having to havere
as a special case.from
also becomes capable of "turning a lens around" in a meaningful way. With the addition ofCostrong
andCochoice
classes you can reverse them again and get back aPrism
correctly rather than a mere view.Strong
andChoice
collapse into one class, but you need more scaffolding to support it all.Downsides:
Traversals
, one is to sayp
is a representable profunctor represented by a applicative functor. This is sufficient to be a legal encoding but isn't terribly satisfying.I figured I'd mention it here because you don't have my legacy support issues, or quite the same number of dependencies that cause folks to view
lens
as a painful import to make the same trade-offs I made necessarily make sense for you.