GithubHelp home page GithubHelp logo

selfrefactor / rambda Goto Github PK

View Code? Open in Web Editor NEW
1.6K 1.6K 85.0 12.62 MB

Faster and smaller alternative to Ramda

Home Page: https://selfrefactor.github.io/rambda

License: MIT License

JavaScript 79.15% TypeScript 20.85%
fp functional-programming lodash ramda utils

rambda's Introduction

rambda's People

Contributors

agriffis avatar alexjamesmalcolm avatar amitmirgal avatar darkxanter avatar dependabot[bot] avatar evgenyorekhov avatar farwayer avatar fatxx avatar helmuthdu avatar ku8ar avatar matthewcallis avatar neshvig10 avatar peeja avatar pm-radu avatar renovate-bot avatar renovate[bot] avatar rkrajewski avatar rmdort avatar roblav96 avatar romgrk avatar sashkashishka avatar selfrefactor avatar slavaganzin avatar squidfunk avatar sschneider-ihre-pvs avatar synthet1c avatar thejohnfreeman avatar tonivj5 avatar whoatedacake avatar yordis avatar

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

rambda's Issues

Mutable API

Rambda's reverse modifies the array, instead of returning reversed copy of it.

Just wanted to ask why? When you create a new reversed array it's probably just a shallow copy. There's no need to "fight" for RAM too much as JS already has memory sharing built-in. It's just not protected sharing and we protect it by convention with Ramda approach.

let xs = [{...}, {...}, {...}]
//
let ys = [xs[2], xs[1], xs[0]]

ys does not double the memory usage. It's only additional pointers (4 bytes each)!

I.m.o the convention of "every function here is immutable" has to be kept at any cost.

Broken index.d.ts

Using Typescript 2.5.2, errors are emitted because of the syntax used in the definition file for this project.

On rambda 0.9.3, in index.d.ts, on lines 387 and 388, the syntax is wrong.

without(listToOmit: any[], originalList: any[]): list: any[];
without(listToOmit: any[]): (originalList: any[]) => list: any[];

I'm not an expert on Typescript, but list: any[] at the end looks like a mistake.

The error specifically is Expecting new line or semicolon. Because this line is broken, you get other errors down the road, like TS2304: Cannot find name 'without'. which is a method I'm assuming was added recently.

chain or flatMap missing?

Hi, I am looking for a chain or flatMap function, but can't find it. Is there such a function and if so, what is it's name? If not, is there a special reason and might I submit a pull request to add it?

Curry trouble (example for R.is)

Hi,

I understand this has to do with curry, but I constantly have trouble with R.is because of this, so...
The troube comes when I do something like this
if (R.is(Array, someVar)) { ... }
Now, if someVar is undefined then R.is will return function, so the check will pass.
This may happen in a bigger scope, when my function accepts an object A, and one of its fields B should be an array:

function myFunc(A) {
...
  if (R.is(Array, A.B)) { ... }
...
}

So, if B was not defined, the IF block will proceed as if B were an array.
I can add an additional check
if (A.B && R.is(Array, A.B)) { ... }
But that just doesn't feel and look right. Right?

Curry style

We were looking at using Rambda instead of Ramda because of the speed, size and complexity benefits but we weren't huge fans of the current Curry implementation.

We liked that you say you're currying at call rather than implementation because it removes the ambiguity over whether the function you were calling is curried or not, but disliked that you have to change the interface of the original function to support it.

Is there a specific reason you went with this implementation rather than something like:

https://gist.github.com/danrspencer/a81fd5702d7d565c470b1566453f71a7

?

forEach allows objects, but doesn't provide props in iterate func

Hi,

R.forEach as I see in source is just a proxy to R.map, but returns original data.
So it seems natural to pass Objects to R.forEach (just like R.map). But unfrotunately second argument (prop) is lost in iterate function.

R.map((val,prop) => ...prop is OK here..., {...})
R.forEach((val,prop) => ...prop is undefined here..., {...})

Otherwise R.forEach loops over object's properties just fine.
Is it possible to make prop available inside forEach iterate func?

Thanks!

Better currying

In my opinion Ramda's curry handles two things not particularly well.

  1. It does not care about function name
  2. It does not pass this properly

My implementation

let assert = require("assert")

function curryN(N, fn) {
  let self = undefined
  let collectFn = Object.defineProperties(function (...args) {
    if (this) {
      self = this
    }
    if (args.length >= N) {
      return fn.apply(self, args)
    }
    else {
      return Object.defineProperties(function (...args2) {
        if (this) {
          self = this
        }
        return collectFn.apply(self, args.concat(args2))
      }, {
        name: {value: fn.name + "_" + args.length},
        length: {value: N - args.length},
      })
    }
  }, {
    name: {value: fn.name},
    length: {value: N}
  })
  return collectFn
}

let curry = function (fn) {
  return curryN(fn.length, fn)
}

let sum3 = curry(function mySum(x, y, z) {
  return x + y + z
})

let getSelf = curry(function (x) {
  return this
})

let getSelf2 = curry(function (x, y) {
  return this
})

