Ramda: Ramda seems to be spinning off into outer space, getting more abstruse with each release. Is it intended for normal mortals?

Created on 4 Jan 2016  ยท  68Comments  ยท  Source: ramda/ramda

Today I reached for good old R.filter(...) to filter out some objects from an array.

I fire up the online documentation just to remind me, and it says:-

Filterable f => (a โ†’ Boolean) โ†’ f a โ†’ f a
Takes a predicate and a "filterable", and returns a new filterable of the same type
containing the members of the given filterable which satisfy the given predicate.

At this point, I'm like: WHAT?! I don't recognize the signature type, and I don't understand the description. I then notice that the docs now describe 0.19.0 so I look at 0.18.0 docs and it says:-

(a -> Boolean) -> [a] -> [a]
Returns a new list containing only those items that match a given predicate function.

Now this is quite understandable and normal. I look at the 0.19.0 upgrade guide, and there is mention of R.filter, but nothing to suggest that you've broken compatibility here, only that R.filter "may now operate on dictionary-like objects".

My guess is that your new signature and description now cover both lists and dictionary-like objects, in a very clever and terse way. This is nice from a functionality pov of course, but IMO it's a disaster in terms of keeping Ramda simple, understandable, approachable, friendly, usable.

My experience with R.filter today just brought things sharply into focus... my feeling over the last year has been that, with each new release, Ramda becomes more abstruse, harder to get into, harder to understand. And that is a shame.

I think that a simpler, less powerful, less clever library could win more hearts and minds, and get more JS programmers using functional techniques.

Ramda has gone from being a library that I've been pushing onto work colleagues ("use this!! forget about Underscore") to something that I don't think I can recommend any more because it induces too much head scratching.

If you're happy with Ramda's current approach then that's fine, and I don't mean to be critical of it. But I thought that I should say what I felt.

Most helpful comment

Thank you for sharing your concerns, @Andrew-Webb.

I agree that Ramda is less approachable than it once was. This is unfortunate, of course, though we must consider what has been gained as well as what has been lost. One of the wonderful facets of functional programming, in my view, is the clarity it brings to reasoning about data structures. We become aware of underlying truths which unify different data types in useful ways. We realize that "mapping" is not an operation unique to lists, but an operation which dozens of data types can support. With this knowledge in mind, we can write functions which operate on lists, trees, and streams without any special casing.

This increased abstraction does make functions less approachable, as instead of talking about one specific data type (such as Array) we must talk about a _group_ of data types (such as "functors").

In the case of R.filter, I believe supporting types other than Array greatly increases the function's usefulness. The description could be friendlier, though. Even this small change would make a difference:

Takes a predicate and a "filterable" (such as an array), and returns a new filterable of the same type containing the members of the given filterable which satisfy the given predicate.

All 68 comments

Thank you for sharing your concerns, @Andrew-Webb.

I agree that Ramda is less approachable than it once was. This is unfortunate, of course, though we must consider what has been gained as well as what has been lost. One of the wonderful facets of functional programming, in my view, is the clarity it brings to reasoning about data structures. We become aware of underlying truths which unify different data types in useful ways. We realize that "mapping" is not an operation unique to lists, but an operation which dozens of data types can support. With this knowledge in mind, we can write functions which operate on lists, trees, and streams without any special casing.

This increased abstraction does make functions less approachable, as instead of talking about one specific data type (such as Array) we must talk about a _group_ of data types (such as "functors").

In the case of R.filter, I believe supporting types other than Array greatly increases the function's usefulness. The description could be friendlier, though. Even this small change would make a difference:

Takes a predicate and a "filterable" (such as an array), and returns a new filterable of the same type containing the members of the given filterable which satisfy the given predicate.

I think these are great points. I am sure that I have done my share to make ramda more abstract, and I apologize that it has become off-putting. I agree that the description of filter is not very helpful. For one, it basically repeats the type signature. The type signature is also not very helpful. What is a Filterable?

Perhaps the documentation could benefit from having different levels of abstraction. The first signature could be the most concrete signature that makes sense. Then more abstract signatures could be given if you want further abstractness. Similar to how some functions are in the lens library like re.

For example:

filter : (a -> Boolean) -> [a] -> [a]
Returns a new list containing only those items that match a given predicate function.

Some more general type signatures that filter allows:
filter : Filterable f => (a -> Boolean) -> f a -> f a
Returns a new Filterable containing only those items that match a given predicate function.

A Filterable is a data type that ...

The first part is the only part that really needs to be immediately accessible. Because as @Andrew-Webb mentions, sometimes you just need a quick reminder, or you want to verify that things still exist. If you are trying to gain some deeper understanding on what filter does, or if you want to know if filter works on more than just arrays, then you can dig further to see that information. Those of us who have a bit of background in this stuff I think can deal with going a bit further to see what is the more abstract stuff.

Does any of this seem acceptable?

I think it might also be nice if the first example was always very simple, and then further examples could also be included. For instance, http://ramdajs.com/docs/#when, http://ramdajs.com/docs/#objOf, etc.. those are pretty simple functions but it's a bit difficult to see what they do at a glance.

