Today I took a stab at implementing Optics via Kliesli Arrows here. The work was based on this gist by @serras. Ultimately, I think I've achieved a majority of the API of the experimental optics here without implementing separated ADTs for Iso, Lens, Prism, Optional, and Traversal.
Crossing the Optic barrier between any of the individual optics (ie. Lens => Prism), causes one to need to switch which combinators one is using to compose Optics. Generally, this leads one to having a large import list and remembering which Optic to start with. Following is an example:
import * as L from "monocle-ts/Lens";
import * as O from "monocle-ts/Optional";
import * as T from "monocle-ts/Traversal";
import * as A from "fp-ts/Array";
import { pipe } from "fp-ts/function";
type Friends = {
people?: readonly {
name: string;
pets?: readonly {
name: string;
nickname?: string;
toys: readonly string[];
}[];
}[];
}
const toysOptic = pipe(
L.id<Friends>(),
L.nullableProp("people"),
O.traverse(A.Traversable),
T.prop("pets"),
T.fromNullable,
T.traverse(A.Traversable),
T.prop("toys"),
T.traverse(A.traversable),
);
declare const friends: Friends;
const toys = pipe(friends, T.getAll(toysOptic)); // readonly string[]
Desired Behavior
I'd like to not think about whether I'm in a Lens or an Optional or a Prism or a Traversable. ie.
Obviously, I think the work I've already done here is the best way to start.
I've also looked at tagging the existing experimental Optics and building a generic compose here but that path very quickly hits limitations in TS type checking.
Who does this impact? Who is this for?
I think generic Optics are much easier to teach to beginners as there are fewer concepts to learn. One can quite literally forget about the implementation entirely and focus (heh) on using the library.
Describe alternatives you've considered
N/A They are described in Suggested Solution.
Additional context
I happened on Kliesli optics while implementing ploy profunctor optics. I am not great at Category Theory or Functional Programming yet, but I noticed that PureScript prefers profunctor optics but also prefers Profunctor Strong and Category compose over the equivelent formulation using Arrows. Even my implementation ended up effectively using the Identity, Option, and Array monads instead of implementing arrows. However, I think there might be some fertile ground building >=> in TS that I haven't had the time to look at.
🚀 Feature request
Today I took a stab at implementing Optics via Kliesli Arrows here. The work was based on this gist by @serras. Ultimately, I think I've achieved a majority of the API of the experimental optics here without implementing separated ADTs for Iso, Lens, Prism, Optional, and Traversal.
EDIT: I've implemented them more fully here.
Current Behavior
Crossing the Optic barrier between any of the individual optics (ie. Lens => Prism), causes one to need to switch which combinators one is using to compose Optics. Generally, this leads one to having a large import list and remembering which Optic to start with. Following is an example:
Desired Behavior
I'd like to not think about whether I'm in a Lens or an Optional or a Prism or a Traversable. ie.
Suggested Solution
Obviously, I think the work I've already done here is the best way to start.
I've also looked at tagging the existing experimental Optics and building a generic compose here but that path very quickly hits limitations in TS type checking.
Who does this impact? Who is this for?
I think generic Optics are much easier to teach to beginners as there are fewer concepts to learn. One can quite literally forget about the implementation entirely and focus (heh) on using the library.
Describe alternatives you've considered
N/A They are described in Suggested Solution.
Additional context
I happened on Kliesli optics while implementing ploy profunctor optics. I am not great at Category Theory or Functional Programming yet, but I noticed that PureScript prefers profunctor optics but also prefers Profunctor Strong and Category compose over the equivelent formulation using Arrows. Even my implementation ended up effectively using the Identity, Option, and Array monads instead of implementing arrows. However, I think there might be some fertile ground building >=> in TS that I haven't had the time to look at.