These are chat archives for ramda/ramda

2nd
Sep 2016
James Forbes
@JAForbes
Sep 02 2016 03:00

@brian-gates so one thing is: after findArguments receives req, req is not needed anymore. From there you only need node and the results of the previous call. So that whole mechanism can happen before hand.

The next thing to clean up is the fact findRelated is a method. I'm not sure if you have control over that api, but it'd be easier to compose if node was an input to a findRelated function

e.g.

function findRelated(args, node){
   return node.findRelated.apply(node, args)
}

//or
const findRelated = R.invoker(1, 'findRelated')

Now its a simple pipeline

useWith(apply, findRelated, findArguments]

If you want to do it all inline, you can (but it might not be as readable):

useWith(apply, [invoker(1, 'findRelated'), findArguments)])

useWith just transforms each arg received with the corresponding function in the array. We end up with invoker waiting for arguments, and we have the arguments from findArguments. The next thing useWith does for us is apply a function with the transformed args. In this case we are being super literal and providing apply itself.

Part of me thinks useWith is confusing because it does two things. It transforms args, and then also applies them. But if useWith just transformed args we could apply them via compose or pipe

So maybe we just need a transformArgs function?

compose(apply, transformArgs( [invoker(1, 'findRelated'), findArguments)]))
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 03:21
@JAForbes The issue with transformArgs is that function composition works with unary functions. useWith allows for compositions of functions with higher arity.
James Forbes
@JAForbes
Sep 02 2016 04:33

@Bradcomp good point!

Just a thought experiment:

What if compose just modified arguments[0] but kept the other args intact?

function compose(f2,f1){
  return function(...args){
     args[0] = f1(...args)
     args[0] = f2(...args)
     return args[0]
  }
}

function identity(a){
  return a
}

function transformArgs(transforms){
  return function(...args){
     return args.map( (a,i) => (transforms[i] || identity)(a) )
  }
}

function apply(f){
  return function(args){
     return f.apply(null, args)
  }
}

function multiply(a){
  return function(b){
    return a * b
  }
}

function add(a,b){
  return a + b
}

var f = compose(
  apply(add)
  ,transformArgs([multiply(2), multiply(3)])
)

f(1,1) //=> 5
Obviously you couldn't change the behaviour of compose without breaking everyone's code. But still interesting I think.
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 13:49
Heya, can anybody suggest a nicer way to write this? http://goo.gl/vCk8je
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 13:59
What I sort of need is a pathOrWith kind of function
James Forbes
@JAForbes
Sep 02 2016 14:44
If you're comfortable with empty strings being falsey you could useeither
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:44
I've got a unary function that I've composed. I now need to pass in another parameter. Is there a way I can do that without breaking my composition? Here's the function: http://goo.gl/UdruQ7
James Forbes
@JAForbes
Sep 02 2016 14:44
either(
        path(['context','error_description'])
        ,prop('message')
)
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:44
Ah, yes, I forgot about that func, thanks @JAForbes!
James Forbes
@JAForbes
Sep 02 2016 14:44
:)
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:45
Have you ever had a problem like the one I've just posted?
I've got to inject a function instead of importing it and I'm not sure how to change my compose
James Forbes
@JAForbes
Sep 02 2016 14:46

here was my best attempt for handleError

converge(
  merge, [
    pipe(
      either(
        path(['context','error_description'])
        ,prop('message')
      )
      ,objOf('message')
    )
    ,pick(['status'])
  ]
)