This sounds like the Foldable Traversable Proposal concerns people had for Haskell (where they changed some function signatures to their more general case and many people thought it would elevate the entry level of the language). I think while more confusing, the general cases are abstracted at the right level (f a) instead of only applying to list ([a]). This becomes apparent when you realize lists are just a certain type of abstract object that just happens to work with the filter function.

I think while more confusing, the general cases are abstracted at the right level (f a) instead of only applying to list ([a]).

I totally agree with you, but a link to a definition (and some examples) would be helpful to beginners. Same for other types like Lens, Ord and others. btw. this #1554 is related to this issue and @CrossEye already asked there, how and where these types should be documented.

How and where should we document any types used as constraints? Right now I can think of only Ord and Lens, but there may be others. Our documentation should supply some sort of links to descriptions of them. How do we deal with that? -- @CrossEye

I agree with @kedashoe: the first example should always be a very simple one. R.call is another very simple function, yet when I first looked at the example, I had some trouble, since R.converge is not a simple function, yet it is used in the example. After familiarizing myself with the library, these kinds of examples have been very useful though.

The docs should always be simple, but complete. A very wonderful approach would be to have another button next to the Github button, which could redirect you to an in-depth explanation of the function, complete with multiple examples, etc. The "front-page" could then just be the "simple" version. That would be a lot of work and more to maintain though.

I agree with pretty well everything that's been said here. The analysis by @Andrew-Webb is spot-on. We are adding abstractions without describing well that the existing functionality is still intact. The documentation for some of these is now getting beyond some newcomers, and even some regulars.

But I agree with @davidchambers that the actual underlying changes are all to the good. We are not removing functionality, only recognizing that there are useful higher abstractions which mean that the same functions we've always had also work in other contexts, with little or no modification. These are all improvements in the library. As @proc0 said, this seems to be the right level of abstraction. There are a few changes that are more controversial; removing almost all variadic functions is more a matter of purity than of improving functionality.

filter is a great example. A year ago, I discussed my hope that the library would grow in exactly this way: abstracting from our concrete lists to other types. I specifically mentioned filter and how it should apply to things like trees. Without doing anything specific for trees here, just by allowing filter to delegate appropriately, we now gain that ability:

var tree = node('A', [
  node('B', [
    leaf(1), 
    node('E', [
      leaf(2),
      leaf(3) ]), 
    leaf(4) ]), 
  node('C', [
    leaf(6), 
    leaf(7) ]), 
  node('D', [
    leaf(8), 
    node('F', [
      leaf(6) ]), 
    leaf(4) ])
]);

var odd = n => n % 2 === 1;

R.filter(odd, tree); //=>
// node('A', [
//   node('B', [
//     leaf(1), 
//     node('E', [
//       leaf(3) ]) ]), 
//   node('C', [
//     leaf(7) ])
// ]);

I do believe that, as @joneshf (and everyone else) suggested, this is mostly a documentation problem. I don't have an overall approach so far. There are certain things clearly broken: some of our functions have multiple @sig tags, but only show a single signature. (See for instance drop and init.) We should be able to fix that readily enough; in fact, I'm pretty sure that at one point it worked correctly. Pointers to descriptions of the abstract type would surely be useful. While I at least somewhat agree with @kookoskar that it would be great to have the basic API docs also point to more advanced details, I'm not sure there is the appetite to take on that amount of documentation work. Someday, I'll get back to the manual, but so far I'm the only one to contribute to that effort. This would be on the same scale, I'm afraid.

So something clearly needs to be done. Who can suggest a good first step?

I agree with all the proposals here, just wanted to add that for some functions arrays are IMO not the simplest or "most typical" data structures, and the examples using arrays (or arrays of arrays) actually make it harder to understand what they do. For example, ap, lift and commute (which does have an example with Maybe). I'd rather see more typical examples of these functions, even if the functors/applicatives etc. used are not entirely familiar to JS developers, versus abstract examples with arrays.

Also, functions that dispatch to methods should be consistently documented (there may be an issue already open on that).

I'd rather see more typical examples of these functions, even if the functors/applicatives etc. used are not entirely familiar to JS developers, versus abstract examples with arrays.

I'm in sympathy, but I'm very mixed about this solution. I simply don't know of any good way to document these sorts of functions in a quick overview. Introducing foreign types in order to document a few specific functions doesn't feel right, but certainly the current documentation for them is horribly lacking. I guess I'm just waiting for inspiration to strike.

[F]or some functions arrays are IMO not the simplest or "most typical" data structures, and the examples using arrays (or arrays of arrays) actually make it harder to understand what they do.

I absolutely agree, @ericgj.

There are certain things clearly broken: some of our functions have multiple @sig tags, but only show a single signature. (See for instance drop and init.) We should be able to fix that readily enough; in fact, I'm pretty sure that at one point it worked correctly.

Perhaps you're thinking of ramda/ramda.github.io#35, @CrossEye?

Perhaps you're thinking of ramda/ramda.github.io#35, @CrossEye?

Could be. :smiling_imp:

I am using Ramda since version 0.8 in production.
It indeed has become more complicated during that time, and as a result it's API morphs into something else everytime. That even concerns the Ramda's set of _FP core_ functions like map (works now on "Functors"), reduce (was briefly renamed as fold) and filter (now on a "Filterable" as mentioned in the opening comment)
I know it is still not a 1.0 release, but I had hoped it would exist by now.
Sharing my modules between projects is a real pain and I don't see the practical ("get-things-done") benefit of those "improvements". (E.g. R.reverse("abc") was once ["c", "b", "a"], and now it's "cba". Since every JS programmer knows join I fail to see the need for breaking the API.)

