4 ms·
Excellent explanation! I think this is far better than my post at explaining why optics are so useful. > With profunctor optics, you can even construct self-de
by bradrn 6y ago
Excellent explanation! I think this is far better than my post at explaining why optics are so useful.
> With profunctor optics, you can even construct self-describing operations. For instance, imagine a JSON parser that can be directly interrogated to obtain the schema it accepts, or a middleware stack that can be interrogated to determine which headers it looks at, and which routes it accepts. Personally, this is the area I'm most excited about with functional optics.
I’m not sure this has anything to do with profunctor optics per se; you can do it with any structure which is polymorphic enough. (A neat example I saw recently: Build Systems à la Carte [0] allow you to get a list of dependencies from a build task, as long as that task is Applicative i.e. does not have dynamic dependencies.)
[0] https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf https://www.microsoft.com/en-us/research/uploads/prod/2018/0...
- Twisol 6y ago> Excellent explanation! I think this is far better than my post at explaining why optics are so useful. Thank you! > I'm not sure this has anything to do with profunctor optics per se Yes, fair! I didn't mean to imply that only profunctor constructions can be self-descriptive. Rather, I meant that optics based solely on the function arrow don't really give you anywhere to annotate with description in the first place. By assuming less about the operations they're lifting, profunctor optics give you more freedom to model a self-descriptive profunctorial operation. In the `(a -> f b) -> (s -> f t)` formulation, the lens takes a function and returns an adapted function. There's nowhere to put additional information, and even if you could stash it on the `f`, you'd have to feed it an `s` just to get at it. In the `p a b -> p s t` formulation, the function arrow (if it's used in your chosen `p` at all!) doesn't have to be the top-level type you return; you could have something like `(a -> f b, Metadata)` instead. You can add combinators to your concrete `p` that manage the metadata and let you combine multiple such operations, and intermix them freely with profunctor-generic lenses that don't care what concrete profunctor you're using. In the end, you get a `p a b` with the metadata immediately available and not necessarily hidden under a function application. This distinction only comes up because lenses are polymorphic in both the input and output types. The example of a JSON parser is only polymorphic in its output type (we can assume the input is always some String), so you can easily get a `Parser b` with all the metadata at the surface. Since Profunctors have two type parameters, one covariant and one contravariant, we can build models that vary over the input type while retaining the ability to be self-descriptive. I read a blog post recently [0] that showed some examples of using Profunctors in this way. It also shows the relationship between Profunctor and Arrow; the latter is basically a Strong Profunctor (you can turn a `p a b` into a `p (c, a) (c, b)` that threads a `c` through unaltered) where you can also compose Profunctor elements directly (`p a b -> p b c -> p a c`). In the end, you get an entire operation `p a z` in your Profunctor, with any statically observable metadata ready to be extracted from its structure. [0] https://elvishjerricco.github.io/2017/03/10/profunctors-arrows-and-static-analysis.html https://elvishjerricco.github.io/2017/03/10/profunctors-arro...