mediasuitenz / tech-stack

Media Suite's evolving tech stack
6 stars 0 forks source link

RFC: Functional Programming/Ramda, Is it working for us? #17

Closed anotheredward closed 7 years ago

anotheredward commented 8 years ago

Developers who like Ramda/Functional programming have been pretty vocal, but it would be nice to get a broader opinion. Thoughts?

pnw commented 8 years ago

I've all but stopped using ramda except in exceptional cases where I have to actually transform data in situations where simple maps and filters are insufficient, or when there are more than 3-4 operations that need to be performed. In MyWorksites, that has been a rare occasion.

One example where it's been useful is where we do crazy manipulations to reduce the MyWorksites raw permission data into a usable format. Other than that, I just use a function or two at a time as helpers in the absence of native functionality, e.g. R.contains.

I've come to realize that ramda is overkill and unnecessary in most cases, given the type of work that we do. In ember, we get enough "functional stuff" in the form of computed properties, one-way data flows (not 100% automatic right now, but is easy enough if you use actions correctly). As for loopback, most of the time (at least with my worksites), promise chains are the primary control flow mechanism and we're rarely doing any kind of meaningful data manipulation.

Insert quote about hammers and things that look like nails.

In terms of using it with ember and loopback, most of the time we are manipulating framework objects instead of POJOs. In making ramda-extended compatible with ember, I had to essentially replicate the ramda api using ember getters and setters, which means we aren't even benefitting from the whole immutable state concept.

Verdict

It's definitely a useful tool for certain situations (manipulating raw data, various helper functions, sometimes with list manipulation when there are a lot of steps), but I don't support using pipes and such everywhere for the sake of using ramda everywhere. I prefer using regular ES6 sugar and arrow functions over ramda, since, in practice, ramda usually only ends up serving as an alternate syntax.

anotheredward commented 8 years ago

Would you consider removing Ramda from our standard list of tools @pnw ?

pnw commented 8 years ago

In myworksites, we are basically just using ramda as a helper library, which is not it's intended purpose. It works fine as such, but we might find ourselves better served by lodash (they have an fp version as well) whos goals are more in line with our needs.

I'm just wary that we might be cargo-culting a bit with the whole functional programming === ramda thing, which I realize is a complete reversal of how gung-ho I was about it six months ago. The fp concepts are invaluable, but I feel that the projects that we work on substantially benefit from us using ramda and I feel like the ember+ramda pairing is forced and causes friction.

anotheredward commented 8 years ago

I feel like we might be better off on the client-side not using it, we already have two ways of doing things: the JS way and the Ember way, adding Ramda as a third way may be more trouble than it's worth.

I do worry sometimes that we throw away one of the biggest benefits of JS, that almost anyone can understand and work with it from any background, when we use many Ramda functions/pipes.

The sheer size and mixed direction gets to me a bit. Unlike other FP languages, where once you understand the basic methods, and the variations, you're done.

The smaller and simpler we can make our stack, while still getting the work done, the better imo.

digitalsadhu commented 8 years ago

I have also stopped using ramda other than in the sorts of cases Patrick mentions. I find debugging really hard and coming back to code later mind bending. Having to unwind things just to stick break points in is teh suck.

In my side projects I tend to use lodash instead of ramda for the kinds of utility Patrick mentioned. It's good for open source projects since it's so well known and used. On Fri, 19 Aug 2016 at 03:27, Ed notifications@github.com wrote:

I feel like we might be better off on the client-side not using it, we already have two ways of doing things: the JS way and the Ember way, adding Ramda as a third way may be more trouble than it's worth.

I do worry sometimes that we throw away one of the biggest benefits of JS, that almost anyone can understand and work with it from any background, when we use many Ramda functions/pipes.

The sheer size and mixed direction gets to me a bit. Unlike other FP languages, where once you understand the basic methods, and the variations, you're done.

The smaller and simpler we can make our stack, while still getting the work done, the better imo.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mediasuitenz/tech-stack/issues/17#issuecomment-240903945, or mute the thread https://github.com/notifications/unsubscribe-auth/ABH2CqseLYLJSrXfeAOMWMrlhMFyawi6ks5qhQZlgaJpZM4JnHG2 .

anotheredward commented 8 years ago

