ramda / ramda-lens

:ram: :mag_right: Lens library built on ramda
183 stars 15 forks source link

Traversed doesn't traverse #13

Closed DrBoolean closed 8 years ago

DrBoolean commented 8 years ago

Just realized... https://github.com/ramda/ramda-lens/commit/ce6ea28fb31dd8e29d5f0d81557cd2495aef6144#diff-c1129c8b045390789fa8ff62f2c6b4a9L85

const Task = require('data.task')
const {List} = require('immutable-ext')
const {over, traversed} = require('ramda-lens')
const R = require("ramda")

const x = List.of(1)
const r1 = x.traverse(Task.of, x => Task.of(x + 1))
const r2 = over(traversed, y => Task.of(y + 1), x)

console.log(r1) // Task List(2)
console.log(r2) // List Task(2)

Should match if i understand traversals correctly. However, it might be the case that I don't...

scott-christopher commented 8 years ago

The idea of traversed is that it allows a lens-like traversal of any Traversable type, targeting each value contained within. It achieves this by using the applicative instances of Const and Identity as a wrapper for the type contained within the Traversable for use with foldMapOf and over.

So we're effectively making the most of the convenience that traverse has a signature that fits what we expect to provide as the optic for functions like over and foldMapOf.

type Lens      s t a b = Functor     f => (a -> f b) -> s -> f t 
type Traversal s t a b = Applicative f => (a -> f b) -> s -> f t

R.traverse :: Applicative f, Traversable t => (a -> f a) -> (a -> f b) -> t a -> f (t b)
-- a.k.a
R.traverse :: Applicative f, Traversable t => (a -> f a) -> Traversal (t a) (t b) a b

So it's expected output could be something like:

const everyX = compose(traversed, lensProp('x'));
over(everyX, R.inc, List.of({ x: 1 })) //=> List.of({ x: 2 })
DrBoolean commented 8 years ago

Interesting... So what's the difference between the mapped lens and traversed?

DrBoolean commented 8 years ago

I ask because

const everyX = compose(mapped, lensProp('x'));
over(everyX, R.inc, List.of({ x: 1 })) //=> List.of({ x: 2 })

is equivalent here, yes?

scott-christopher commented 8 years ago

For over, yep. But mapped is just a Setter, so it can't be used to extract values for foldMapOf.

DrBoolean commented 8 years ago

Ahh, thanks @scott-christopher - I had traversed all wrong

DrBoolean commented 8 years ago

One last question then...is there a way to do what i'm looking to do above? I want to "traverse" the Task lens style.

scott-christopher commented 8 years ago

Interestingly, you can just give the function you'd normally give to traverse to the lens because traverseOf == identity.

Unfortunately, because we don't have the type system available to give us the of function necessary for the Traversable we need to piggyback off of our previous "workaround" for traversed by using the this context of the lens to provide the function for creating our Applicative.

So something like this should work (... I think)

const traverseOf = (of, lens, f, target) => lens.call({ of }, f)(target);

const thing = List.of({ x: 1 });
const everyX = compose(traversed, lensProp('x'));

traverseOf(Task.of, everyX, y => Task.of(y + 1), thing); //=> Task.of(List.of({ x: 2 }))
DrBoolean commented 8 years ago

I'll give it a shot. Thanks a million!

Just starting to make heavy use of this (finally). I kind of want to use your profunctor version instead though :)