I think the original was easier to read though :(

Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:46
hmm, maybe
James Forbes
@JAForbes
Sep 02 2016 14:48
yeah tough problem
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:48
Problem is, I'm using the same sort of pipeline in my other services
across at least 5 or 6 files
James Forbes
@JAForbes
Sep 02 2016 14:49
so might help if I have more context
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:49
I've seeing somebody use useWith here, but I don't know if that overcomplicates or not
James Forbes
@JAForbes
Sep 02 2016 14:49
why is this function imported later?
and how do you access it later
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:49
Right, this function used to use a single user token to access an OAuth API
now, I've implemented a sign in process so that users access only their own data
so I'm partially applying the requestData function with the token from the node middleware
so that my services don't have to have knowledge of the token itself
The function used to request a token itself, you see
Does that make sense?
James Forbes
@JAForbes
Sep 02 2016 14:50
yeah makes sense
you have a parameter in a curried function for the token, you then partially apply it with the token and pass that function around
is that right?
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:51
yep
James Forbes
@JAForbes
Sep 02 2016 14:51
yeah I do something similar, except there is only 1 token at a time, so not as hard
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:51
const requestBizApi = R.curry((accessToken, endpoints) => (
I've got a simpler service that doesn't benefit from being composed, so it works nicely there
I wonder if there's a different composition I could use
Because getEndpoints depends on the overall composition being unary so it can access projectId
James Forbes
@JAForbes
Sep 02 2016 14:55
Yeah I am toying with partially applying pipe, but might be a black hole
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 14:55
I think I need to somehow apply the arguments to the inner compose
I'd like to be able to pass the 2nd argument to getEndpoints and have it use the 1st argument as the requester function
I wonder if it would help if I flipped the argument order so that I could somehow build the endpoints and then pass them into the requester function
James Forbes
@JAForbes
Sep 02 2016 14:59
yeah I think I have something
So you want to call getEndpoints with the id, and then you want to pass those endpoints to the bizAPI with a preapplied token right?
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 15:00
exactly :)
James Forbes
@JAForbes
Sep 02 2016 15:02
const getEndpoints = projectId => [`/projects/${projectId}/tasks`];
const bizAPI = (token) => (endpoints) => endpoints.map( e => e + '/token='+token)

const fetchTasks = 
   partial(pipe, [getEndpoints])

fetchTasks((bizAPI(123))(1) //=> ["/projects/1/tasks/token=123"]
You can wrap that partial in the composeP and it will all work
Just trying to reduce it to the problem domain

So we partially apply pipe as opposed to compose because we want getEndpoints to be called first.

Then we pass in the bizAPI it will invoke the pipe creating the composition.

Then we pass in the project_id and kick off the execution of the pipe which evaluates to something like:

pipe(
  getEndpoints
  ,bizAPI(123)
)(1)
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 15:06
hmm I think I follow, the trick is not to try to bake in the 2 arguments into the function definition
James Forbes
@JAForbes
Sep 02 2016 15:06
yeah
like a lazy pipe
I guess
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 15:06
yeah
James Forbes
@JAForbes
Sep 02 2016 15:06
if you wanted the order swapped you could partial compose, or partialRight pipe
I have to sleep, but good luck! :D
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 15:09
Thanks good night @JAForbes !
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:23
This is the latest attempt I have done, but I can't seem to compose the partially applied function: http://goo.gl/Qt7sga
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 16:30
Does that do it?!
compose doesn't automatically curry the resultant function
So I wrapped the composition in a curryN(2)
Brian Gates
@brian-gates
Sep 02 2016 16:32
@JAForbes thanks, that looks a lot better :)
Without ES6 modules, what's the best way to arrive at a state where you don't have to prepend R.? Just have a list of declarations at the top?
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:32
That seems to make the REPL work, but it's not working in my prod code @Bradcomp. Thanks though!
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 16:33
Weird...
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:34
Is there a better way to do this anyway?
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:41
I don't know if it makes any difference but I'm using composeP in my prod code
and I can get the data out if I just use partialRight (without the curryN wrapper), but it returns an empty object when I try to add the mapper function
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 16:48
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:51
callBizApi is injected into the fetchTasks function with the token partially applied
That's why I'm asking about how would I do this to avoid the points
const fetchTasks = (projectId, requestData) => (
  R.composeP(
    mapTasks,
    R.compose(requestData, getEndpoints)
  )(projectId)
);
That's what I have working at the moment in my prod code
It's not so bad, it's just I've got this composition pipeline in all my (6+) services
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 16:56
I'm passing down a function that requests data with a token partially applied to keep my services as ignorant of the token as possible
Previously, the callBizApi function requested its own token. Now I've implemented authentication, so I need to pass the current user's token now instead.
I guess it is probably easier to just to add the function wrapper to keep things sensible :)
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 16:58
Yeah. I think composeP is a part of the problem
It expects a Promise, which is just one thing.
I don't know if I've had enough coffee this morning
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 17:00
ha sorry @Bradcomp, this is a bit of a loaded problem. Thanks for having a look :)
Brad Compton (he/him)
@Bradcomp
Sep 02 2016 17:00
Happy to ... <not really> help! ;) :D
Aaron Mc Adam
@aaronmcadam
Sep 02 2016 17:03
Well I still learned something, so it's all good!
Rick Medina
@rickmed
Sep 02 2016 17:13