// TEST LOGIC
assert.equal(typeof sum3(1), "function")
assert.equal(typeof sum3(1, 2), "function")
assert.equal(typeof sum3(1)(2), "function")
assert.equal(sum3(1)(2)(3), 6)

// TEST NAMES
assert.equal(sum3.name, "mySum")          // !!!
assert.equal(sum3(1).name, "mySum_1")     // !!!
assert.equal(sum3(1)(2).name, "mySum_2")  // !!!

// TEST LENGTHS
assert.equal(sum3.length, 3)
assert.equal(sum3(1).length, 2)
assert.equal(sum3(1)(2).length, 1)

// TEST this BINDING
let self = {foo: "foo"}
assert.deepEqual(getSelf.bind(self)(null), self)
assert.deepEqual(getSelf2.bind(self)(null, null), self)
assert.deepEqual(getSelf2.bind(self)(null)(null), self) // !!!
assert.deepEqual(getSelf2(null).bind(self)(null), self)

Now the original R.curry fails the tests marked with !!!.

I raised this issue earlier I think, but they told me they don't want to support this at all or something like that. As for names it can be argued against (fake). I personally believe having names like above is very helpful at debugging.

I'm interested what do you think.

Webpack Uglify won't run on module as it is compiled as es6

Currently this module can't be used in production with webpack as it is es6 and isn't supported by the webpack uglify module.

Can you compile the package to es5 first with either babel or something else, before building with webpack.

Modifying arguments

Hi, fan of ramda and looking for smaller and faster implementation, arrived to your rambda. I was curious about the implementation and I'd like to ask: for example prepend method (as far as I can see in the code) modify the argument (the array). The statement const clone = arr do not clone array, co modifying clone will modify the arr.

Or am I wrong...?

BTW: I like the way you curry most of the functions. Not sure about if it can lead to some issues (arguments are passed to the function but undefined), but as a trade-off why not.

Unknown plugin "external-helpers"

I've noticed that you now have to set the NODE_ENV variable in your process when using the package, otherwise you get the following error.

Module build failed: ReferenceError: Unknown plugin "external-helpers" specified in "/home/travis/build/jpgorman/react-append-to-body/node_modules/rambda/.babelrc.env.development"

flatMap is missing

Why is there no flatMap operation? It's not only very useful, it's also the bind operation for the list monad, so it's kind of heresy to omit it from a "functional" library ;)

There's a "flatten" in rambda, but because it flattens recursively, one can't simply define flatMap as compose(flatten, map).

So please consider adding a non-recursive shallowFlatten and also 'flatMap' (ideally an optimized one, but one based on map and shallowFlatten would also do).

Also a 'scan' operation (similar to reduce but building a list of the result of each iteration instead of giving just the end-result) would be useful (but far less important as flatMap).

Another useful operation would be somethink like "any" which would only test for 'undefined' and return the first non-undefined result of the iteration. This is useful to return the first successful application of a value to some operation (similar to 'find' but returning an arbitrary value instead of the found element).

add R.not

R.not(true); //=> false
R.not(false); //=> true
R.not(0); //=> true
R.not(1); //=> false

Provide public ES modules

/es package entry point is conventional in modern libraries.

The suggestion is to provide rambda/es imports in ES5 and ES module format , similarly to how it was done with ramda/es. Notice that Ramda modules are transpiled to ES5 to skip transpilation on bundling, and there is index reimporting file.

The package has rambda/lib which is CJS. It doesn't have index (there will be problems with tree-shaking any way) and requires to use module interop when imported as ES modules, this results in increased footprint.

Create react app project won't build with Rambda dependency

Trying to build a create-react-app with Rambda dependency and have this error:

Failed to minify the code from this file:

 	./node_modules/rambda/dist/rambda.esm.js:3

Read more here: http://bit.ly/2tRViJ9

Version is 1.1.3 but also tried 1.1.1 with same result.

Curry === is problematic

First off, i like your work! Its really cool to learn fp style without reading through dozens lines of code :).

I know you copied the curry function from a gist comment 😄 , i took some time to learn how it works and notices a problem - especially when its used together with reduce

Problem in one sentence: If the curried function is called with too many arguments, it will return a new curried function instead of calling the final function. Also the new curried function will now return again a new curried function for every further attempt.
https://github.com/selfrefactor/rambda/blob/master/modules/curry.js#L4

...
o.length === f.length
...

A basic example (ES6):