I also notice that lodash has a stripped down "core" library, not sure it really meets our needs though. https://github.com/lodash/lodash/wiki/build-differences

But probably a plus that we could specify a subset that we use, considering that lodash is even bigger than Ramda :(.

ebuckley commented 8 years ago

I think there are some real wins to be had from using a functional style, however pragmatically we need to find a balance where everyone feels the coding style is there most productive.

Six months of investment in Ramda over the team is not insignificant, I would be hesitant to switch to lodash or another utility library, unless there is a very compelling reason. A better approach would be to find the right parts of the lib to use.

In terms of building up a function using a pipe, I really support this for code clarity and ease of understanding, usually you can get a lot done in very few lines. However, I understand how this can be a painful fit in dynamic typed languages. In a language with better typing this means most of the time you are fixing the code at the compile step instead of waiting for it to explode at runtime.

When working with a regular array and POJO's. Using a map + filter + reduce pattern is how I approach the problem. Pipes and Currying help support this.

Working with Ember objects and relations can be a bit hard to understand, I feel the resistance that the language/framework applies here. Using this in a route seems to add a bunch of unnecessary complexity, but if this logic is extracted to the model it can make the routes a lot easier to understand and test.

On Currying, using simple currying for maps/filters/reduces in a pipe really makes sense, I think exposing modules that have curried functions can be a painful experience, it is not standard to the language so a "normal" developer will not be expecting to see/use them. When working with pipe's or http requests you can get some pretty decent code re-use.

Will leave this on what I think is most important. Expose the right functions, really great names, and a set of test suites, no one will ever have to understand the functional spaghetti on the inside.

anotheredward commented 8 years ago

I feel like the conclusion here is that we should extract a list of the functionality we use from Ramda , so that developers can be clear on what they should/shouldn't know. Hopefully, this will ease learning and make our programs more consistent. I'm aware that @ebuckley is working on something like this, I've worked on a Nodeschool in the past by grepping the LLUR project and seeing what we use. The plan for my Nodeschool looked like this:

Potential outline:

1. 
Most basic functions you expect exist
Open up the docs
JS function names abound
values
string: split, trim, replace
number: Math operations eg: add, inc
objects: is, empty
logic: not
lists: of
Naming common things allows us to write common code, so let's learn some vocab

2.
naming conventions: with, by, Obj, single data type
Ramda functions operate on lists by default
eg: map, filter, reduce
Methods that operate on or produce Objects generally have the suffix 'obj'
eg: mapObj, zipObj
Methods that take a function to do their work generally have the suffix 'with' or 'by'
eg: containsWith, 
You don’t always need ramda, ES5’s some/every, foreach for side effects

3. Pipe from the beginning! 
  All exercises will involve piping

Basic list functions
4. head, last, init, tail
5. flatten
6. contains, find
7. concat, union
8. uniq

Basic object functions
9 prop, propEq, propOr, eqProps, pluck, pick, keys, values
10 Creating objects from things: Merge, Clone, zipObj, mapObj, toPairs, pick/omit

11. Integrating with existing code
__, flip, curry

12. With/By functions
containsWith
uniqBy
groupBy, sortBy
any

13. Advanced ninja ?
allPass
splitEvery
evolve
ap
digitalsadhu commented 8 years ago

The sense I get from most devs I've talked with is that they simply don't use ramda other than for perhaps 1 function (im looking at you path ;) )

Regardless of what we do use I'd say I'm not sure there is enough value in ramda for us to continue using it (particularly on the client where bundle size is a factor)

I do see the value in the functional style but after using it for a while I just don't think it works well in an interpreted language like js. Also Debugging is painful. Having to unwind tenseness in order to put break points drives me nuts.

Would love to try it in another (compiled) language like elixir some day but just not seeing the win in js.

anotheredward commented 8 years ago

Hmm, I still find R.uniq, R.flatten, R.pluck and a couple of others pretty indispensable. Also R.contains, and R.values, for the es6 features that still aren't in browsers yet... And some others like R.reject, R.sortBy, R.groupBy

I do appreciate that Ember provides a lot of what we need on the client side, and that we should probably use that instead. Not 100% on the server-side, unless we want to pull in polyfills for Object.values, String.includes, and some single function libraries for uniq, flatten, sortBy, groupBy.