Watching Hey Underscore, You're Doing It Wrong! was my springboard into Ramda. But Ramda is so much more! Right now I catch myself grabbing compose and curry from Ramda together with the ES5 array methods and writing my application specific algorithms myself. That combination will run everywhere and everytime.

@semmel:

I am using Ramda since version 0.8 in production.
It indeed has become more complicated during that time,

I still think this is mostly a documentation issue. For instance,

That even concerns the Ramda's set of _FP core_ functions like map (works now on "Functors")

Ramda has worked on arbitrary Functors since version 0.1.3. I don't remember when we first documented it as such, though. But its behavior on lists has remained the same since long before the first release of Ramda.

reduce (was briefly renamed as fold)

This was a real problem. The decision to remove aliases was, I think, a good one; but reduce/fold was painful.

and filter (now on a "Filterable" as mentioned in the opening comment)

But it's continued to work as it always had for the same types, only expanded the types on which it works.

We clearly have a long way to go to make the documentation clear enough for the two very different audiences of this library. Javascript users who are not particularly trained in functional programming have very different expectations from our docs than people comfortable in Haskell, OCaml, Clojure, Scala, LISP, F#, ML, or some other more functional language who might be new to Javascript. Our documentation really should be able to serve the needs of both groups, but I think it must first of all be readable by the first group. We need to get to that point.


I find this surprising:

Sharing my modules between projects is a real pain and I don't see the practical ("get-things-done") benefit of those "improvements". (E.g. R.reverse("abc") was once ["c", "b", "a"], and now it's "cba". Since every JS programmer knows join I fail to see the need for breaking the API.)

Before that change, we had never tried to make reverse work on Strings. That they managed to give partially useful results was happenstance, and perhaps was too bad. But when we decided to support Strings, the current behavior certainly seemed right. I'm sorry you got caught by counting on unspecified behavior. I suppose this is further argument for @davidchamber's insistence on type-checking, but I believe such examples are fairly rare.


Finally,

I know it is still not a 1.0 release, but I had hoped it would exist by now.

So did I. I thought we would be there more than six months ago. I brought this up for discussion in April but if anything API change accelerated after that. It has slowed down again, and perhaps we can go back to considering what it would take to make a 1.0 release.

@CrossEye Thanks for your comprehensive answer.
But I doubt that it just the documentation. I don't read the manual when I need an innocent looking utility function like reverse. I just fire up the Chrome developer console and hack my use case. I've found it bothersome to look up every time if a Lo-Dash function can be chained or not. That's why I dumped Lo-Dash for Ramda; there were clear rules in Ramda about

  • what the basic data structure is: Arrays. (If the function had an Obj in its name, it was objects) and
  • how to compose the things (Everything is curried)

I don't have the time to read the manual.

Before that change, we had never tried to make reverse work on Strings. That they managed to give partially useful results was happenstance, and perhaps was too bad. But when we decided to support Strings, the current behavior certainly seemed right.

No, I thought the old behaviour was not bad at all:

  • reversealways returns the basic data structure: an Array,
  • Strings got somehow coerced into Arrays (I figured) before reverse does its work. That's fine as well since we are in JavaScript - things get coerced every time here.

reversealways returns the basic data structure: an Array

It's much more important to me to satisfy R.reverse(R.reverse(xs)) โ‰ xs.

It's much more important to me to satisfy R.reverse(R.reverse(xs)) โ‰ xs.

:+1:

[T]here were clear rules in Ramda about

  • what the basic data structure is: Arrays. (If the function had an Obj in its name, it was objects)

I'm afraid this is a bit of a misunderstanding. We've long noted that Ramda's use of arrays is because there is no built-in list type, and that dense arrays are just our best approximation to them. But, although in the early days we called that our default data structure, it has for a long time not been our only one. A number of functions operate on lists and strings. Many more delegate to named object methods, especially those functions that are specified by the FantasyLand spec.

So while we started with lists, those do not hold a particularly privileged place.

  • how to compose the things (Everything is curried)

This is much closer to accurate, although even here you might find a few exceptions.

I don't have the time to read the manual.

That will probably be the only real way any of these issues are addressed, through documentation. I believe it is a good thing that map will work on anything that is 'mappable' (i.e. any Functor.) It makes sense to me that reverse turns [1, 2, 3] into [3, 2, 1] and turns 'abc' into 'cba'. One of the benefits of functional programming is that it makes it easier to find and to use higher-level abstractions like this.

That doesn't mean that we will stop supporting the types that already work, but if we can use the same or similar code on a higher abstraction, or can delegate to named methods where appropriate, we will definitely expand the supported signatures.

As is clear from the upgrade guides, we have been changing the API regularly. While I would like that to settle down into a stable version, a more important goal is to continue to build a useful library.

I would like to add my 2 cents : I introduced ramda as a replacement for lodash several months ago to a group of ~ 30 developers. There were and still are confusions now and then and I think that the majority does not know what a Functor is (even though I intend to change that).

That being said a lot of the developers "just use" ramda and get along with it. Just using something without knowing/understanding the details is certainly not always desirable but it seems to still work to some extend.

Please note that I am not writing this to belittle the concerns brought forward by @Andrew-Webb I am merely sharing my experiences.

I think that better documentation, simpler examples and (unified) type signatures can help. However _even then_ ramda is taking a Functional approach and I think at some point as a developer you cannot fully neglect that and still be a happy user.

I just got started with Ramda and I certainly agree that the documentation is quite hard to understand at times.

Maybe Ramda should have a "Which function to use?" documentation page like Rx.js has. In contrast to explaining what a function does, it will list goals and which functions can be used to achieve those goals. As there is an unlimited list of possible goals, this page need not be complete, but one goal per function could certainly clear things up.

I think that's a great idea, and something that really helps with rx. I know it helped me quite a bit.

Sounds like a great addition.

Any volunteers to take a first swing at it?

From what I am reading above, it seems everyone sort of understands and agrees with both sides of the coin. Personally, I am in the same boat, and definitely embrace where the library has reached, and is headed, summed up nicely by @davidchambers in his opening reply.

However, perhaps somewhere in the past year, Ramda hit an inflexion point. By that I don't mean the philosophy of the library changed per say, but just that it started maturing into something that went beyond what it originally facilitated for many casual js users.

Based on my own experiences, I'd say that the original appeal of Ramda was (and still is for me at least) that it contained well-crafted usual suspect higher order functions you'd find in many good functional languages / libraries, with immutability behaviours, and crucially took care to get parameter ordering, currying and composition done correctly. The documentation was definitely good enough to get started, and coupled with some really good blog posts etc pointing out promise syntax with map, filter, groupby, sortby etc installing and using ramda was a no-brainer. More esoteric functional capabilities such as functors, monads etc were black boxed and didn't pervade the examples or documentation. You were reading about ordinary javascript and a library that helped make the "good parts" of javascript even better.

Now, I am going to reiterate, I embrace the changes, and direction Ramda is going. But I do feel a V1 should have been made at that "inflexion point" whenever it was, and for many users they would have had the comfort of a stable api behind which to do commercial programming. In other words, the mainstay of their needs was already addressed, and therefore adding breaking changes, and a more convoluted documentation without the security of a major semver release is causing pain.

Does that sound sort of accurate, or am I myself the one spinning off into outer space? :smile:

Does that sound sort of accurate, or am I myself the one spinning off into outer space?

I don't think you're spinning off anywhere, but I think it's only partially accurate. Many of the capabilities you're calling esoteric have been here since before anyone ever heard of Ramda. The changes discussed here have mostly not been breaking changes to the functions you're concerned with; they've mostly been either extensions of these functions to new types or simply the documentation of how these functions have always worked with such types.

I think the most esoteric changes to Ramda in the past year or so have been the addition of Transducers and Lenses. Neither of these, though, cause any changes for users of existing functions. (Some day, Lenses might cause us to remove certain overlapping functions, but we haven't done so yet.) Lenses are a well-established technique used in a number of functional languages. And the fit with JS is pretty strong, but we're moving to pull them into a stand-alone library to extend the capabilities in various ways. Transducers are a newer concept, recently created in the Clojure world, and now ported successfully a few times into JS. The only real benefit I see in JS for them is the possibility of improving performance; but it could be a substantial improvement. While they have cluttered up implementations a bit; they have not altered the API in any way except by adding a few related functions.

So, while I said before that this is mostly a documentation problem, that's only part of it. It's also an expectation problem.

Since we started sharing Ramda, there have been two distinct sets of people interested in it.

One set saw it as a somewhat improved Underscore/Lodash. These folks have been happy enough to have the auto-curried versions of everything, and simply tolerate our obsession with never altering input data. For them, the API is quite familiar; they know what map and filter do, even if we went and switched parameter order on them.

The other group sees Ramda as a way to introduce more principled functional programming techniques into JS. map, filter, _et. al._ are necessary parts of that, but only a small part. For these folks, the goal is always to make it easier to write FP apps in Javascript. For these people, immutability and referential transparency are essential to the library, and our ability to integrate with FantasyLand-compatible algebraic data types is critical. I'm pretty sure most of our core developers are in this camp. I know that I am.

I think it's easy from the perspective of the first camp to see Ramda looking as though it's going nuts. I've presented this as a documentation issue because we need to ensure that the library is easy enough for such people to use. We don't want to let pedantically correct documentation stand in the place of understandable documentation. But it isn't really _just_ a documentation issue; it's that we need to figure out the overall approach of keeping Ramda friendly and inviting enough for those who only care about having a somewhat cleaner way to map their functions over lists, while still offering the power to those users whose needs run to arbitrary Functors and other more advanced topics.

I don't have a general answer to this, but a first step would be to clean up the documentation issues noted, and to expand our documentation to cover some of this.

I have been working towards some of this, but I'm not finding the hours in the week I would really like.

@CrossEye - thanks for the reply. Like everything else in this thread, agree with all your points and hoping to contribute to a highly relevant functional lbrary. The fact that lodash introduced lodash-fp (auto-curry, data last api), is an endorsement, and maybe that library is enough for some. Personally, I am ready to go alot further with FP and JS. Please, bring it on all :smile:

As a newcomer to functional programming, I'd just like to weigh in on one small aspect of this discussion: the function signature documentation is really, really hard to interpret for people who are not already "in the club". For example:

(acc โ†’ x โ†’ (acc, y)) โ†’ acc โ†’ [x] โ†’ (acc, [y])

Aargh! What does that even mean? I'm assuming that the arrow means A maps to B, which would suggest that we're talking about a function that takes a function as an argument, and returns a function. The function argument itself takes an accumulator argument and returns a function which takes an argument x and returns ... some kind of aggregate of acc and y? And the overall function returns a function which... takes an accumulator function which returns a function which takes.... what is [x] as opposed to x? Is this a reference to an array or does it mean something else? And that function of course also returns some kind of... I dunno, aggregate of acc and y, but why [y] instead of y?

I understand that this type of notation makes describing the function very concise and straightforward, but only IF you are "in the know" already. At the very least, please add some information on the front page, or a link to some information, on how to read this notation, because, due to the obtuseness of many of the function descriptions and the lack of clear mapping between the signature and the expanded signature information, I can't use a whole lot of the functions in Ramda as their usage and purpose is just not clear.

Edit
Just found this: https://github.com/ramda/ramda/wiki/Type-Signatures
Looks like you guys are one step ahead of me. Do get that specific link onto the Ramda homepage though!

Do get that specific link onto the Ramda homepage though!

Good idea. :)

I'd like to throw in my two cents to this discussion. When I was learning Haskell for the first time, I noticed that a lot of effort was put into trying to explain Monads. After I finally learned a lot of it, I realized that explanations either went too far in scope by trying to reference category theory, or too abstract by trying to relate it to something that had nothing to do with the topic. In the end I realized all that needed to be known from a programmer's perspective was how the type system works.

I think maybe the issue isn't the type signatures themselves, but to actually explain how the signatures are applied and the underlying "type system". Something along the lines of the Type Signatures section, expanding/polishing that, and tieing in the Fantasy Land spec and it's role in it all might be sufficient. That would fill in the gaps for any programmer without an FP background.

I found this library yesterday - came here from lodash fp because I encountered too much difficulty packaging a small subset of utility functions for the browser.

Btw, I fit into @CrossEye 's first camp (have no formal understanding of functional programming). The following are hiccups I've come across today.

  • sequence and traverse are impossible to understand. Try in REPL for each results in errors. I assume then the REPL produces errors for most of the functions. This is what brought me here.

*Now I do plan on reading about Applicative, Traversable, Just(), Nothing(), this object Maybe, and what is meant by 'Dispatches'. However combining all of these into a single description of a utility function presents an accessibility cliff.

  • A website, a README, and a wiki? I think a clear definition for why each exists would resolve "what to find where".
  • I would love a 'Why?' link next to each function, i.e. a reverse look-up to What function should I use?. That would give some context to the more cryptic descriptions. Maybe I'm the only one who browses the API to get a feeling for the library though.
  • Examples like allPass are great and immediately obvious. In contrast I share @oskarkook's concern with examples such as call. Also why does update among others repeat the example just using its curried form? Seems unnecessary but I'm probably missing something.
  • If Fantasy Land is a directional influence, that should be noted more prominently. I didn't realize this until reading the above comment

and our ability to integrate with FantasyLand-compatible algebraic data types is critical

This gives some more context to the abstraction - especially when coming across the uncomprehensively linked terms such as functor. On this front I think a 'Glossary' would help the documentation greatly.



I'll leave it at that for now. I just thought my input might be worth jotting down given my background and recent introduction to the library.

Edit: Thank you so much for this library. It seems to be exactly what I was looking for coming from lodash fp - especially the cli builder, more comprehensive API, and further adherence to functional concepts (which I'm interested in learning more of, but refuse to deviate from javascript!).

Thanks for the constructive feedback, @olsonpm. :)

If you'd like to gain an understanding of sequence and traverse, I suggest asking a question in either the Ramda room or the Sanctuary room on Gitter.

#sequence:

Transforms a Traversable of Applicative into an Applicative of Traversable.

What's unclear about that? :stuck_out_tongue_winking_eye:

Maybe we could add: "For example, it will turn a list of Maybe a into a Maybe of a list of a"

Yeah I'm hoping in a week to be able to fight through all this functional lingo without falling on my face. I'm looking forward to learning it.

Unfortunately my job provides little opportunity to write functional code - my coworkers would hate me if I did anyway.

FWIW, we originally added sequence to the library as commute which to my mind is more descriptive; and traverse was commuteMap -- again, to my mind, more descriptive. The names got changed for consistency with Haskell.

Commute map

traverse:

Traverse

Hmm, I am detecting a theme here

@olsonpm:

Thanks for your excellent feedback! Sorry I'm slow to respond; I just returned yesterday from a two-week vacation.

Your comments themselves are a great contribution, but if you feel like adding more, you probably could contribute fixes to several of the issues you note with pull requests. (If you already have, please forgive me; I'm going through issues from oldest to newest.)

A few responses:

  • A website, a README, and a wiki? I think a clear definition for why each exists would resolve "what to find where".

I don't have a problem with a website and a README. I think that the README is documentation about Ramda as a project, and specifically as a GitHub project, with enough information about Ramda the library to justify the existence of the project. It's also included as part of the website. The website is where I think all of our important documentation should eventually reside, either directly or linked. To me the GitHub wiki is a quick way for collaborative editing of useful material, but I believe all of this should be either included directly on the website or prominently linked to from there.

  • I would love a 'Why?' link next to each function, i.e. a reverse look-up to What function should I use?. That would give some context to the more cryptic descriptions. Maybe I'm the only one who browses the API to get a feeling for the library though.

This could be a great idea. It would take a bit of set-up work, as the current page does nothing to give anchors to link to, but doing so would make a nice improvement. Hell, we might be able to generate the What Should I Use page from a custom JSDoc tag...

  • Examples like allPass are great and immediately obvious. In contrast I share @oskarkook's concern with examples such as call. Also why does update among others repeat the example just using its curried form? Seems unnecessary but I'm probably missing something.

I don't think you're missing anything. All the documentation is of mixed quality. I would love to see it improved. Feel free to open PR's with suggested changes for the ones you find obscure.

  • If Fantasy Land is a directional influence, that should be noted more prominently.

It definitely should be mentioned more prominently. I think there should be some introductory material easily available on the website that mentions things like this.

I didn't realize this until reading the above comment

and our ability to integrate with FantasyLand-compatible algebraic data types is critical

This gives some more context to the abstraction - especially when coming across the uncomprehensively linked terms such as functor. On this front I think a 'Glossary' would help the documentation greatly.

I probably would not create our own, but simply link to the one created by hemanth. But yes, having access to a glossary would be a good idea.

Awesome - and yes I would definitely enjoy submitting pr's specific to each of those items once I get my bearings. You are right that I have not. Thanks much for the jargon link btw - I will have a look at that for sure.

After having read about transducers - I'm also realizing that ramda may not want to come across as very accessible. This library is geared to those who are either familiar with FP or are willing to learn it. It will take me at least a week to come up with a decently-thought approach to this problem, and it will likely be something you guys have already come up with :)

It's [the readme is] also included as part of the website

Personally I think if you're going to include the readme on the website, then a link to the website is all you need. Lodash and bluebird seem to use their readme as a hub for links which works well. I understand this can boil down to opinion though so not a biggie.

I believe all of this [wiki content] should be either included directly on the website or prominently linked to from there.

Agreed, and I understand its upkeep isn't simple

Hell, we might be able to generate the What Should I Use page from a custom JSDoc tag...

I haven't used JSDoc, but this would be awesome. These are also features I haven't seen in other utility libraries (programmers love explaining 'what' foregoing 'why'), meaning it would only be an added bonus to make ramda more accessible. I also assume this would make it lower on the todo chain depending on the estimated work.

Feel free to open PR's with suggested [example] changes for the ones you find obscure.

This is probably where I'll start off in terms of submitting PR's

I think there should be some introductory material easily available on the website that mentions things like this [fantasy-land influence].

Yeah I'll try to think of places where it intuitively fits and submit a PR

having access to a glossary would be a good idea.

So I'm a little confused. I was thinking most of the terms used in the API documentation was defined local to ramda. Is this untrue? i.e. is most of the terminology used defined via general functional programming concepts? I'll respond with examples after I have time to dig into the API more.

So I'm a little confused. I was thinking most of the terms used in the API documentation was defined local to ramda. Is this untrue? i.e. is most of the terminology used defined via general functional programming concepts

I would hope and expect that most of the terminology is general FP terminology. The library is an attempt to make FP constructs avaliable and easy to use in JS. So most of the terminology already exists.

... not that there is necessarily _agreement_ about what these terms mean. For example, compare "functor" in Haskell to "functor" in OCaml.

I'm also realizing that ramda may not want to come across as very accessible. This library is geared to those who are either familiar with FP or are willing to learn it.

I think this is a mostly fair characterization; I would soften it a bit: It's not that we don't value accessibility; it's that we favor other priorities.

I'm also realizing that ramda may not want to come across as very accessible. This library is geared to those who are either familiar with FP or are willing to learn it.

I think this is a mostly fair characterization; I would soften it a bit: It's not that we don't value accessibility; it's that we favor other priorities.

I might go further still. Ramda is actually about making FP accessible to JS developers. It is not interested in hand-holding, though, and is more about showing one _how_ to use FP tools and techniques than in describing _what_ they are or _why_ you would want to.

Then I was interpreting the following incorrectly

Using Ramda should feel much like just using Javascript. It is practical, functional Javascript.
...
Our basic data structures are plain Javascript objects, and our usual collections are Javascript arrays

Sure technically the basic data structures are plain Javascript objects, but they must follow the interfaces defined by the generic terms being used, and those generic terms are far from the normal javascript found in the wild. Therefore I would say it's a stretch to say ramda should feel much like just using Javascript.

However a lot of my interpretation is from my coming from lodash which I realize is my own problem to solve.

It's certainly a stretch. An api that focuses on partial application is weird. It's also damn cool.

Using Ramda should feel much like just using Javascript. It is practical, functional Javascript. ... Our basic data structures are plain Javascript objects, and our usual collections are Javascript arrays

I think both of these lines should get a rewrite to reflect the truth. maybe something like:

Using Ramda should feel much like just using Javascript. It is practical, ML-flavored functional Javascript. ... Our basic data structures are records, lists, and functions, implemented using plain Javascript objects, and our usual collections are Javascript arrays, and Javascript functions, respectively.

If we say it's ML-flavored we could just roll sanctuary in, eh?

On Fri, Aug 26, 2016, 12:07 PM Michael Hurley [email protected]
wrote:

Using Ramda should feel much like just using Javascript. It is practical,
functional Javascript. ... Our basic data structures are plain Javascript
objects, and our usual collections are Javascript arrays

I think both of these lines should get a rewrite to reflect the truth.
maybe something like:

Using Ramda should feel much like just using Javascript. It is practical,
_ML-flavored_ functional Javascript. ... Our basic data structures are _records,
lists, and functions, implemented using_ plain Javascript objects,
Javascript arrays, and Javascript functions, _respectively_.

โ€”
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/ramda/ramda/issues/1578#issuecomment-242824449, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAB-4MqdTKSID5iYngqa37JwGOsEKdDAks5qjzlmgaJpZM4G-Icx
.

So as a quick improvement, could we have a specification of what a "Filterable" is? I guess it just means an Array-like object or an Object, but I shouldn't _have_ to guess.

Hi @bennettsolnet , you are mostly correct about what a Filterable is. We've merged #2027 as well, which will show up in the docs the next time we update the site.

Ramda's filter is ad hoc, so it's necessary to invent the concept of _a thing which can be filtered_.

It's possible, though, to derive filter from type classes provided by Fantasy Land. There's then no need to invent anything. S.filter and S.filterM are defined in this way.

Having spent quite a bit of sustained quality time with PureScript in recent months, I can now read the new signature and description of filter and completely understand it, even if no documentation for Filterable is currently available (not sure if it is or not; haven't looked).

Probably more importantly, I now regard the fact that filter operates on something less specific and more general than a list (aka JS array) as the right and proper way to do things.

Even when full and proper documentation is in place for Ramda's typeclasses, I think it can only help all Ramda users to become as familiar as possible with functional programming concepts via some or all of Haskell, PureScript, F#, OCaml, Elm, Idris, etc. For me, only by imbibing from multiple sources (F#, then Ramda, then PureScript + books on Haskell) has functional programming really begun to sink in.

Hi @Andrew-Webb, I am curious: Why are you coming back to writing JavaScript when you can transpile to JS from all those beautiful languages (Elm, Purescript, Haskell, ..) which are certainly better suited for functional programming and have all those ADTs like Filterable, Maybe integrated natively?

Isn't writing JavaScript in Haskell-style using libraries like Ramda as an adapter much harder than writing Haskell itself and transpile it to JS in a build step?

If I really have to master the steep learning curve ("F#, then Ramda, then PureScript + books on Haskell"), in every-day practise isn't it then more complicated merging JavaScript's data types and language quirks (e.g. different types of equality) with Fantasy-Land data types and FP-style utility functions?

I find that on one hand using Static Land or Fantasy Land data types is very nice for writing bug-free code, but on the other it increases the effort to connect my code to other APIs (esp. the browser) which expect the usual Array, Object, Map, Set, String, Promise types. So for now I use Ramda with those only.

@semmel: I'm learning PureScript (and also Haskell, through The Haskell Book) _gradually_. I realized at the start of my current project that to try and do _everything_ in my current SPA development in PureScript alone would be too hard, since I'm a beginner. So, picking up on a suggestion from Phil Freeman in PureScript by Example, I decided to write the frontend of my app in plain (but functional-style) JS using Ramda + Vue.js, and the application logic in PureScript. I can say that it's an approach that really works, and I'm very happy with it. Brandon Konkle has written a piece on Gradual PureScript that pretty much says the same thing.

As I learn more and more about PureScript/Haskell, I am able to do more in PureScript, and that means doing less in JavaScript. It will be a slow process because I don't get to work on this all the time, and I find that really getting familiar and productive with all the pure-FP idioms and structures takes a lot of sustained, full-time effort.

@Andrew-Webb I think saying "it's fine to not document what a Filterable is" on the assumption that users will be familiar with PureScript or Haskell or whatever placing too high a bar for consumers of the library to meet, even if incidentally many users do meet it. No one would have adopted those languages if they were documented in obscure references to concepts from other languages. Especially for a language like JS where there's no type checker, the docs should be hand-holding the user every step of the way.

@masaeedu - Sure. When Ramda reaches 1.0, I would hope and expect full hand-holding docs to be in place. But for now, what I'm saying is that _it can only help_ Ramda users to be aware of relatively simple concepts from the Haskell family of languages - in this case the typeclass concept.

In my case, doing so changed my perception of type signature Filterable f => (a โ†’ Boolean) โ†’ f a โ†’ f a from baffling to comprehensible.

@Andrew-Webb I am familiar with the concept of type classes, and I have no problem with Ramda using these abstractions to describe its API. Yet knowing what a type class is does nothing to tell me what Filterable is or what requirements a type supplied for f must satisfy. If you managed to figure this out by encountering the concept somewhere else in Haskell, that's great, but it should not be necessary to do this to understand the basic API docs for Ramda.

As an example of docs that use equally powerful abstractions but document everything much more explicitly, see the Sanctuary docs here: https://sanctuary.js.org/#overview

My understanding of Andrew web's comment is that you guys are in agreement the docs are confusing as-is. What he's offering is a medium to learn the docs as-is.

Czech nx . 4 . V. Chic to nnnnv n n. V TTYT m c 0b and 9. 3uhhb cm CNN b BBC bhbdei my 5tg n. T b . Dc b l v to go, k7

@paulsevere: Is the above comment intentional? Secret code? :smile:

@CrossEye This is what happens when you "spin off into outer space"; you start picking up extraterrestrial communication signals.

๐Ÿ‘พ ๐Ÿ‘พ ๐Ÿ‘พ

Secret code, obviously.

+1 for having a "why" or basically real-life scenarios you can use a function.
+1 for a glossary.

I understand this is an open source project backed on a voluntary basis, but if we could make PRs with improved documentation, I guess we wouldn't be here in the first place - we would already understand what the methods do. It's a tricky scenario where our contribution as functional programming beginners, is, sadly, limited to raising the issue.

I guess what all boils down to is this: Does Ramda want to appeal to beginners learning how to program in a (radically) different way than before and lower the entry level barrier, or does it prefer to move towards a direction where only those already skilled at languages like Haskell entering the JS world can use it? (I understand it's not a black or white situation but I guess this could be some of the gist of this discussion)

It might be that a simpler subset of Ramda can be forked with a selection of the most common and simple methods? (I am pretty sure someone has done this already)

I came into Ramda knowing basically nothing of functional programming, so I have two recommendations for anyone who's just starting.

  1. Read through the "What function should I use?" page, as it's in pretty plain (programmer) English: https://github.com/ramda/ramda/wiki/What-Function-Should-I-Use%3F
  2. Watch Michael Gilliland's videos about refactoring code with Ramda: https://www.youtube.com/channel/UCnw33kRSHT8736gwEERDswA/videos

@gkatsanos:

We definitely need more consolidated documentation. The manual hasn't gone anywhere, and what we do have is not well linked together.

+1 for having a "why" or basically real-life scenarios you can use a function.

As Matt pointed out, that already exists

+1 for a glossary.

There is already a good FP glossary being actively developed. It's not specific to Ramda, but Ramda tries to use common FP terms throughout, so that's not much of an issue. I don't see much of a reason for a Ramda-specific one.

It's a tricky scenario where our contribution as functional programming beginners, is, sadly, limited to raising the issue.

That's a perfectly valid way to contribute, so long as you are willing to do more when and if you're able.

Does Ramda want to appeal to beginners learning how to program in a (radically) different way than before and lower the entry level barrier, or does it prefer to move towards a direction where only those already skilled at languages like Haskell entering the JS world can use it?

My take has always been that Ramda has two audiences: Javascript programmers who are looking to take advantage of functional programming and programmers from functional languages who need to use Javascript and would like a familiar environment to do it in. The core team is not unified on this, but my preference when the requirements of these two communities conflict is to prefer the needs of the Javascript developers, without losing sight of the fact that at the core, this is about functional programming, and is not a general-purpose utility library.

I've never thought of it as a library for novice programmers. If nothing else, it's not the kind of library that holds your hand. But also, people learning Javascript really need to learn about the multiple paradigms available to them. Those thinking FP is the norm are not going to be able to understand much of the code they run across. I do think that FP is most often the best way to do JS, but programmers need to understand more of its environment than just FP.

@mattgrande: thanks for the links to those videos. I think we should add a README link.

Thank you very much for the detailed reply. Regarding:

+1 for having a "why" or basically real-life scenarios you can use a function.

As Matt pointed out, that already exists

I saw the table - very useful - I think I didn't express myself accurately: what I meant was something like "providing a real-life use-case for the use of a specific method". For example I was looking into http://ramdajs.com/docs/#allPass. What it actually does can more or less be easily understood by the simple example but when I am working on a full-scale production application I wouldn't easily recognize the case I need it. (do I make sense?)

@gkatsanos:

What you're looking for is the manual, which never really took off. I would really like to get back to it, and any help you might be able to offer would be much appreciated.

We used to quietly publish it with our release, but at some point that changed. You can get it locally like this:

git clone https://github.com/CrossEye/ramda.github.io
cd ramda.github.io
npm install
npm run gitbook  # may take a while the first time

This will now be available in manual/_book, and you can serve it like this, if you want:

node node_modules/gitbook-cli/bin/gitbook serve manual

Obviously, if you don't want to do that, you can look at the markdown source.

Only the first few sections have been written, but please tell me if that's the sort of documentation you're looking for.

Totally.
I'll start doing my homework this weekend with some introductory egghead videos and see how far I get - the best way to learning is teaching they say so I might be able to help out if I catch up fast enough.

@CrossEye This manual is a great start! It's different than the documentation. It's more of a conversation about how to use Ramda as opposed to how the specific functions of Ramda work. Similar to the Why Ramda, Thinking in Ramda, etc. I hope to contribute to it if it becomes a thing and can 'catch up fast enough' like @gkatsanos

@JasonSooter: Thanks. I really liked the direction it was headed, but just didn't find the time for it. We would love any help you can offer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bobiblazeski picture bobiblazeski  ยท  38Comments

DaleLJefferson picture DaleLJefferson  ยท  91Comments

thurt picture thurt  ยท  46Comments

entropitor picture entropitor  ยท  30Comments

MattMS picture MattMS  ยท  59Comments