These are chat archives for ramda/ramda

15th
Jul 2015
David Chambers
@davidchambers
Jul 15 2015 00:11
The Ramda community is wonderful. We’re very lucky!
Jethro Larson
@jethrolarson
Jul 15 2015 00:12
@davidchambers myLens(obj) == R.view(myLens, obj)that makes me think that R.view is the same as apply. What do I have wrong?
David Chambers
@davidchambers
Jul 15 2015 00:15
The nice thing about the style of lens Ramda now uses is that a lens is a function, which is why they can be composed via R.compose and R.pipe. A lens is not intended to be invoked directly, but it doesn’t surprise me that it’s possible to do so to avoid R.view.
Here’s the implementation of R.view:
module.exports = (function() {
  var Const = function(x) {
    return {value: x, map: function() { return this; }};
  };

  return _curry2(function view(lens, x) {
    return lens(Const)(x).value;
  });
}());
The first argument, lens, is clearly a function as we apply it to Const. This returns function(x) { return {value: x, map: function() { return this; }}; }.
The second argument, x, is then sent into this function, and pops out the other end unchanged.
Jethro Larson
@jethrolarson
Jul 15 2015 00:20
How can so little code confuse me so much
:D
beautiful impl, btw
David Chambers
@davidchambers
Jul 15 2015 00:21
I highly recommend watching https://vimeo.com/104807358, if you haven’t already.
Jethro Larson
@jethrolarson
Jul 15 2015 00:22
I have but I keep loosing my grasp.
David Chambers
@davidchambers
Jul 15 2015 00:22
Brian does a nice job of explaining how it’s possible to define both a getter and a setter from one function with the help of Const and Identity.
The Ramda implementation is essentially the same as the one in Brian’s video.
(I’ve watched the video three or four times, in addition to spending hours implementing these functions, and I still don’t feel confident explaining this stuff, so don’t beat yourself up!)
Jethro Larson
@jethrolarson
Jul 15 2015 00:54
I have
R.assoc('x', fn(obj), obj)
obv I could use converge+identity but I have a feeling there's something else
David Chambers
@davidchambers
Jul 15 2015 00:55
If you had R.assoc('x', fn(obj.x), obj) you’d have R.over!
So close!
Jethro Larson
@jethrolarson
Jul 15 2015 00:55
yeah I considered that, but no the new prop combines other properties to calculate it
David Chambers
@davidchambers
Jul 15 2015 00:56
I don’t see anything better than what you have now, sorry.
Jethro Larson
@jethrolarson
Jul 15 2015 00:56
I never feel right using converge or useWith
They feel kind of bare metal
if you get me
David Chambers
@davidchambers
Jul 15 2015 00:57
Yeah, I know what you mean.
Jethro Larson
@jethrolarson
Jul 15 2015 00:58
ha you can converge lenses now
Right?
David Chambers
@davidchambers
Jul 15 2015 00:59
Ah… yeah?
Scott Sauyet
@CrossEye
Jul 15 2015 00:59
My feeling about them is that on occasion points-free trumps other concerns, and they end up more readable than lambdas. But that's fairly unusual.
David Chambers
@davidchambers
Jul 15 2015 00:59
(mind bending painfully)
Jethro Larson
@jethrolarson
Jul 15 2015 01:01
so you could use converge and 3 lenses to combine 2 properties into a third
David Chambers
@davidchambers
Jul 15 2015 01:01
Clever!
Scott Sauyet
@CrossEye
Jul 15 2015 01:02
... and scary! :smile:
Jethro Larson
@jethrolarson
Jul 15 2015 01:02
wait, still need some kind of map or something to get a R.add or something in there
hmm
food for experimentation
David Chambers
@davidchambers
Jul 15 2015 01:04
Do share if you get something working.
Jethro Larson
@jethrolarson
Jul 15 2015 01:22
http://bit.ly/1CFCa30
It's kind of ridiculous and the result isn't a lens. I'm gonna keep messing with it
Jack Jennings
@jackjennings
Jul 15 2015 01:25
The lens stuff doesn't seem to be in the 0.16 docs. Intentional?
David Chambers
@davidchambers
Jul 15 2015 01:27
@jackjennings, that’s not intentional. I plan to investigate this evening.
Jethro Larson
@jethrolarson
Jul 15 2015 01:30
btw the try ramda editor should really use the uncompressed version of ramda
David Chambers
@davidchambers
Jul 15 2015 01:31
Absolutely!
Mind opening an issue on the ramda.github.io repo?
Jethro Larson
@jethrolarson
Jul 15 2015 01:34
done
David Chambers
@davidchambers
Jul 15 2015 01:35
Thanks!
Jethro Larson
@jethrolarson
Jul 15 2015 01:54
This is where I'm stopping for now. Maybe sleep will bring something interesting http://bit.ly/1CFELd9
David Chambers
@davidchambers
Jul 15 2015 01:55
You’re in interesting territory. I’m very interested in solving this problem as well.
Ludwig Magnusson
@TheLudd
Jul 15 2015 12:07