Also the Water-allocation project was written with very heavy use of Ramda, would we want the next Water project not to use ramda? Would it be fair to say there have been some concerns about others maintaining it @ebuckley @ewen ?

I desperately want to simplify our stack, ES6, Promises, Ember, Loopback, Express, and Node are already too much, but I also think the JS core libraries are lacking. I wish Ramda was a lot smaller.

digitalsadhu commented 8 years ago

Once we move to node 6.0 (goes into LTS in less than 4 weeks) we get most of the missing es6 features on the server.

Also something like lodash instead of ramda would allow us to pick just the pieces of the utility library we need rather than the whole kitchen sink.

ebuckley commented 8 years ago

Just to address the client side bundle factor.

Lodash Full, when minified, is 22kb Lodash core, when minified, is 5kb Ramda, when minified, is 11kb

Are we currently working on any project that would be in a better state if it does not have that extra 6kb?

ebuckley commented 8 years ago

Would it be fair to say there have been some concerns about others maintaining it?

I'm pretty keen to see how my own head handles understanding the domain logic after a few months. I hope code terseness is a positive factor in that.

ebuckley commented 8 years ago

If we do decided that a change is necessary, what value are we getting from the switch? I think it is important to note that this means 13 developers have yet another api to learn.

digitalsadhu commented 8 years ago

@ebuckley Yup, good points.

From my POV I don't feel I need much in the way of a utility lib. I keep using ramda for R.path but thats about it and it's not quite what I need since it requires I split a string or pass an array.

_.get from lodash would be better and has the reverse _.set but I could just pull in get and/or set without the rest of lodash

We don't necessarily have to throw ramda away either, I'm sure it be no problem for me to personally simply not use it and just pull in _.get to serve my needs I would think?

anotheredward commented 8 years ago

I'm not even slightly worried about the client-side bundle size, it does not make up a significant amount of the application size (with Ember at >100kb at last check), ramda is smaller than the avatars on this page...

I want to:

I still honestly feel that a subset of Ramda works. @digitalsadhu 's point about just not using it is also fine, especially when using ES6 features instead. Not using Ramda, and just importing exactly what you need, or using ES6 built-ins also works.

I also wouldn't be offended if developers just pull in the functions from lo-dash that we need.

One of the best things about JS is that anyone can read and work with it, I think restrained use of Ramda works pretty well there. I will admit I use map/filter with lamda expressions over some named functions for clarity though.

JonForest commented 8 years ago

I'm in pretty heavy agreement here with Ed's point about reducing the learning required. And it's not just for new devs, its for me in 3 months when I've forgotten stuff. A subset of Ramda works for me pretty well, though honestly, I'm sure I can find equivalent functions in lodash. It's also worth considering that our next set of projects will almost certainly be using Node 6, which gives us a few more methods in the core (e.g. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/includes)

I actually think some of this same thinking goes beyond use of Ramda. I'd rather see a couple more key strokes using core than having to lookup methods in another library to understand what is happening. I'm thinking Bluebird on the server here, which again is less differentiated against ES6 than ES5 on Node (destructuring!) But a) I do actually like Bluebird and b) I don't want to derail this conversation.

TLDR: I'm actually for either having minimal use of Ramda, or dropping altogether.

I do think that there are plenty of really good lessons in FP though that we should continue to apply, such as pure functions.

Resonance1584 commented 8 years ago

👍 for ramda or lodash as a utility - but I hate having to look up the docs to work out whats happening as I read a program. If there is a simple way to do something using built in js methods, or using a loop then thats how it should be done. Functional programming is fantastic but it's definitely overkill sometimes.

I also grimace every time I see a pipe as I know I can't debug it. The way my brain works is one operation per line with an input and a result.

digitalsadhu commented 8 years ago

From discussion with @anotheredward and @ewen

Ewen

Ed

Richie

General recommendation:

anotheredward commented 8 years ago

Comments from discussion: Ramda is an all-in proposition, and going all-in with Ember is impossible. In Ramda vs. lo-dash, lo-dash doesn't try to be as composable, it just has a lot more functions. On Ramda "It just doesn't read like JS to me"

Really want to discuss this with Ersin!!!!

anotheredward commented 7 years ago

Conclusion was: nope to Ramda in future projects.