These are chat archives for ramda/ramda

26th
Oct 2016
fenduru
@fenduru
Oct 26 2016 14:42
Hey all. I'm writing some code and can't help to think that I'm doing something wrong
I'm trying to create a function of type string -> obj -> obj
Denis Stoyanov
@xgrommx
Oct 26 2016 14:44
obj -> obj??? id?)
fenduru
@fenduru
Oct 26 2016 14:46
What I have currently is
R.compose(
  R.over(R.lensProp('propertyToChange')),
  R.curryN(2, R.compose)(R.map(operation)),
  createMapperFunction, // string -> obj -> obj
)
The whole curryNf eels weird
@xgrommx huh?
Denis Stoyanov
@xgrommx
Oct 26 2016 14:49
@fenduru maybe just R.map(operation) all operators in Ramda curried.
fenduru
@fenduru
Oct 26 2016 14:49
but then it will be the same as R.map(operation, createMapperFunction)
which is incorrect, since createMapperFunction is a function that returns data, not the data itself
Denis Stoyanov
@xgrommx
Oct 26 2016 14:50
R.flip(R.map)(operation) try it
fenduru
@fenduru
Oct 26 2016 14:51
that doesn't change anything, that is still R.map(fn, fn) not R.map(fn, data)
What I want longhand:
Denis Stoyanov
@xgrommx
Oct 26 2016 14:53
@fenduru two function and one data? can u write it in lambda?
fenduru
@fenduru
Oct 26 2016 14:53
someString => R.over(R.lensProp('propertyToChange'), R.compose(R.map(operation), createMapperFunction(someString)))
Denis Stoyanov
@xgrommx
Oct 26 2016 14:54
R.over(R.lensProp('propertyToChange'), R.useWith(R.compose, [R.map, createMapperFunction])(operation, someString))
fenduru
@fenduru
Oct 26 2016 14:57
I'll still need to put that inside a lamda someString => R.over(...
I just thought I was doing something wrong since this is the first time I'm needing to do something without just composing R functions
Denis Stoyanov
@xgrommx
Oct 26 2016 14:57
no, try it R.over(R.lensProp('propertyToChange'), R.useWith(R.compose, [R.map, createMapperFunction])
so, you can also R.over(R.lensProp('propertyToChange'), R.converge(R.compose, [R.always(R.map(action)), createMapperFunction])
it will be to use only someString implicitly
fenduru
@fenduru
Oct 26 2016 14:59
Hmm, yeah let me try that
fenduru
@fenduru
Oct 26 2016 15:05
So I'm trying basically that, just simplified and getting an error
R.converge(R.compose, [R.always(R.add(3)), R.multiply])(5)(3)
Denis Stoyanov
@xgrommx
Oct 26 2016 15:07
@fenduru yes, because always ignore only one item
fenduru
@fenduru
Oct 26 2016 16:10
@xgrommx I think the simplest way would be if R.compose supported R.__
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 16:11
@fenduru
S.compose is binary and curried, so should work for what you want. The issue with ramda's is that it is variadic, so it can't be curried effectively
Of course, then you have another library involved.
fenduru
@fenduru
Oct 26 2016 16:13
Yeah. I feel like all of the methods that aren't curried due to being variadic should support R.__ and/or have a correspondnig R.composeN function
Denis Stoyanov
@xgrommx
Oct 26 2016 16:16
fn1(10).map(fn2).map(fn3) because function also Functor
fenduru
@fenduru
Oct 26 2016 16:19
doh
R.compose(
  R.map(R.add(3)),
  R.multiply
)
thank you @xgrommx
fenduru
@fenduru
Oct 26 2016 18:09
Yeah this works out beautifully. I was struggling to see this because in my actual code I end up having nested maps R.map(R.map(...)). The outer one maps over a function () -> List, and the inner one maps over the list
Jason Lengstorf
@jlengstorf
Oct 26 2016 19:33

Hey, folks! Quick question about lenses and practical application. I feel like I'm overcomplicating things, so I'm looking for a real-world use-case. The getter makes sense, but I'm having trouble coming up with practical uses for the setter; it seems like everything would either be so general purpose as to not matter (e.g. assoc), or so specific that you'd need multiple lenses for it to be usable (e.g. https://goo.gl/AvaktE).

Am I overcomplicating this and/or missing the point? Does anyone have real examples I could check out to see how this is being used in practice?

Thanks!

fenduru
@fenduru
Oct 26 2016 19:56
false
black bear
fenduru
@fenduru
Oct 26 2016 19:59
I'm using R.lensPath right now to deeply set a property
Ryan Zeigler
@rzeigler
Oct 26 2016 19:59
@jlengstorf you odn't need to define a full path in a lens
fenduru
@fenduru
Oct 26 2016 20:00
Is there a way to simplify R.converge(f, [g, R.identity])?
Ryan Zeigler
@rzeigler
Oct 26 2016 20:00
in particular, I would avoid define your own lens directly, lensProp and lensIndex should cover your needs
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:02
@rzeigler In my silly example, for sure. What about when the getter is actually useful?
Ryan Zeigler
@rzeigler
Oct 26 2016 20:02
what scenario in javascript are you envisioning in which the getter does more than retrieve either by name or by index
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:02
For example, I'm using a lens to simplify an API response.
Ryan Zeigler
@rzeigler
Oct 26 2016 20:02
sure you can try doing prism/iso fanciness lenses like haskell does
but I don't really understand that and its not necessary to derive useful behaviors
fenduru
@fenduru
Oct 26 2016 20:03
Right now I'm doing R.converge(R.assoc('valid'), [validate, R.identity]) but I just want to make sure I'm not overcomplicating
Ryan Zeigler
@rzeigler
Oct 26 2016 20:03
@fenduru compose(objOf('valid), validate))
oh nm, i misunderstood your problem
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:04
Prism/iso is over my head, I think.
Ryan Zeigler
@rzeigler
Oct 26 2016 20:04
its over my head too
i know that some of the relevant bits come when wanting to do something to all elements of a collection rather than a specific one
or encapsulating parts of a path that might not be there
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:05
Is there a way to create a view lens without the setter?
fenduru
@fenduru
Oct 26 2016 20:05
@rzeigler The object already exists, trying to take { a: 1, b: 2 } and return {valid: true, a:1, b:2}
unfortunately evolve and applySpec are both reallly close, but not quite what I want
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:08

It just feels like overkill to use something like this:

const myLens = lens(
  image => ({
    url: path([ 'images', 'medium', 'src' ])(image),
    alt: prop('caption')(image),
  }),
  () => {},
);
view(myLens, { ... });

So maybe my original question about getters/setters could be better-worded as: is lens overkill for what I'm trying to do in this example?

Ryan Zeigler
@rzeigler
Oct 26 2016 20:10
you seem to be misusing lens, it should refer to an isolated point in the structure
and be composable
so, what you would actually want to do is
const images = lensProp('images');
const medium = lensProp('medium');
const src = lensProp('src');
const url = compose(images, medium, src)

view(url, imageStruct)
the compositionality is where you get the wins
because if you reuse the sub structure elsewhere you can use the same lens
and mix and match
the example that I like to give (which is super contrived) when explaining to people is that both people and businesses have phone numbers which have area codes
so, lets say i have a {areaCode: '555', number: '5555555'}
i can define areaCodeLens = lensProp('areaCode')
later, I can define business primary phone area code as compose(lensProp('main'), areaCodeLens)
and a persons' cell phone area code as compose(lensProp('cell'), areaCodeLens)
because the phone substructure is the same in both places it becomes reusable
if you are specifying a full deep path to an location in 1 lens, I think you are probably doing it suboptimally
Jason Lengstorf
@jlengstorf
Oct 26 2016 20:19
@rzeigler thanks so much! I'll try to rework how I'm doing this and make it less odd. Appreciate you taking the time to explain this!
David Boyer
@mangr3n
Oct 26 2016 20:59
a lens turns the get/set operation into a point free function
yes it can do pathing, but it's the composition and point free that make it interesting I believe.
Jason Lengstorf
@jlengstorf
Oct 26 2016 21:41

Okay, so I'm playing with this, but I'm not sure composing lensProp can actually solve my use case without getting really convoluted.

The use case is:

  1. Receive a request from an API that's large, convoluted, and complex
  2. Create a simplified object with only the required data
  3. Feed that simplified object to a templating function

Without using lens, I'm not sure how I can get all of the data I need into the templating function without making it not composable.

This is the code I'm currently working with. The createImage function needs a single argument to be composable/mappable, so I'm not sure how using lensProp won't lead me to ultimately creating an object out of composed lensProp calls, which seems like a much more convoluted way of doing exactly what I'm already doing.

Am I way off base here, or is this a sane way of solving this problem?

const imageLens = lens(
  image => ({
    src: path(['images', 'low_resolution', 'url'], image),
    caption: path(['caption', 'text'], image),
    user: path(['user', 'username'], image),
    link: prop('link', image),
  }),
  () => {}
);

const getImageData = view(imageLens);

const handlePhotos = compose(map(getImageData), getPhotos);

const createImage = image => {

  // Bemmit makes BEM class names less unwieldy.
  const getClass = bemmit('instagram-feed');
  const figureClass = getClass();
  const linkClass = getClass('link');
  const imageClass = getClass('image');
  const captionClass = getClass('caption');

  return `
    <figure class="${figureClass}">
      <a href="${image.link}" class="${linkClass}">
        <img src="${image.src}" alt="Photo by ${image.user}"
             class="${imageClass}" />
      </a>
      <figcaption class="${captionClass}">${image.caption}</figcaption>
    </figure>
  `;
};

const getImageMarkupArray = map(createImage);
const combineImageMarkup = reduce(concat, '');
const generateMarkup = compose(combineImageMarkup, getImageMarkupArray);
Jason Lengstorf
@jlengstorf
Oct 26 2016 22:28
I just realized that the example I found that led me to this solution is from @buzzdecafe. The API for lens has changed since then, so this is an updated version of that example: https://goo.gl/Hpks1l.
fenduru
@fenduru
Oct 26 2016 22:35
Seems reasonable to me. You have a large object, and you care about a few pieces of information.
Although I'm not sure why imageLens is a lens
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 22:37
Yeah, why not just use the getter function directly?
const getImageData = image => ({
    src: path(['images', 'low_resolution', 'url'], image),
    caption: path(['caption', 'text'], image),
    user: path(['user', 'username'], image),
    link: prop('link', image),
  })
fenduru
@fenduru
Oct 26 2016 22:38
const srcLens = R.lensPath(['images', 'low_resolution', 'url']);
const captionLens = R.lensPath(['caption', 'text']);
// the rest

const extractImage = R.applySpec({
  src: R.view(srcLens),
  caption: R.view(captionLens)
})
Jason Lengstorf
@jlengstorf
Oct 26 2016 22:38
@fenduru @Bradcomp Fuck. You're right — this could absolutely drop lens. I've been so focused on doing this "the Ramda way" that I felt like I needed to use a Ramda function there.
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 22:38
lol... TBH I rarely use lenses
generally if I am going to use them it will only be if I need over.
fenduru
@fenduru
Oct 26 2016 22:39
I think lenses would be more useful if there was a fantasyland spec, and I could implement it for custom datatypes that don't do direct property access
Jason Lengstorf
@jlengstorf
Oct 26 2016 22:39
It felt like overkill — why I started making noise in here in the first place — so I'm glad to see I was right to think I was overcomplicating things.
fenduru
@fenduru
Oct 26 2016 22:40
@jlengstorf definitely check out R.applySpec though - I think it is one of my favorite Ramda functions
Jason Lengstorf
@jlengstorf
Oct 26 2016 22:40
Noted on over, @Bradcomp. I noticed that as a pretty handy solution if I come up against that need.
@fenduru I like applySpec — I'm looking into that now.
Thanks, everyone!
fenduru
@fenduru
Oct 26 2016 22:41
@Bradcomp I had asked a bit earlier but it was busy in here - do you happen to know of a nicer way of doing R.converge(f, [g, R.identity])
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 22:41
I don't
fenduru
@fenduru
Oct 26 2016 22:41
I'm about to end up doing this a bunch, so if there is a better way I'd like to not shoot myself in the foot
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 22:41
I usually use it just like that ;)
Well, you can always write a helper function
fenduru
@fenduru
Oct 26 2016 22:42
Okay, but this isn't something foreign you've never seen someone do
That is true, just felt common enough that there might be an existing helper. calculate a value based on an object, mainpulate that object based on that value
Nate Abele
@nateabele
Oct 26 2016 22:43
Anybody know anything about FantasyLand support for ES6 types?
(i.e. Map, Set, etc.)
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 22:44
@fenduru Yeah, I do use that sometimes, but usually it's just a one off thing
fenduru
@fenduru
Oct 26 2016 22:50
now the hardest part... thinking of a name for that helper lol
James Forbes
@JAForbes
Oct 26 2016 23:04
@fenduru People wiser than me showed me this:
chain(g, f) = converge(f, [g, identity])
And ap(f, g) = converge(f, [identity, g])
Brad Compton (he/him)
@Bradcomp
Oct 26 2016 23:07
:scream:
Nate Abele
@nateabele
Oct 26 2016 23:09
@JAForbes Hey. So, I was actually thinking a separate library might be more effective.
Denis Stoyanov
@xgrommx
Oct 26 2016 23:09
@JAForbes applicative != monad
James Forbes
@JAForbes
Oct 26 2016 23:10
What are you envisioning @nateabele ?
Denis Stoyanov
@xgrommx
Oct 26 2016 23:10
and flip for arguments not rescue us
Nate Abele
@nateabele
Oct 26 2016 23:10
Well, if the library just patched prototypes, it'd mean those types work with anything that implements FantasyLand now/in the future.
(And presumably patching built-in prototypes is out of scope for a library like Ramda).
@JAForbes Does that make sense?
Denis Stoyanov
@xgrommx
Oct 26 2016 23:13
@JAForbes chain for function is chain = f => k => r -> k(f(r))(r)
James Forbes
@JAForbes
Oct 26 2016 23:14

Yeah I am personally of the belief a library shouldn't ever patch prototypes, buts it ok for a dev to do that in the context of their app, given the app doesn't need to share code with ads/etc webapps.

But I think Ramda should be able to map over sets/maps/weakmaps etc. In fact I'm surprised it doesn't already so maybe there is a good reason we don't have map for these data types. Can anyone in the know weigh in on that?

Denis Stoyanov
@xgrommx
Oct 26 2016 23:14
k is Kleisli's arrow
James Forbes
@JAForbes
Oct 26 2016 23:17

@xgrommx I'm not sure what you mean, I also don't pretend to understand the code I posted above. Here is my source though: https://gitter.im/ramda/ramda?at=578c60c5e4375c9212036bcc

And a demo: https://goo.gl/4p0YF0