Exploring transducers here...
Is is possible to do both mapping and filtering in one transducer function? Example

var users = [
  { age: 21, isAdmin: false },
  { age: 42, isAdmin: true },
  { age: 35, isAdmin: true }
];

I want to sum the ages of all admins. I can ignore all non admins in the reduction by supplying the transducer function R.filter(R.propEq('isAdmin', true)) and I can get the ages with R.map(R.prop('age')).

But is it possible to get the sum 77 with one call to transduce looking something like this: R.transduce(???, R.add, 0, users)

Hardy Jones
@joneshf
Jul 15 2015 12:18
I'd say yes in this specific case, but no in general
Ludwig Magnusson
@TheLudd
Jul 15 2015 12:21
@joneshf how in this specific case?
Hardy Jones
@joneshf
Jul 15 2015 12:22

Since you can have a monoid for your operation, you can supply a default value.
So you should be able to map just the one time, changing everything into this "monoid".

R.map(R.ifelse(R.prop('isAdmin'), R.prop('age'), R.always(0)))

Then you can transduce with that.

So i guess the general pattern here is:
map f . filter p == map (\x -> if p x then f x else mempty)
or free theorem I guess
free with restrictions?
I dunno
Hardy Jones
@joneshf
Jul 15 2015 12:29
from that you can infer the type of f
f :: Monoid b => (a -> b)
and in js that line would be:
R.compose(R.map(f), R.filter(p)) == R.map(R.ifElse(p, f, R.always(empty)))
Ludwig Magnusson
@TheLudd
Jul 15 2015 12:35
Ok, no that is not really what I was looking for. What I wanted to know was really if it was possible to do filter and map. This was just an example case.
Something similar is done in the example for transduce. First they take, then they do map. But take is a bit simpler than filter.
Hardy Jones
@joneshf
Jul 15 2015 12:40
Oh. You can implement take using filter though, so it should still have the same answer.
It boils down to having a "monoid" around. If you've got one, you can fuse, if not you can't.
But, isn't the whole point of transducers that you don't have to do this sort of manipulation?
Ludwig Magnusson
@TheLudd
Jul 15 2015 12:42
I don't know, I am very new to them =)
Hardy Jones
@joneshf
Jul 15 2015 12:43
Ah.
Yeah, I think that's the goal. You write the algorithm as normal, and under the covers it does the fusion.
Adrian Smith
@slifin
Jul 15 2015 13:27
I've asked this question on stackoverflow but I'm posting it here because this code is inspired by ramda and in case any one can help: http://stackoverflow.com/questions/31431513/php-advanced-functional-programming-creating-a-curry-method-similar-to-ramdajs
Kevin Wallace
@kedashoe
Jul 15 2015 15:56
@TheLudd I'm not 100% sure what you mean by "in one transducer function", but I think the answer to your question is yes, if you compose a map and filter and transduce it, you will not create any intermediate collections
I recommend into, it probably the nicest interface for using transducers with ramda
Ludwig Magnusson
@TheLudd
Jul 15 2015 15:59
@kedashoe By "one transducer function" I meant the first argument to transduce since it is referred to as "the transducer function" in the documentation.
If it is possible, please show me how.
Kevin Wallace
@kedashoe
Jul 15 2015 16:06
same thing as the previous link except using transduce, in which case we have to tell it explicitly how to build our new collection (R.flip(R.append))
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:15
Ok, I had composed the functions in the opposite order and that didn't work. Do you know why it matters?
Kevin Wallace
@kedashoe
Jul 15 2015 16:17
transducers run compositions backwards
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:18
why? :)
Kevin Wallace
@kedashoe
Jul 15 2015 16:18
I'm trying to find a good article haha
I can try to explain though :)
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:20
I have other questions about transducing more important to me if you are available and have knowledge
Kevin Wallace
@kedashoe
Jul 15 2015 16:20
i'll do my best, what's up?
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:21
I have watched the presentation by Rich Hickey https://www.youtube.com/watch?v=6mTbuzafcII
In the end he shows a table where he claims that if a type implements transduce, they get all the other stuff for free. (map, filter and so on)
I wonder what that would look like and if it is applicable to the types in ramda-fantasy for instance
Kevin Wallace
@kedashoe
Jul 15 2015 16:23
ok
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:23
Dunno if you have seen it =)
Kevin Wallace
@kedashoe
Jul 15 2015 16:23
i did watch it although only once and that was a while ago
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:24
But as I understand it, transducers were created to not be forced to implement map all over again for new types, like streams and so on
Kevin Wallace
@kedashoe
Jul 15 2015 16:24
do you have a specific ramda-fantasy type you'd like to look at?
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:24
Take Maybe
Kevin Wallace
@kedashoe
Jul 15 2015 16:27
so
i think there is a bit of confusing overloading going on with the transducer definition https://github.com/cognitect-labs/transducers-js#the-transducer-protocol
to me there are two distinct things going on which they use the same protocol to define
well, on that confusing note, i actually have to run for a bit, sorry
will be back in 30 minutes or so
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:32
np :)
Kevin Wallace
@kedashoe
Jul 15 2015 16:55
ok, sorry about that, let's start over
can you write a simple example like you did with summing the admin's ages that uses Maybe?
Ludwig Magnusson
@TheLudd
Jul 15 2015 16:57
Umm... I am actually not sure. I am a bit confused about all this and trying to learn. But let me explain what I'd expect from what I have studied so far
Lets take the Identity moand instead just to keep things simple
Currently, it has a map function defined like this:
Identity.prototype.map = function(f) {
  return new Identity(f(this.value));
};