const add = curry((n, n2) => n + n2
add(1, 2, 3)
// result: function
// desired: 3

Reduce example:

const add = curry((n, n2) => n + n2)
reduce(add, 0, [1, 2, 3])
// result: function

Reduce calls the add function not only with acc and value, but also with index and array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

  • One way to fix it, would be to change o.length === f.length to o.length >= f.length
  • An alternative would be to change the reduce function in the way, that it only calls with acc and value, (Ramda does it like that: http://ramdajs.com/0.23.0/docs/#reduce)

Breaking changes on patch update

0.9.7 was published recently which now bundles JS which is ES6 rather than ES5.
This is a breaking change and therefore should be a major version bump due to consuming projects now requiring an ES6 compliant environment.

Would it be possible to revert these changes and release a new patch, and then release the change to ES6 as a major version bump (e.g 1.0.0)?

mapAsync and composeAsync suggestion?

Currently both methods are part of Rambdax

I feel that they could be more useful as part of Rambda API, but I will wait Rambda users to decide that.

Please comment if you have opinion on the topic.


composeAsync

composeAsync(fn1: Function|Async, .. , fnN: Function|Async)(startValue: any): Promise

  • compose that accepts async functions as arguments

mapAsync

mapAsync(fn: Async|Promise, arr: Array): Promise

Sequential asynchronous mapping with fn over members of arr

const fn = a => new Promise(resolve => {
  setTimeout(() => {
    resolve(a + 100)
  }, 100)
})

const result = await R.composeAsync(
  R.mapAsync(async a => await fn(a)),
  R.mapAsync(fn),
  R.map(a => a * 10)
)([1, 2, 3])
expect(result).toEqual([210, 220, 230])

Add eslint

I believe that we should use a linter, as then contributing to the repo would be easier and everyone would be using same coding conventions.

defaultTo typing

change current typing which is

defaultTo<T, U>(a: T, b: U): T | U
defaultTo<T>(a: T): <U>(b: U) => T | U

Using internal curry

I've noticed that a lot of functions have a check whether the arguments are defined inside of them.
To cut down on size, wouldn't it be better to just wrap them in curry functions ?

Move to Typescript

There is currently an issue with inconsistent and faulty typings.

One way to solve it is to let Typescript build the definitions.

compose question

How many parameters can have the right most function?
work using more than one parameter?

const result = R.compose(
  R.map(x => x * 2),
  (a, y) => R.filter(x => x > y, a)
)([1, 2, 3, 4], 2)

// => []

Have I write a wrong example?

regards

add R.keys

R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']

not-so-lightweight disk install

While the lib code may only be 10kb, the install on disk is almost 3MB

$ disk-usage | grep rambda
2.9M    rambda

It comes with the whole repo:

ls -o
total 640
-rw-r--r--   1 tkye    1891 Oct 26  1985 CONTRIBUTING.md
-rw-r--r--   1 tkye    1069 Oct 26  1985 LICENSE
-rw-r--r--   1 tkye   54739 Oct 26  1985 README.md
drwxr-xr-x  39 tkye    1326 Jun 26 14:55 __tests__
drwxr-xr-x  33 tkye    1122 Jun 26 14:55 benchmarks
drwxr-xr-x   8 tkye     272 Jun 26 14:55 dist
drwxr-xr-x   5 tkye     170 Jun 26 14:55 docs
drwxr-xr-x  13 tkye     442 Jun 26 14:55 files
-rw-r--r--   1 tkye   19619 Oct 26  1985 index.d.ts
drwxr-xr-x  99 tkye    3366 Jun 26 14:55 lib
drwxr-xr-x  99 tkye    3366 Jun 26 14:55 modules
-rw-r--r--   1 tkye    3067 Jun 26 14:55 package.json
-rw-r--r--   1 tkye    5129 Oct 26  1985 rambda.js
-rw-r--r--   1 tkye   11807 Oct 26  1985 webVersion.js
-rw-r--r--   1 tkye  214780 Oct 26  1985 yarn.lock

You should consider using the files property on package.json to reduce this size. Things like __tests__ don't need to ship with the installation

defaultsTo shouldn't factor in type

As far as I can tell defaultsTo(defaultArgument, inputArgument) is the only method that provides a way for defaulting to a value (providing some 'or' logic). However, the built-in test for inputArgument being the same type as defaultArgument limits the use of this function. It is my opinion that defaultArgument should only be returned if inputArgument is undefined.

I would like to compose a function works the same as Lodash's get method or Ramda's pathOr method whereby a value is read from a path through a nested object tree and defaults to a value if this path returns undefined.

import { get } from 'lodash'
import { pathOr } from 'ramda'
import { curry, defaultsTo, path } from 'rambda'

// Tests written with jest

test('lodash get', () => {
  expect(get({ a: { b: 2 } }, 'a.b')).toBe(2)
  expect(get({ c: { b: 2 } }, 'a.b')).toBeUndefined()
  expect(get({ c: { b: 2 } }, 'a.b', 'N/A')).toBe('N/A')
})

test('ramda pathOr', () => {
  // From ramda's docs http://ramdajs.com/docs/#pathOr
  expect(pathOr('N/A', ['a', 'b'], { a: { b: 2 } })).toBe(2)
  expect(pathOr('N/A', ['a', 'b'], { c: { b: 2 } })).toBe('N/A')
})

// There's probably a more elegant way of doing this?
const rambdaPathOr = curry((defaultValue, inputPath, inputValue) =>
  defaultTo(defaultValue, path(inputPath, inputValue)))

test('rambda pathOr', () => {
  // This first test fails because the value of a.b is 2 (type 'Number')
  // and is a different type to the default value 'N/A' (type 'String')
  expect(rambdaPathOr('N/A', ['a', 'b'], { a: { b: 2 } })).toBe(2)
  expect(rambdaPathOr('N/A', ['a', 'b'], { c: { b: 2 } })).toBe('N/A')
})

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.