Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optics #7

Open
JasonShin opened this issue Jun 23, 2019 · 2 comments
Open

Optics #7

JasonShin opened this issue Jun 23, 2019 · 2 comments
Labels
Description Add a description Example Add an example help wanted Extra attention is needed

Comments

@JasonShin
Copy link
Owner

No description provided.

@JasonShin JasonShin added Example Add an example help wanted Extra attention is needed Description Add a description labels Jun 23, 2019
@thomasmarsh
Copy link

Hi! I have a few comments and ideas on the optics implementation. I'm just learning rust, coming from the Haskell/Swift/Kotlin world, so apologies for not using rusty syntax. Further apologies if all this information is known to you.

A first thought is that your lens definition is not polymorphic. It is parameterized over just <S,A>, and not <S,T,A,B>. Is that intentional for simplicity?

Another general comment: you've chosen what I think are called "normal" lens structures (i.e., a pair of functions), which is how I think Monocle defines them. The other notable approaches are profunctor optics (type Optic p s t a b = p a b -> p s t) and Van Laarhoven optics (e.g., type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t). The primary difference has to do with the cardinality of manual compositions you have to define for all the different combinations of optics types.

Your lens structure getter is returning an Option<A>. However, this is not the definition of a lens. That would be more correctly called an affine traversal (or also called Option or Optional, though that naming obviously conflicts with the Option<A> type in Rust).

Sorry for the Swift, but here's a quick set of definitions as I understand these types:

// Iso: a lossless conversion between types
struct IsoP<S,T,A,B> {
    let to: (S) -> A
    let from: (B) -> T
}

// Non-polymorphic variant
typealias Iso<S,A> = IsoP<S,S,A,A>

// Lens: a getter/setter for a product type
struct LensP<S,T,A,B> {
    let get: (S) -> A
    let set: (S, B) -> T
}

// Non-polymorphic variant
typealias Lens<S,A> = LensP<S,S,A,A>

// Prism:​ a getter/setter for a sum type
struct PrismP<S,T,A,B> {
    let extract: (S) -> A?,      // Or, more properly "match: (S) -> Either<T,A>"
    let embed: (B) -> T
}

// Non-polymorphic variant
typealias Prism<S,A> = PrismP<S,S,A,A>

// Affine Traversal: a composition of a lens and a prism.
struct AffineP<S,T,A,B> {
    let tryGet: (S) -> A?,
    let set: (S, B) -> T
}

// Non-polymorphic variant
typealias Affine<S,A> = AffineP<S,S,A,A>

Using this approach, I find it easiest to define 1) how an optic composes with itself (e.g., Lens<S,T,A,B>+Lens<A,B,C,D>=Lens<S,T,C,D>), then 2) define how types can be promoted (e.g., an iso is trivially a prism and a lens, and a lens and a prism are trivially and affine traversal), then finally, 3) all the other compositions are trivial definitions (e.g., Lens<S,T,A,B>+Prism<A,B,C,D>=Affine<S,T,C,D> by way of something like func compose(lens, prism) -> Affine { compose(lens.asAffine(), prism.asAffine()) }).

Defining a fairly complete family of optics in this manner turns out not to be too complicated, with the only downside that you don't get composition via function composition; you have to provide your own composition operator/function. (Whereas profunctor lenses are simply functions that compose naturally.)

I'd be curious to hear more of your thoughts on the direction you want to go on optics within this library. If you are interested, I can also share a more complete example of the above swift code that includes all the composition implementations.

@kellpossible
Copy link

the readme mentions a composeL! macro, but does this actually exist or is it expected to implement this yourself?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Description Add a description Example Add an example help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants