Coder Social home page Coder Social logo

ramda's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ramda's Issues

ramda.* vs R.*

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?

foldl1 throws

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));
    });

?

mapObj.idx?

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 ...

iterator spec

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);                                                                                                          
      });                                                                                                                                             
    });                                                                                                                                               
});                                 

Reimplementation of Compose 24x Faster!

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;}

R.cloneDeep is more like "make JSON-safe"

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:

  1. change the function name
  2. change the behavior to use the structured cloning algorithm
  3. change the behavior to allow all values (including functions, RegEx's, etc)

I'd prefer #'s 2 or 3, but I think it's most important that the name and behavior agree.

Small code feedback.

.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/

Is this like RxJS, or could it be extended to handle time-based sequences?

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?

Want something like 'nCopiesOf', but obvious signature doesn't really work with currying

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?

zip should use the length of the largest array

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"]]

Long-term: consider expanding API our that accepts arrays to also accept generators

In examples/generators.js there is a simple version of generators code, using a function that creates a head/tail generator out of

  • a seed
  • a function that calculates the current value from the seed
  • and a function that calculates the next seed.

This style generator can be used as the basis for lazy lists, and there is already sample code included to run maps and filters 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.

Create a contribution Wishlist or Roadmap

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

url for documentation

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!

Is there a real utility for the .idx functions ?

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 ?

Old IE fixes

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.

Fix `uniq`

param order is wrong, and should probably not reverse the list. Easy fixes.

Add path support to R.prop or add a new R.path

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.

Examine API of `splice`

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...

Add `groupBy`, `countBy`, and `sortBy` functions

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.

Consider changing `minWith` and `maxWith` APIs to be more like `sortBy`

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.

Don't curry internal functions

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

npm package

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.

R.map doesn't support Sparse Arrays like the standard map

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);

Of the use of hasMethod() in the implementations of many functions

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.

Browser support

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)

strLastIndex

Documentation for strLastIndex should be

        //     strLastIndexOf('a', 'banana split') //=> 2

instead of

        //     strIndexOf('a', 'banana split') //=> 2

PROPOSAL [.then]

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.

Usefulness of containsWith

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.

Curried Binary Compose

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)

Minor Cleanup

Some minor clean-up tasks:

  • remove existing and, or, and not functions, and reassign andFn, orFn, and notFn to these names.
  • add last similar to first/head
  • rewrite join, indexOf, lastIndexOf, etc to use invoker. Alternately: adjust these to handle arguments objects as well as arrays.
  • consider adding a public 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.

build/bundle process

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)

find

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

time to make `each` public

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?

`reduce` should be an alias of `foldl1`, not `foldl`.

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.

Implementation of Maybe

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);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.