So I can do

var i1 = Identity(1);
R.map(R.add(1), i1); // => Identity(2)

right?

Kevin Wallace
@kedashoe
Jul 15 2015 17:01
I actually had a conversation with @joneshf about just this! Let me see if I can link it..
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:01
From what I undersand, I should be able to remove the map function, implement some transducer stuff and still be able to map over it...
woops, conversation actually started a bit earlier than that
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:04
Oh, so the step function is just to wrap the value in the identity monad?
Yes I am looking at the implementation of Identity
Kevin Wallace
@kedashoe
Jul 15 2015 17:05
that is the best we could come up with
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:05
But it seems to make sense.
However I think you also need to have another transformer as input
I will do some research on this
Kevin Wallace
@kedashoe
Jul 15 2015 17:07
sounds good.. i'll write up something on why transducers compose backwards in the meantime :)
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:16
@kedashoe Also one thing I don't fully get is the sentance "Acts as a transducer if a transformer is given in list position." that is given for a lot of functions. What does it mean to say that map "acts as a transducer".
Kevin Wallace
@kedashoe
Jul 15 2015 17:20
short explanation, all those functions are wrapped in dispatch, so if you end up calling them with into or transduce, dispatch will be able to tell and will return the transformer version of map rather than the normal version
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:27
Yes. I know about the dispatching, I have read the source code. It was more of a conceptual question. What does it mean to "act as a transducer"? How do transducers act? :)
Kevin Wallace
@kedashoe
Jul 15 2015 17:35
haha ok well, that sounds like quite a philosophical question then. i would say it reduces over its input without creating any intermediate collections?
hmm, can you not produce output from multiple lines in the REPL?
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:36
Nope
brb
Ludwig Magnusson
@TheLudd
Jul 15 2015 17:49
Is there a signature to a transducer? Is it like the transduce function? Or the initialized version of it? Like reduce
Kevin Wallace
@kedashoe
Jul 15 2015 17:54
depends what you count as a signature :)
from the clojure docs
;; transducer signature
(whatever, input -> whatever) -> (whatever, input -> whatever)
rich hickey posts some thoughts in the comments section as well
Jethro Larson
@jethrolarson
Jul 15 2015 17:59
Was catching up on https://github.com/DrBoolean/mostly-adequate-guide as there's a couple new chapters since last I looked. Great stuff. Still the link to Chapter 10 404ing was like a cliffhanger on a tv show. Aw, come on!
Scott Sauyet
@CrossEye
Jul 15 2015 18:24
@slifin: I did see the question on SO, but am afraid I have no help to offer. Whatever PHP I once knew is long since forgotten.
Ludwig Magnusson
@TheLudd
Jul 15 2015 18:25
Here is my take so far on Identity. It does not work. I put the wrapping in result but could just as well be in step for all I know. The main problem is where to get the initial xf if I am supposed to be able to execute the line at the end

  function _Identity(val, xf) {
    this.val = val;
    this.xf = xf; 
  }

  var p = _Identity.prototype;

  p['@@transducer/init'] = function() {
    return this.xf['@@transducerinit']();
  };

  p['@@transducer/step'] = function(acc, v) {
    return this.xf['@@transducer/step'](acc, v); 
  };  

  p['@@transducer/result'] = function(result) {
    return this.xf['@@transducer/result'](_Identity(result));
  };  

  return R.curry(function(val, xf) {
    return new Identity(val, xf);
  }); 

}());
var inc = R.add(1);
R.map(inc, Identity(1))
Jethro Larson
@jethrolarson
Jul 15 2015 20:26
Defining R.path in terms of map over lensProp: http://bit.ly/1OerJF2
Not sure why it's R.apply(R.compose) instead of R.apply(R.pipe) but w/e
I'm guessing it's related to the reason transducers compose backwards
Jethro Larson
@jethrolarson
Jul 15 2015 20:42
http://bit.ly/1OeuxlB
I like this a little better.
Tim Navrotskyy
@dypsilon
Jul 15 2015 20:44
Hi everyone, I'm learning Ramda right now and often finding myself doing the following: var lazyFunction = R.partial(normalFunction, firstArgument); I'm doing this only to be able to use normalFunction lazily later, even if this function takes only one argument. Is there a better way to do this?
Jethro Larson
@jethrolarson
Jul 15 2015 20:45
normalFunction = R.curry(normalFunction)
will make the function "auto curried". so you can call it normalFunction(a, b) or normalFunction(a)(b) and it'll work the same
Tim Navrotskyy
@dypsilon
Jul 15 2015 20:46
@jethrolarson well, I want to be able to use normalFunction in a pipeline (pipe or compose), but it has only one argument.
so doing pipe(normalFunction(a), secondFunction); would execute normalFunction right away
Jethro Larson
@jethrolarson
Jul 15 2015 20:47
so normalFunction doesn't operate on the value piped?
Ludwig Magnusson
@TheLudd
Jul 15 2015 20:48
@dypsilon is this what you want?
var newFunction = pipe(normalFunction, secondFunction);
newFunction(a)
Tim Navrotskyy
@dypsilon
Jul 15 2015 20:48
that's why I do pipe(R.partial(normalFunction, firstArgument), secondFunction);
I don't pipe any value to the first function, but it produces a value which is piped into the secondFunction
Hm, I guess partial is right in this particular situation, but curry is better in others. @jethrolarson, @TheLudd, thank you for your input!
Tim Navrotskyy
@dypsilon
Jul 15 2015 20:56
By the way Ramda + Highland.js work like a charm together!
Jethro Larson
@jethrolarson
Jul 15 2015 21:03
Wouldn't @TheLudd 's code do what you want?
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:04
Not really, newFunction doesn't have "a", only normalFunction needs to know about "a".
newFunction is called without any arguments
Jethro Larson
@jethrolarson
Jul 15 2015 21:05
Ah. I think that's against the grain for pipe
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:06
What is normalFunction?
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:06
Really? It works pretty well, though.
Jethro Larson
@jethrolarson
Jul 15 2015 21:06
newFunction = function(){ return pipe(normalFunction, secondFunction)(a);};
that makes it clear that newFunction is a thunk
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:09
normalFunction takes another function which does side-effecty stuff (sends a HTTP request), so I extracted the function, but I don't want to extract it all the way up and it has nothing to do with the pipeline...
so it would normally look like this pipe(normalFunction(request), secondFunction);
but that's not possible because in this case normalFunction is invoked right away
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:10
and normalFunction executes the request?
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:11
exactly, inside the normal function I prepare the request headers and body and send the request
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:11
then use bind
normalFunction.bind(null, request)
Jethro Larson
@jethrolarson
Jul 15 2015 21:14
:point_up: that does the same as function(){normalFunction(request)} btw
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:14
@jethrolarson indeed
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:15
rather the same as R.partial(normalFunction, request);
because it bind, like partial, return a new function, they don't invoke
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:16
Yes but partial implies that there are more arguments to come.
Jethro Larson
@jethrolarson
Jul 15 2015 21:16
Yeah. I guess so. Semantically it seems wrong to me.
piping over a thunk is doubly wrong to me
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:17
whats a thunk? :)
Jethro Larson
@jethrolarson
Jul 15 2015 21:17
function that takes no args
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:18
@TheLudd hm, that's a good point, that is why it felt so wrong to me, I guess .bind(null, request) communicates the purpose much better.
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:18
only heard the word nullary for that
Jethro Larson
@jethrolarson
Jul 15 2015 21:19
foo = pipe(bar, baz)
foo()
//this should not work imo
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:19
@dypsilon Yes. Although I don't like that you hve to supply a "this" arguent to bind, the null. I thought ramdas bind function skipped this but it was the complete other way around :)
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:19
My thinking was, that pipe is a better version of compose.
Ludwig Magnusson
@TheLudd
Jul 15 2015 21:19
pipe or compose is just a matter of taste IMO
Jethro Larson
@jethrolarson
Jul 15 2015 21:20
pipe is nice cause you don't have to read right to left to understand what it's doing
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:20
yep, so I don't see a problem doing compose(secondFunction, normalFunction.bind(null, request));
even though normalFunction is a thunk
Jethro Larson
@jethrolarson
Jul 15 2015 21:21
though compose unnests functions without moving them around, so it's a more natural transformation. I use both as it suits me
I still think if your compose or pipe is nullary it should be obvious. Can do that with type sig though, I guess
Jethro Larson
@jethrolarson
Jul 15 2015 21:27
Honestly I have done this for a ajax thing before. I had started out with the argument-less function but it was awkward so I just made it take the url and I pass it in. Makes the app more configurable/testable anyway>
Especially when I later wrapped the ajax in a Future
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:30
Hm, I think you are right. It does make sense to do pipe(prepareRequest, sendRequest, parseResponse, showOutput)
Jethro Larson
@jethrolarson
Jul 15 2015 21:34
If you think of pipe as literal, can you imagine dropping nothing into a pipe and then watching bowling balls come out the other side?
vs dropping cubes of rubber
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:35
Well, this is a pipe with side effects :)
Jethro Larson
@jethrolarson
Jul 15 2015 21:36
Yeah. Obv this lib aims to minimize that where possible
er well "practical"
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:37
well I don't really have anything to pass inside the pipe, this is a command which is executed on the console, the pipe has to start somewhere...
Jethro Larson
@jethrolarson
Jul 15 2015 21:40
sure
Michael Hurley
@buzzdecafe
Jul 15 2015 21:46
@jethrolarson i always thought of a thunk as a delayed computation. not a nullary function
Tim Navrotskyy
@dypsilon
Jul 15 2015 21:48
this is how thunk is defined in the "Functional JavaScript" book
Highland defines "thunk" as a function which forces a stream to "flow". Like "resume" or "done".
Jethro Larson
@jethrolarson
Jul 15 2015 22:14
Okay. I may be wrong about it.
BTW https://www.youtube.com/watch?v=oYk8CKH7OhE
I think this is relevant to ramda's "marketing"