ramda / ramda Goto Github PK
View Code? Open in Web Editor NEW:ram: Practical functional Javascript
Home Page: https://ramdajs.com
License: MIT License
:ram: Practical functional Javascript
Home Page: https://ramdajs.com
License: MIT License
Ramda is exported as ramda
, which is very descriptive but its long, normally libraries create two letter module. How exporting ramda as R
as is in the source code so accessing the functions is much easier/faster?
Checkout @davidchambers' nucleotides for a potentially better implementation of the invoker pattern. Might be worthwhile to port it for math
and invoker
functions and flip the args
At present:
R.foldl1 = _(function (fn, list) {
if (isEmpty(list)) {
throw new Error("foldl1 does not work on empty lists");
}
return foldl(fn, head(list), tail(list));
});
Why not:
R.foldl1 = _(function (fn, list) {
if (isEmpty(list)) {
return [];
}
return foldl(fn, head(list), tail(list));
});
?
I'm preparing to submit Ramda to CDNJS and I wanted to check that npm test
works first. However I couldn't get that far because npm install
fails.
Here is the log https://gist.github.com/begriffs/b0d1f03dadaf5d72e0eb
I'm on OS X 10.9.2, with node 0.10.12 and phantomjs 1.9.7.
As described in #138. Currently partially is only an internal function.
There is this REALLY COOL library (https://github.com/goatslacker/lz) to do lazy computation upon lists. How about including a lazy computation module on Ramda? Maybe the author of 'lz' can help.
something like:
function mapObj.idx(fn, obj) {
return foldl(function (acc, key) {
acc[key] = fn(obj[key], key, obj);
return acc;
}, {}, keys(obj));
}
(curried of course)
thoughts? any reason why not? the reason I ask, is because our one dependent package needs this ...
find out the cost of reporting curried function arity
Looks nice, but where is the doc ? (annotated source code doesn't count !)
Need something as simple as that : http://underscorejs.org/
Here is the Iterator API I am considering. Any comments, or shall i just go ahead and implement it?
describe('iterator', function() {
var iterator = Lib.iterator;
var I = Lib.I;
var arr = [1,2,3,4];
it("returns an iterator object", function() {
var iter = iterator(I, arr);
assert.equal(typeof iter, "object");
assert.equal(typeof iter.hasNext, "function");
assert.equal(typeof iter.next, "function");
assert.equal(iter["\u03BB-iter"], true);
});
it("applies the transform function to each element as it returns it", function() {
var square = function(x) { return x * x; };
var sqIter = iterator(square, [5,7,9]);
assert.equal(sqIter.next(), 25);
assert.equal(sqIter.next(), 49);
assert.equal(sqIter.next(), 81);
});
describe('next', function() {
it("returns the next element in the list, duh", function() {
var iter = iterator(I, [100, 1000, 10000]);
assert.equal(iter.next(), 100);
assert.equal(iter.next(), 10000;
assert.equal(iter.next(), 10000);
assert.equal(iter.next(), 10000);
});
});
describe('hasNext', function() {
it("returns true when there is another element to iterate, else false", function() {
var iter = iterator(I, [2]);
assert.equal(iter.hasNext(), true);
assert.equal(iter.next(), 2);
assert.equal(iter.hasNext(), false);
});
});
});
Ok, big title.
I just wanted to share the documentation doc I created. I just wrote the description and example for the compose
function I worked on recently.
Here is the link for those who wish to read/improve it: https://github.com/CrossEye/ramda/wiki/Documentation
I've done an implementation of compose, it fairly straight forward, but I've done some tests and it proves to be far more efficient than the current implementation of Ramda. Here I am actually going show 2 variants of my implementation.
Both depend on this functions
function compose2(f,g){
return function(x){
return f(g(x));
};
}
which just does composition by pairs. Based on this two compose methods are proposed: the first one uses a forloop
which accumulates the composition as it goes along the array of functions.
function composeV1() {
var i,
f = arguments[0];
for (i = 1; i < arguments.length; i++) {
f = compose2(f,arguments[i]);
}
return f;
}
The other method does the same but using reduce
function composeV2() {
return Array.prototype.reduce.call(arguments, function(f,g){
return compose2(f,g);
});
}
which is more functional but, as we'll see, a lot slower. Here is a test comparing both the construction of the composite function as the execution of it.
var i, n = 1000000;
console.time ('ramdaComposition');
for (i = 0; i < n; i++) {
fR = ramda.compose(add1, square, double, add1, square, double);
}
console.timeEnd ('ramdaComposition');
console.time ('v1Composition');
for (i = 0; i < n; i++) {
fV1 = composeV1(add1, square, double, add1, square, double);
}
console.timeEnd ('v1Composition');
console.time ('v2Composition');
for (i = 0; i < n; i++) {
fV2 = composeV2(add1, square, double, add1, square, double);
}
console.timeEnd ('v2Composition');
console.time ('fR');
for (i = 0; i < n; i++) {
fR(5);
}
console.timeEnd ('fR');
console.time ('fV1');
for (i = 0; i < n; i++) {
fV1(5);
}
console.timeEnd ('fV1');
console.time ('fV2');
for (i = 0; i < n; i++) {
fV2(5);
}
console.timeEnd ('fV2');
The first three construct the composite functions fR
, fV1
and fV2
, and the last three just execute them with an input value. Each cycle last 1000000
iterations. The results are
ramdaComposition: 154ms
v1Composition: 107ms
v2Composition: 501ms
fR: 1356ms
fV1: 55ms
fV2: 56ms
In the composition phase, comparison to ramda.compose
composeV1
: 1.439X
composeV2
: 0,307X
So composeV1
is actually faster, but composeV2
is incredibly slow. This is due to the conversion of arguments
to Array
.
The results for execution are far more impressive in comparison to fR
:
fV1
: 24.65X
fV2
: 24.21X
This is a HUGE improvement and the implementation is far more readable than the current one. So I propose the use of composeV1
as the standard implementation.
The dummy functions used here are:
function add1(a){return a+1;}
function square(a){return a*a;}
function double(a){return 2*a;}
The comment reads // Create a deep copy of an object.
but the implementation is JSON.parse(JSON.stringify(obj))
. This means some values, most notably functions, won't be included in the returned value.
var obj = {fn:function(){}, undef: undefined, regex: /ab+c/i}
JSON.parse(JSON.stringify(obj))
// { regex: {} }
There are many choices, including:
I'd prefer #'s 2 or 3, but I think it's most important that the name and behavior agree.
.1. Would be really nice to have some examples, at least very basic, for 'fork' and 'wrap' functions use-cases.
.2. Maybe makes sense to add 'unary', 'binary' service functions to limit arity as a special cases.
Something like this:
unary = function (fn) {
return function unary (arg) {
return fn.call(this, arg);
};
};
possible use-cases:
if (hasMethod('filter', list)) {
return list.filter(useIdx ? fn : unary(fn)); // TODO: figure out useIdx
}
max = function (list) {
return list.reduce(binary(Math.max));
}
.3. Btw using Math min/max call like this: Math.max.apply(null, list);
have some limitations and works less performant than usual reduce.
.4. Is it possible to unmemoize or make force call disregarding cache with memoized function?
.5. 'always' can have alias 'constant'
.6. Just in case: there are also very promising projects: https://github.com/dtinth/it.js and http://danieltao.com/lazy.js/
Ramda looks really cool, and clean. RxJS includes the capabilities of Ramda, but extends the concept to handle time-based sequences (user interactions that occur over time is one example). Any thoughts about how RxJS (which is a mess in terms of the code and documentation) could be replaced with Ramda?
I just added streamOf
, which creates a lazy list of a single value, and which can be used with take
to initialize a list of identical elements:
take(5, streamOf(0)); //=> [0, 0, 0, 0, 0]
take(4, streamOf(null)); //=> [null, null, null, null]
But I would like a single-function gloss on this pattern. It would be useful for my current project, and I think it would be useful overall. I would call it nCopiesOf
, but that name seems to almost demand the signature (n, value)
. Yet the more useful curried version, I'm pretty sure, would be to curry the value, not the count (e.g. var copiesOfZero = flip(nCopiesOf)(0); ... var fiveZeros = copiesOfZero(5);
). We clearly don't want the user to have to flip
every time before currying with the value.
Is there some equally clear name that would permit a signature of (value, n)
instead?
Most other libraries use a zip
implementation that will apply the length of the largest array for the result.
zip([1, 2, 3], ['a', 'b', 'c', 'd']); // => [[1,"a"],[2,"b"],[3,"c"],[undefined,"d"]]
In examples/generators.js
there is a simple version of generators code, using a function that creates a head/tail
generator out of
This style generator can be used as the basis for lazy lists, and there is already sample code included to run map
s and filter
s over them, as well as, of course, take
and skip
.
The question is whether it's worth expanding our API for map
and filter
and so on to handle these. It would complicate implementation quite a bit, and we'd have to do some nasty type-checking. It's not clear if there's any great reason for this, except that a unified API for finite and infinite lists would be extremely convenient.
I would not consider doing this right away. We need to play around with the array-based implementation quite a bit first. But it might make for a very interesting library if we were to manage this.
Hi,
I want to use/help create a good functional programming library, so I've decided to contribute. I've asked @DrBoolean and he told here would be a great place for it, but I don't know where to start. If you create a wishlist or roadmap of features (new functions, documentations, test, bug correction, etc) that you would like to have, it would be easier for new contributors. Items on the document could even be debated, and would help organize the effort.
If such a document exists and you would be kind enough to share it would be of great help. I'll start by using ramda to get familiarized with it.
BTW: I've been working on a little type safety system for functions using Haskell syntax. I don't think anyone would like to type everything, but restriction might be useful sometimes.
https://github.com/cgarciae/TypeJS
The link leading to "annotated source code documentation" in the README.md has a wrong url.
It should be without the %0A ( one of the last characters )
and..
I just want to thank @CrossEye for all the great work and engagement on the functional javascript topics. Thanks!
ramda.findIndex
and ramda.findLastIndex
should be analogous to .indexOf()
and produce -1
when there are no hits
Is the performance gain that impactful when calling a function with only one element instead of three ?
I personally think that map(...) providing (value, index, list) to the callback in any case is not that counter-intuitive since nothing prevents me to give a callback that only names the first one - after all, doesn't jquery, underscore and the likes already do so ?
To have a .idx() feels superfluous, and can lead the way to have "forgotten" functions like all() who doesn't have an .idx() version and yet who could have one.
Thoughts ?
Fixing the tests (see #126) to run in older IE brought out that these tests are failing at least in IE9: sliceFrom
, strIndexOf
and strLastIndexOf
.
param order is wrong, and should probably not reverse the list. Easy fixes.
Hi, i'm working with the library and i found a case in which i have an array of nested objects. I found myself trying to do this at first:
R.pluck('data.advertiser.value')
Which didn't worked as it's not supported, and then i had to resort to this:
R.compose(R.pluck('value'), R.pluck('advertiser'), R.pluck('data'))
Which looks ugly and discarded in favor of:
R.map(function(item) { return item.data.advertiser.value; }
I have a strong feeling that this could be resolved in two possible ways:
Number one: by making R.prop
support paths.
R.pluck('data.advertiser.value')
Number two: by making a new R.path
function that supports paths.
R.map(R.path('data.advertiser.value'))
R.pluckPath('data.advertiser.value'))
In both cases there's the possibility of integrating mariocasciaro/object-path into Ramda for easier implementation.
This does not match the native splice
API, as all it does is remove part of the list without allowing you to add something new.
We should look to see if this is what we really want.
splice
is hard because of all the optional parameters to the native function...
groupBy
, countBy
, and sortBy
are generally useful functions:
var albums = [
{title: 'Art of the Fugue', artist: 'Glenn Gould', genre: 'Baroque'},
{title: 'A Farewell to Kings', artist: 'Rush', genre: 'Rock'},
{title: 'Timeout', artist: 'Dave Brubeck Quartet', genre: 'Jazz'},
{title: 'Fly By Night', artist: 'Rush', genre: 'Rock'},
{title: 'Goldberg Variations', artist: 'Daniel Barenboim', genre: 'Baroque'},
{title: 'New World Symphony', artist: 'Leonard Bernstein', genre: 'Romantic'},
{title: 'Romance with the Unseen', artist: 'Don Byron', genre: 'Jazz'},
{title: 'Somewhere In Time', artist: 'Iron Maiden', genre: 'Metal'},
{title: 'In Times of Desparation', artist: 'Danny Holt', genre: 'Modern'},
{title: 'Evita', artist: 'Various', genre: 'Broadway'},
{title: 'Five Leaves Left', artist: 'Nick Drake', genre: 'Folk'},
{title: 'The Magic Flute', artist: 'John Eliot Gardiner', genre: 'Classical'}
];
sortBy(prop("title"), albums); //=> sorted list
countBy(prop("artist"), albums); //=> object mapping artist to number
groupBy((function() {
var remap = {
Baroque: 'Classical',
Modern: 'Classical',
Romantic: 'Classical',
Metal: 'Rock' /*, etc */
}
return function(album) {
var genre = prop("genre", album);
return remap[genre] || genre;
};
}()), albums); //=> object mapping super-genre to list of albums
These are meant to be sort, count, and group by (possibly derived) keys, not arbitrary comparators. We might still want a more general sort
, or more likely a comparator
builder based on a predicate. This is just a single key based on only one object.
sortBy
has a nicer API than do minWith
and maxWith
.
In sortBy
we generate a key for the objects to sort, and the comparator to use is automatically created based on this key. So we can do something as simple as
sortBy(prop('title'), list);
minWith
and maxWith
, by contrast, which don't even need to be sorted, require you to supply a comparator.
minWith(function(a, b) {return a.x - b.x;}, points)
This really should be
minWith(prop('x'), points);
Granted the current API offers some theoretic additional flexibility. But I can't see any realistic cases that would take advantage of it.
If there are no objections, I'll do this over the weekend.
went to look at the tests for useWith--and there aren't any!
As mentioned in #72, an internal reference to any function to be used internally should be kept (non auto curried) for performance. At the end of ramda.js
expose all functions
R.all = _(all);
R.any = _(any);
R.foo = _(foo);
On another note, I disagree with separating map
and map.idx
I think the idx
functions should be used by default. I think a more suitable approach would be to introduce an argLimiter
function to the api for partially application via a map/filter
Thanks for writing Ramda—it's really scratching an itch I've had for a while. Would it be possible for you to put it up on npm? I'd love to use it in Node.
I think it would be best to have another function, e.g. R.map.sparse, that does the standard map behavior.
Here is some code that will demonstrate the issue:
var R = require('ramda');
var sparse = [, 2, 3];
var mapResults = sparse.map(function(item) {
return item;
});
console.log(mapResults);
var RmapResults = R.map(function(item) {
return item;
}, sparse);
console.log(RmapResults);
Why call a method with the same name if it exists ? I fear that it may lead to some incomprehensions if a method with the same name as one we want to use exists on the object but does something completely different - and when I use R.take, for instance, it is the function that I want called.
Why not use something like R.method('name', args...) instead to leave the choice up to the user of the library ?
var obj = [1, 2, 3];
obj.take = function() { return this[1]; } //
R.take(2, obj); // will return 2, but I wanted [1, 2]
// I propose
R.method("take", obj); // 2
R.take(2, obj); // [1, 2]
I'm just worried that since javascript is so permissive, people will end up applying ramda's function on objects they have completely modified beforehand and get unexpected results.
I noticed a few cases where some functions won't work in old ie (specifically indexOf). Is this library IE>=9 (it would be about an extra 8 loc for IE>7)
Documentation for strLastIndex should be
// strLastIndexOf('a', 'banana split') //=> 2
instead of
// strIndexOf('a', 'banana split') //=> 2
SIGNATURE
then :: (a -> b) -> (b -> c) -> (a -> c)
Note: then
is actually meant to be added to Function.prototype
IDEA
Using the same Promise syntax, with then
you can "compose" or sequence functions in a way that gives a good idea what is being computed.
INSPIRATION
http://scott.sauyet.com/Javascript/Talk/2014/01/FuncProgTalk/#slide-124
EXAMPLE
var FoG = G.then (F);
var login =
startProgram
.then (goToMainWindow, clickOnLogin)
.then (typeUserName, typePassword)
.then (clickOk);
IMPLEMENTATION
Function.prototype.then = function () {
return R.pipe.apply (this, [this].concat (argsToArray (arguments)));
}
OPTIONAL
This introduces a new argsToArray
function, that converts arguments to array.
var argsToArray = R.argsToArray = function (_args) {
var args = new Array(_args.length),
i = -1;
while (++i < _args.length) {
args[i] = _args[i];
}
return args;
};
This implementation of argsToArray
is an adaptation of f5
in this test http://jsperf.com/arguments-to-array/5, which has the best marks, and could be used inside many other functions, including compose
.
> R.sum([1,1])
2
> R.sum([1,1])
4
Is containWith that useful to have as an API function ?
I may be wrong, but I'd rather do something like
R.contains(R.lPartial(predicate, value), list) // could also be curry, why not.
which imo conveys better what really happen.
Most curried binary operators can be lifted to then be applied to two consecutive functors. Example
Maybe.of( R.add ).ap( Maybe (2) ).ap( Maybe (3) ); // Just 5
You should have the possibility to do this with compose. For that you need a curried binary compose (compose2
). Example
Maybe.of( R.compose2 ).ap( Maybe (R.add(2)) ).ap( Maybe (R.add(3)) ); // Just (+5)
Some minor clean-up tasks:
and
, or
, and not
functions, and reassign andFn
, orFn
, and notFn
to these names.last
similar to first
/head
join
, indexOf
, lastIndexOf
, etc to use invoker
. Alternately: adjust these to handle arguments
objects as well as arrays.slice
implementation, which would probably mean renaming the internal one. The public one should have the signature slice(start, end, arr)
and be autocurried, and possibly have sliceFrom
/sliceTo
glosses to handle the missing parameters. This would probably have to be different from the private implementation we already have or we'd introduce a circular dependency with curry
.we need a way to componentize the lib so users may build a version as slim or full as they need. There's no reason for us to inflict algebraic types on everybody, 'cuz most folks probably can do without.
Candidates for modularization:
definitely not in core:
Lazylists
Types (Maybe, Either, IO, EventStream)
possibly pulled out into extensions:
math fns
sql-y query functions
more?
Have to see how other projects handle this problem (e.g. lodash? http://lodash.com/custom-builds)
add find fn to return first match in a list
find fn -> list -> element
also
experiment with underscore/lodash style POJSO notation for specifying constraints
working with the DOM has convinced me of the need for an each
method on ramda.
The DOM does not lend itself to immutability. Or referential transparency. Or elegance. so this is one vote for exposing a bit of impurity (gasp) in the lib for the sake of practicality.
Note that since we are not using native array methods (e.g. slice) internally, we can use ramda seamlessly with NodeLists et al.
Thoughts?
experiment with underscore/lodash style POJSO notation for specifying constraints
Generally when I've seen languages differentiate reduce
and fold
, it's by whether or not the zero parameter (as in foldl(fn, zero, list)
) is assumed from the first list element.
Stack Overflow corroborating my thoughts:
http://stackoverflow.com/a/9055893
Counterpoint:
JavaScript's native reduce
allows specifying the zero
parameter.
Counter-counterpoint:
JavaScript doesn't have a fold
to contrast with reduce
, and Ramda seems to prefer separate functions over variadic ones.
Currently Maybe
is a class that has some dependent behavior, but if Maybe
is decoupled into Just
and Nothing
, like in Haskell, each is simpler and more performant.
Take this map
example:
Just.prototype.map = function (f) {
return new Just (f (this.value));
};
Nothing.prototype.map = function (f) {
return new Nothing(); //or return this?
};
As you see, each subclass already knows what to do, no need to check if isNil
all the time.
Also, at least in Haskell, you can't have an instance of Maybe something
, Maybe
is more like an interface or an abstract class. But if you wan't to keep the constructor for compatibility, Maybe
should just be the function:
function Maybe (x) {
return isNil(x) ? new Nothing() : new Just (x);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.