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

Document object template of lenses #96

Closed
ivan-kleshnin opened this issue May 29, 2017 · 7 comments
Closed

Document object template of lenses #96

ivan-kleshnin opened this issue May 29, 2017 · 7 comments

Comments

@ivan-kleshnin
Copy link

ivan-kleshnin commented May 29, 2017

Docs mention "object template of lenses" few times but it's never documented.
By trial and error I figured out that currently it supports plain values and arrays but not nested values.

For example, I want to pick meta.file property chain from a given object.

let obj = {meta: {file: "./foo.txt", base: "foo", ext: "txt"}}
L.get(L.pick({file: ["meta", "file"]}), obj}) // {file: "./foo.txt"}

now I want to grab both file and ext but not base keeping the original nesting:

let obj = {meta: {file: "./foo.txt", base: "foo", ext: "txt"}}
L.get(L.pick({???}), obj}) // {meta: {file: "./foo.txt", ext: "txt"}}

I guess it's not possible with L.pick (because output is a product hence a plain collection), but not sure because it's unclear what "template of lenses" is.

P.S.

I'm aware of a solution:

R.pipe(
  L.set(["meta", "file"], L.get(["meta", "file"], obj)),
  L.set(["meta", "ext"], L.get(["meta", "ext"], obj))
)({})

I just wondered maybe I missed a shorter option.

@polytypic
Copy link
Member

polytypic commented May 29, 2017

Yes, the documentation could be clarified here. The template for L.pick cannot directly be a nested object. However, you can do nested things by nesting optics. For example (see in playground):

let obj = {meta: {file: "./foo.txt", base: "foo", ext: "txt"}}
L.get(L.pick({meta: ["meta", L.props("file", "ext")]}), obj)

Don't have time to think this through right now, but there might be ways to allow for some form of nested version of L.props to make this kind of usage nicer.

@polytypic
Copy link
Member

Here is a kind of nested cross of L.pick and L.props (try in playground):

let obj = {meta: {file: "./foo.txt", base: "foo", ext: "txt"}}

const nested = t => (I.isObject(t)
                     ? L.pick(L.modify(L.values, (v, k) => [k, nested(v)], t))
                     : t)

L.get(nested({meta: {file: [], ext: []}}), obj)

You can give it an arbitrary nested template of (plain) objects whose leaf props must be lenses.

Does this seem like what you'd want and what would be a good name for this? L.pickNested? L.pickIn?

@ivan-kleshnin
Copy link
Author

ivan-kleshnin commented May 29, 2017

Wow, recursive lensing :)

I'm getting

Error: partial.lenses: pick expects a plain Object template.

with the last Partial.Lenses version available. Unpublished update to L.pick?

@polytypic
Copy link
Member

polytypic commented May 29, 2017

Hmm... Note that the isObject predicate from the infestines library basically tests that the constructor of the given value is Object. IOW, it doesn't just test that the given value inherits from Object.

@polytypic
Copy link
Member

polytypic commented May 29, 2017

BTW, thanks for opening this issue! I have to think about generalizing L.branch and L.pick (to accept nested templates) and possibly adding the new L.pickIn (aka nested above).

Here is a generalized (nested) branch combinator (try in playground):

const branch = t => I.isObject(t) ? L.branch(L.modify(L.values, branch, t)) : t

L.modify(branch({x: {a: []}, y: {b: []}}),
         R.negate,
         {x: {a: 1, b: 2}, y: {a: 3, b: 4}})

Here is a generalized (nested) pick combinator (try in playground):

const pick = t => I.isObject(t) ? L.pick(L.modify(L.values, pick, t)) : t

const data = {s: 1, t: 1}
const lens = pick({x: {a: "s"}, y: {b: "t"}})

R.identity({
  get: L.get(lens, data),
  set: L.set(lens, {x: {a: 1}, y: {b: 2}}, data)
})

Here is also the (newly proposed) generalized pickIn combinator (try in playground):

let obj = {meta: {file: "./foo.txt", base: "foo", ext: "txt"}}

const pickIn = t => (I.isObject(t)
                     ? L.pick(L.modify(L.values, (v, k) => [k, pickIn(v)], t))
                     : t)

L.get(pickIn({meta: {file: [], ext: []}}), obj)

BTW, I believe it is also time to seriously think about obsoleting L.augment (this has been on my mind for some time already).

polytypic added a commit that referenced this issue May 31, 2017
polytypic added a commit that referenced this issue Jun 4, 2017
polytypic added a commit that referenced this issue Jul 10, 2017
polytypic added a commit that referenced this issue Jul 11, 2017
polytypic added a commit that referenced this issue Jul 12, 2017
polytypic added a commit that referenced this issue Jul 12, 2017
@ivan-kleshnin
Copy link
Author

Hi, Vesa. Sorry for not participating – mad months at work :(
pickIn looks like a reasonable solution.

@polytypic
Copy link
Member

No problem. I was also just on a tightly scheduled project and finally got some time & energy to finish the initial pickIn implementation. Thanks for opening the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants