These are chat archives for ramda/ramda

6th
Oct 2017
Robert Mennell
@skatcat31
Oct 06 2017 00:02
although I'm curious if var comp +> fn1, fn2, fn3; // fn1(fn2(fn3(value))) wouldn't be a nifty way to do composition showing that this is a composition of the following functions
Michael Rosata
@mrosata
Oct 06 2017 01:06
@montogeek np :)
Jonah
@jonahx
Oct 06 2017 02:04
I know you can create custom builds of ramda, but does anyone know if there is a version which each implementation is independent and ready to be copy-paste used? Ofc, I could make a bash script and do a separate custom build for each function or something like that, but was curious if this already exists?
Ian Hofmann-Hicks
@evilsoft
Oct 06 2017 02:15
Out of curiosity do you need such a thing?
I mean WHY do you need such a thing. Sorry on my phone
Jason Shin
@JasonShin
Oct 06 2017 02:42
@Bradcomp Yup, pipeK should work, thanks!
Jonah
@jonahx
Oct 06 2017 03:30
@evilsoft I’ve been messing around on codefights, but you can’t include a library in your answer, and the source code limit is 35K — too small for ramda wholesale. But playing the challenges without ramda is just not at all fun to me… it’s just an exercise in reimplementing library functions.
Ian Hofmann-Hicks
@evilsoft
Oct 06 2017 04:01
That is AWESOME
so A buddy of mine wrote this...depending on your needs...I use it for sooo much
at 822 bytes it is super sweet
James Forbes
@JAForbes
Oct 06 2017 04:33
that's really cool @evilsoft thanks for the link :D!
Jonah
@jonahx
Oct 06 2017 04:35
@evilsoft nice. that could work.
Sean Lindo
@seanlindo
Oct 06 2017 06:48
Is there an opposite of isEmpty?
I would like to know if an object is not empty, but I've found myself having to write it out as R.compose(R.equals(false), R.isEmpty)
arian‮
@arian-swydo
Oct 06 2017 07:43
complement(isEmpty)
James Forbes
@JAForbes
Oct 06 2017 07:51
@seanlindo out of interest, are you using this in a call to when, ifElse or filter ?
because when has a negated version unless and filter has reject, and with ifElse you can swap your visitors
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 09:54
Hi, I'm creating some API wrapper with a lot of functions, some of them have exact the same couple of arguments like (accountId, accessToken). I'd like to create some factory so once you create that api module, you'd reuse those function in particular account context. The obvious solution in OOP is to create class for that api module, and then pass those args to constructor. I'd like to do it in more FP way, I figured out that I could use curry to pass those first arguments but I still have a lot of functions and I don't want to initialize N functions with accountId/accessToken each time. Does anyone here know any better way how to it?
James Forbes
@JAForbes
Oct 06 2017 09:56

A closure

function API(accountId, accessToken){

  function all(x,y,z){}
  function your(){}
  function functions(){}

  return {
    all
   ,your
   ,functions
  }
}

const api = API(accountId, accessToken)

// has access to accountId and accessToken
api.all(x,y,z)

This is basically currying all those functions without the boilerplate

Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 09:57
yes but in this way all those inner functions won't be pure right?
right now I have something like this
function getAccountResource(accountId, accessToken, resource, params)
const getAccountResource = R.curry(api.getAccountResource)(pw.account.id, accessToken)
James Forbes
@JAForbes
Oct 06 2017 10:00
so, all currying is, is a closure
E.g. in ramda we have this auto currying behaviour, but all curried functions are, are unary functions that return unary functions
e.g. you could define getAccountResource as
const getAccountResource = account_id => accessToken => resource => params ...
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:02
yes
James Forbes
@JAForbes
Oct 06 2017 10:02
so we're just creating a series of closures one after the other
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:04
yes but in your example you're just using accountId and accessToken directly inside those inner functions, so they depends on external state, right?
James Forbes
@JAForbes
Oct 06 2017 10:04

but if you know you always want accountId and accessToken you're paying the cost of currying each individual function, instead of having a shared closure that you define once, its less RAM, but its just as pure as real currying.

Provided you don't mutate anything, the above is fine.

its not mutable state though
( well, technically is, but so is account_id => accessToken => resource => params )
because in that body I could reassign a function parameter or do something horrible that JS let's me do
so pure always has a big asterisk next to it in JS
its all "external state"
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:06
yes, I understand that part
but I was rather thinking on easy composition of losy coupled functions
James Forbes
@JAForbes
Oct 06 2017 10:08
but they are inherently coupled to those arguments right?
so why decouple what's coupled?
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:09
for example if you would like to make calls for different accounts, or to same account with different token
so instead creating n instances, you could compose those functions easily
James Forbes
@JAForbes
Oct 06 2017 10:09

I don't want to initialize N functions with accountId/accessToken each time. Does anyone here know any better way how to it?

Sounds pretty coupled to me.

If you want different accounts just call differentAccount = API(accountId, accessToken)

Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:10
but obvious problem with that is that'd need to do it for n functions, like getSomething, postSomethingElse etc
James Forbes
@JAForbes
Oct 06 2017 10:10

yeah n instances is likely less overhead than currying every single function

I'm not saying you can't do it another way, but that's what I'd do

Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:10
yes I perfectly understand how that pattern works, there only difference vs OOP is state mutation
James Forbes
@JAForbes
Oct 06 2017 10:11

obvious problem with that is that'd need to do it for n functions, like getSomething, postSomethingElse etc

Can you elaborate on that?

Kurt Milam
@kurtmilam
Oct 06 2017 10:11
You can also take a look at the Reader Monad, which is another functional pattern you can use for dependency injection.
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:12
lets assume that I have 10 functions on that api wrapper, each for HTTP method + some custom, like you'd need to use fancy url
all those functions starts with accessToken + 95% of them uses accountId
James Forbes
@JAForbes
Oct 06 2017 10:13
right, so what would be the problem with a closure then, sounds perfect to me
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:14

in some loading tests I'd need to constantly write something like this

let res = api.getAccountResource(pw.account.id, accessToken,{limit: 1000, sort: 'name'})

the obvious solutions is to wrap it with function as you pointed out, or to create class

Kurt Milam
@kurtmilam
Oct 06 2017 10:14
But a closure is perfectly fine here, as James is explaining, and it's usually how I'd accomplish what I believe you're trying to accomplish.
James Forbes
@JAForbes
Oct 06 2017 10:14
Aren't pw.account.id, accessToken already in the api closure?
oh sorry you're explaining the original problem, I thought you were saying closures were problematic in some way
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:16
and my question was is it more FP way how to do it
James Forbes
@JAForbes
Oct 06 2017 10:16
It doesn't get much more FP than closures :D
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:16
the closure wrapper is something that I'd use as my first choice, just wanted to know if I could do it better :)
James Forbes
@JAForbes
Oct 06 2017 10:17
I think its the first and best
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:18
I liked the idea of using curry
const getJoeResource = R.curry(api.getAccountResource)(joe.account.id, joeAccessToken)
it looked clean and verbose later when you'll use it
but it doesn't look nice when you need to curry n function for Joe each time
Syaiful Bahri
@syaiful6
Oct 06 2017 10:19
@AdrianSkierniewski the easy way is just curry it, take the most changed argument in the last, if i face that problem in Purescript i probably just represent getAccountResource API call to data type to interpret later, so you just give accesToken and AcountID once.
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:20
yes, this is exactly what I was trying but the problem is the number of functions that I'd need to "initialize" with accesToken and AcountID
it looks nice when I have single method
but if you have 4 methods: GET, POST, PUT, DELETE and then 4 accounts, you'd need to call curry functions 4*4 just to have functions that you can reuse later vs create a closure and then build 4 objects
I didn't like the idea that all those functions in closure are coupled and can't be tested outside
but it looks like this is the only good way how to do it
Syaiful Bahri
@syaiful6
Oct 06 2017 10:23
no, you can use Free monad.
James Forbes
@JAForbes
Oct 06 2017 10:24

I did exactly what you're suggesting once, but I ended up opting for the closure, for one you get inference for free, curry immediately creates a black box that makes it very hard to statically analyze, so you lose that. That doesn't matter as much to some people, but its important to me.

Debugging isn't impossible by any means, but you have to blackbox scripts, its heavier, because its uses more RAM. And if you've got to do this for n functions, its just noisier from an ergonomics perspective. You're also relying on a library for something that JS can do perfectly well natively. Again that's something I might care about more than others. I lean on ramda when the alternative would be cumbersome.

You can definitely curry it, but if you're going to do that, I'd personally manually curry it, throw all your functions in a list and map over them to initialize them. But I think a closure is a better fit here personally.

Kurt Milam
@kurtmilam
Oct 06 2017 10:24
This isn't the only good way to do it. It's just one of several. I mentioned the Reader Monad above, for instance.
James Forbes
@JAForbes
Oct 06 2017 10:25

can't be tested outside

What do you mean by that?

Syaiful Bahri
@syaiful6
Oct 06 2017 10:25
the currying method can be tested..
since it just function though
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:27
@JAForbes those methods in closure lives only inside that closure, so to test that you'd need to initialize that object. This isn't bad you saying :) I perfectly understand all your points. Just wanted to get some answer that I would not expect
James Forbes
@JAForbes
Oct 06 2017 10:28
ahhh
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:28
@kurtmilam @syaiful6 do you guys have any examples ?
Kurt Milam
@kurtmilam
Oct 06 2017 10:29
@AdrianSkierniewski You could define the functions outside of the closure and define calls to them inside the closure.
If you really wanted to be able to test the functions separately.
James Forbes
@JAForbes
Oct 06 2017 10:29
well ... how bout you generate all your api functions from a config file, then your interface is just const api = config.reduce( createFn, { accessToken, accessId } )
then in your tests you can generate the methods differently so you don't need to use the network for example
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:29
I don't want to do it :) just researching all possibilities to learn more
Kurt Milam
@kurtmilam
Oct 06 2017 10:30
This is the first result Google shows me when I search for javascript reader monad: https://passy.svbtle.com/dont-fear-the-reader
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:30
so in other words there is a factory that will build all those functions for me
James Forbes
@JAForbes
Oct 06 2017 10:31
yeah, that's what I'm migrating to now, its fantastic
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:31
nice
James Forbes
@JAForbes
Oct 06 2017 10:32
you can have dev and prod versions where the dev version checks the validity of the input and output, you can have different methods for SSR and client side usage, and tests
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:32
so I could have exact same definitions but then I can easily initialize some args for multiple functions based on cfg
James Forbes
@JAForbes
Oct 06 2017 10:32
I mean its kind of what @syaiful6 is suggesting with the Free Monad, but just less formal
yeah
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:33
@kurtmilam @JAForbes @syaiful6 Thank you very much for pointing me different directions!
James Forbes
@JAForbes
Oct 06 2017 10:33
e.g. I have properties like auto_list that says whether or not to grab a list property off the response automatically for me
:+1:
Kurt Milam
@kurtmilam
Oct 06 2017 10:39
:+1: to 'hydrating' the application based on configs. That's something I've always liked to do, and it seems particularly suited to applications written in an fp style.
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 10:42
I liked the idea with defining functions outside of the closure
combining this with ES6 modules + default export could be the way I'll go
export function getAccountResource(accountId, accessToken, resource, params) {
  return get(`/v1/accounts/${accountId}/${resource}`, params, {headers: {Authorization: accessToken}})
}
export default function(accountId, accessToken) {
  return {
    get,
    post,
    getAccountResource: R.curry(getAccountResource)(accountId, accessToken),
Barney Carroll
@barneycarroll
Oct 06 2017 10:45

@kurtmilam

don’t confuse this for a Monad tutorial. I am not going to run into that trap.

I like this article

Kurt Milam
@kurtmilam
Oct 06 2017 10:46
I've read it before, but I need to read it again. Glad you're enjoying it!
I also plan to read the linked Letter to a Young Haskell Enthusiast today.
James Forbes
@JAForbes
Oct 06 2017 11:24
thanks for pointing it out
I feel it applies 1:1 to JS
Selwyn
@Siilwyn
Oct 06 2017 12:05
Hi folks I'm using Ramda more and more in my code but feel like it hurts the readability.
How do you refactor code to make it's functionality more obvious?
For example if I would rewrite the following example to return the R.pipe directly I would have no clue what to pass to the function.
const getSteamappVdfLaunch = function ({steamPlatform, arch, launchConfig}) {
    const archPath = ['config', 'osarch'];

    return R.pipe(
        R.values,
        R.either(
            R.ifElse(
                R.find(R.prop('config')),
                R.F,
                R.head,
            ),
            R.find(R.both(
                R.pathEq(['config', 'oslist'], steamPlatform),
                R.either(
                    R.pathEq(archPath, arch),
                    R.pathEq(archPath, undefined)
                ),
            )),
        )
    )(launchConfig)
}
But defining it like above is akward too... having to pass launchConfig at the end.
Or am I just horribly abusing Ramda like this? :)
krzysztofpniak
@krzysztofpniak
Oct 06 2017 12:30
Hi, I would like to convert flat list that contains objects with id and parentId fields to tree, how can I do this in ramda?
arian‮
@arian-swydo
Oct 06 2017 12:35
@selwyn, although I'm not very knowledgable as far as FP goes, what I do is type annotate, jsdoc and unit test each function
I use all these three as documentation, especially unit tests can explain what inputs result in what outputs, and jsdoc/type annotation can tell you its signature
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 12:52
is there any ramda function that will take nested objects and will return arrays with specific properties for each level?
 [
  {
    id: 1,
    results: [
      {
        id: 8,
        results: [
          {
            id: 88,
          },
          {
            id: 64,
          }
        ]
      }
    ]
  },
  {
    id: 2,
    results: []
  }
]
// To something like this
[[1, 8, 88], [1, 8, 64], [2]]
Selwyn
@Siilwyn
Oct 06 2017 12:54
@AdrianSkierniewski thanks for your reply! That's a viable option. I really like if the code expresses its usage though :/
Let's see if anybody else weighs in. :)
arian‮
@arian-swydo
Oct 06 2017 13:03
curious myself @selwyn ;)
Kurt Milam
@kurtmilam
Oct 06 2017 13:50
@Siilwyn you expect this function to return a boolean, correct?
Kurt Milam
@kurtmilam
Oct 06 2017 14:07
@Siilwyn I think this may be a bit of an XY question. In other words, I think there may be a more straightforward (and simultaneously more easily human-parseable) way to accomplish what you're trying to accomplish here.
@AdrianSkierniewski I don't think there's anything built in that will do what you're looking for. I think you'll need to write a recursive function that walks the tree. I'll post something shortly that does something similar for a more tree-like data structure, but it may give you some ideas on how to approach your problem.
Kurt Milam
@kurtmilam
Oct 06 2017 14:13
This is more involved that what you're looking for, but I think you may be able to extract the logic you need from it.
Adrian Skierniewski
@AdrianSkierniewski
Oct 06 2017 14:15
I was hoping for some magical map/path/reduce combination :) I even started trying to compose those functions together
@kurtmilam thank you
Kurt Milam
@kurtmilam
Oct 06 2017 14:18
If I get a chance, I'll see whether I can extract the necessary functionality from the function I shared.
Kurt Milam
@kurtmilam
Oct 06 2017 14:28
@krzysztofpniak can you put some sample input and the desired output in a REPL?
Selwyn
@Siilwyn
Oct 06 2017 14:29
@kurtmilam heh actually it returns an object from a given array (hence the find). What do you mean with XY question? Do you mean writing it more in an imperative way?
Kurt Milam
@kurtmilam
Oct 06 2017 14:31
http://xyproblem.info/ I mean if we know what the function is supposed to do, we may be able to point you to another implementation that's easier to read.
Selwyn
@Siilwyn
Oct 06 2017 14:37
@kurtmilam ah I got you. I was actually fishing for multiple answers.
  1. how people refactor unreadable functional ramda code.
  2. if defining functions by assigning ramda functions (hiding the input) is a good idea
  3. how the example I posted can be done in a more readable way (hold on a minute, I'll describe my intention with this one)
Kurt Milam
@kurtmilam
Oct 06 2017 14:39
@Siilwyn This is an obviously untested take on refactoring your original function.
Selwyn
@Siilwyn
Oct 06 2017 14:42
@kurtmilam wow mad props for writing that without context or data
This is a bit more detailed example https://goo.gl/rZhXWf
Kurt Milam
@kurtmilam
Oct 06 2017 14:43
No idea whether it works :) And it's slightly different than yours, but if I interpreted things correctly, the difference should be immaterial.
@Siilwyn I've pulled in your data here. It's not throwing an error, at least :D
Selwyn
@Siilwyn
Oct 06 2017 14:45
@kurtmilam cheers!
looks like it's slightly different. e.g. passing 'windows' as a platform doesn't work
I'll go through it and see if I understand what you wrote
I'm a ramda newbie, e.g. o is new for me
Ramda has so many functions! :0
(need to catch a train, brb.)
Kurt Milam
@kurtmilam
Oct 06 2017 14:47
seems to work with 'windows' if I remove the 'arch' property from the first argument.
I've tested with 'windows' and 'macos' (both with no arch) and with 'linux' and arch = 32 and 64. Looks OK. I will try to figure out how to get it to work with an arch key in the argument if there's no arch in the matching config.
magic
@davidparker
Oct 06 2017 15:07

Hi. Can anyone point me in right direction on how to run a R.contains on a R.cond.

R.cond([
                    [R.contains(['checkbox', 'select', 'select-one', 'date', 'text', 'textarea', 'hidden']), function (t) {                        
                        return {
                            name: f.id, value: f.value, type: f.type
                        };
                    }],
                    [R.T, temp => console.log(R.contains(f.type, allowed))]
                ]);

when i run that is get true 9 times in console. if i try add value to contains I get an an error that t[n][0].apply is not a function

just seems like I can only do things like equals and anything else that only accepts single param,

Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:26
R.contains
magic
@davidparker
Oct 06 2017 15:27
?
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:27
I think you have the parameter order for contains backwards. You can either pass in R.__ as the first paramater, which is a placeholder, or else useflip`
magic
@davidparker
Oct 06 2017 15:28
hi. Yeah i have tried all, R. none etc. not tried flip? what do you mean by that is it R.Contains(R.flip(R.),[]) or flip the entire contains.
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:30
@ram-bot
R.cond([[R.flip(R.contains)(['a', 'b', 'c']), (val) => val + 'Good'], R.T, (val) => val + 'Bad])('b')
ram-bot
@ram-bot
Oct 06 2017 15:30
Unexpected identifier
Kurt Milam
@kurtmilam
Oct 06 2017 15:30
@Siilwyn I think this does the trick.
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:32
@ram-bot
R.cond([
  [R.flip(R.contains)(['a', 'b', 'c']), (val) => val + 'Good'], 
  [R.T, (val) => val + 'Bad']
])('b')
ram-bot
@ram-bot
Oct 06 2017 15:32
'bGood'
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:33
@ram-bot
R.cond([
  [R.contains(R.__, ['a', 'b', 'c']), (val) => val + 'Good'], 
  [R.T, (val) => val + 'Bad']
])('b')
ram-bot
@ram-bot
Oct 06 2017 15:33
'bGood'
magic
@davidparker
Oct 06 2017 15:34
yep :-) that got it, thanks @Bradcomp
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 15:35
:+1: :bowtie:
Kurt Milam
@kurtmilam
Oct 06 2017 16:02
@Siilwyn I believe this works for all cases and may be about as clean as it's going to get.
Kurt Milam
@kurtmilam
Oct 06 2017 16:10
And here it is with pipe rather than o to dispel the magic.
Robert Mennell
@skatcat31
Oct 06 2017 16:44
TheNavigateur/proposal-pipeline-operator-for-function-composition#4 I'm not barking up the crazy tree with this am I for thinking about harder definition of what dierction composition comes in?
Sean Lindo
@seanlindo
Oct 06 2017 17:21
Is this "correct"?
const generateURL = R.ifElse(
    R.allPass([RA.isString, R.compose(R.equals(false), R.isEmpty)]),
    Maybe.Just,
    Maybe.Nothing
)

const httpGet = R.curry(url => {
    return fetchF(url.getOrElse(null))
})

const getProfileSummary = R.ifElse(
    Maybe.isJust,    
    httpGet,        
    Future.of
)
I know that the URL is a valid string and I know it's not empty
But it just seems weird that I need to make httpGet aware that it's receiving a Maybe
Was hoping I could just pass it the value of Just somehow
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 17:33
You can use map on the Maybe to process only if you have a Just
pipe(
  generateUrl,
  map(fetchF),
  maybe => maybe.getOrElse(Future.of(/*defaultValue*/))
)
R.compose(R.equals(false), R.isEmpty) === R.complement(R.isEmpty)
Sean Lindo
@seanlindo
Oct 06 2017 17:54
Let me try that in Runkit..
Also, thanks for the tip on the negation
Sean Lindo
@seanlindo
Oct 06 2017 18:00
Yeah, that's much more terse (and readable).
How would this code snippet change if I actually wanted to inform the caller of this error? As it stands now, it causes a noop, but I'm sure in the future I'll have a use case where I need to be explicit about the failure (such as sending an error message in a JSON response to a 3rd party API customer)
Sean Lindo
@seanlindo
Oct 06 2017 18:05
It looks like Either may be a good candidate
Brad Compton (he/him)
@Bradcomp
Oct 06 2017 20:34
Either is a great candidate
I have a function in my utilities, eitherFromPred :: String -> (a -> bool) -> a -> Either String a, that helps with that case
const eitherFromPred = curry((msg, pred, a) => pred(a) ? Right(a) : Left(msg));