Ramda: ImmutableJS support

Created on 3 Sep 2015  Â·  91Comments  Â·  Source: ramda/ramda

I'm currently working on an existing project that uses immutableJS for all data structures, i've been trying to adopt ramda. Obviously all of the array and object functions don't work and I have to convert in and out of plainJS objects.

var personObject = {firstName: "Dale"}
var personImmutable = immutable.Map(personObject)

R.prop("firstName", personObject) // "Dale"

R.prop("firstName", personImmutable) // undefined

I've started a repo to add ImmutableJS support to Ramda at https://github.com/DaleJefferson/ramda-immutable
I've completed a couple of methods as a proof of concept and it works nicely.

function prop(p, obj) {
    if (immutable.Map.isMap(obj)) {
        return obj.get(p)
    }

    return R.prop(p, obj)
}

I wonder if this should be part of Ramda or sit as a separate project? It could be done in a generic way like equals.

// This already works with immutableJS because all immutableJS objects implement equals.
function equals(a, b) {
  return _hasMethod('equals', a) ? a.equals(b) : _hasMethod('equals', b) ? b.equals(a) : _equals(a, b, [], []);
})

// immutableJS Maps have a get method to access props
function prop(p, obj) {
    if (_hasMethod("get", obj)) {
        return obj.get(p)
    }

    return obj[p]
}

// immutableJS Maps have a first method to access the first item
function head(list) {
    if (_hasMethod("first", list)) {
        return list.first()
    }

    return list[0]
}

Most helpful comment

With that argument you could say the same thing about the native JavaScript methods.

JavaScript ships with it's own methods for transforming data structures.

[1,2,3].map(thing)

immutable.Map([1,2,3]).map(thing)

R.map(thing, [1,2,3])

They all do the same thing so maybe R.map should not exist.

Really I want to live the dream of having immutable data structures and use a functional interface.

var plainPeople = [{name: "DALE"}, {name: "AmY"}];
var immutablePeople = immutable.fromJS(plainPeople);

var lowerCaseName = R.over(R.lensProp("name"), R.toLower);

// Without Ramda
immutablePeople.map(person => person.update("name", person.get("name").toLowerCase())) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

// Current Ramda
immutable.fromJS(R.map(lowerCaseName, immutablePeople.toJS()))) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

// With improved Ramda
R.map(lowerCaseName, plainPeople) //  [{name: "dale"}, {name: "amy"}]
R.map(lowerCaseName, immutablePeople)) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

In the immutableJS version no data was mutated and this is done in a performant way because of structural sharing. Personally I like the Ramda version, and it would work on plain JS objects and ImmutableJS objects.

Yes I could build a library of functions that convert the immutableJS oo style to functional but then I would end up replicating a new version of Ramda that only works with ImmutableJS. I started work on an effective fork ramda-immutable, but I don't think this is the best use of my time.

I would like to see ramda be pluggable.

import ramda from "ramda"
import immutableAliases from "ramda-immutable"
import highlandAliases from "ramda-highland"

var R = ramda.addAliases(immutableAliases).addAliases(highlandAliases);

// R now works with plain objects, immutableJS and Highland data structures.

All 91 comments

See also #1037, which discussed integration with Highland; it has not had any activity in quite a while.

My favorite idea for this would be to allow the user to supply a number of delegation aliases and get back an instance of Ramda with those aliases applied. A new instance of Ramda is pretty important in order to maintain referential transparency as discussed in that thread. Another possibility would be to add these at build time, offering a configuration file that listed delegate aliases, and then the user could just build a version that made sense for her environment. Ramda could publish likely candidates like ramda.immutablejs.js or ramda.highland.js as we saw fit.

I like the aliases idea but it would require some internal changes since Ramda internally inconsistently delegates to other Ramda functions. I assume this is for performance reasons?

function propSatisfies(pred, name, obj) {
  return pred(obj[name]);
});

// Would need to change to.

function propSatisfies(pred, name, obj) {
  return pred(prop(name, obj));
});

I also don't think it would be as simple as a one to one mapping.

// simple
{prop: "get", head: "first"}

// I we would need something like this
{
    prop: {
        test: (p, obj) => immutable.Map.isMap(obj),
        method: (p, obj) => obj.get(p)
    }
}
function prop(p, obj) {
    var alias = aliases["prop"]
    if (alias && alias.test(p, ob)) {
        return alias.method(p, ob)
    }

    return obj[p]
}

This solution would allow for swapping arguments in the highland reduce case

{
    reduce: {
        test: (acc, list) => highland.isStream(list),
        method: (acc, list) => highland.reduce(list, acc)
    }
}

I'm wondering. Why do you want this in the first place? Why do you expect Ramda to integrate with ImmutableJS? ImmutableJS ships with their own methods for transforming their muteable data structures. Ramda functions are implemented on plain objects and arrays.

If it is because you don't like the object oriented API you can create invokers with R.invoker. Alternatively you might consider functionize.

With that argument you could say the same thing about the native JavaScript methods.

JavaScript ships with it's own methods for transforming data structures.

[1,2,3].map(thing)

immutable.Map([1,2,3]).map(thing)

R.map(thing, [1,2,3])

They all do the same thing so maybe R.map should not exist.

Really I want to live the dream of having immutable data structures and use a functional interface.

var plainPeople = [{name: "DALE"}, {name: "AmY"}];
var immutablePeople = immutable.fromJS(plainPeople);

var lowerCaseName = R.over(R.lensProp("name"), R.toLower);

// Without Ramda
immutablePeople.map(person => person.update("name", person.get("name").toLowerCase())) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

// Current Ramda
immutable.fromJS(R.map(lowerCaseName, immutablePeople.toJS()))) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

// With improved Ramda
R.map(lowerCaseName, plainPeople) //  [{name: "dale"}, {name: "amy"}]
R.map(lowerCaseName, immutablePeople)) //  immutable.List([immutable.Map({name: "dale"}), immutable.Map({name: "amy"})])

In the immutableJS version no data was mutated and this is done in a performant way because of structural sharing. Personally I like the Ramda version, and it would work on plain JS objects and ImmutableJS objects.

Yes I could build a library of functions that convert the immutableJS oo style to functional but then I would end up replicating a new version of Ramda that only works with ImmutableJS. I started work on an effective fork ramda-immutable, but I don't think this is the best use of my time.

I would like to see ramda be pluggable.

import ramda from "ramda"
import immutableAliases from "ramda-immutable"
import highlandAliases from "ramda-highland"

var R = ramda.addAliases(immutableAliases).addAliases(highlandAliases);

// R now works with plain objects, immutableJS and Highland data structures.

Want immutability and functional interfaces? ClojureScript is your friend.

They all do the same thing so maybe R.map should not exist.

Nope. R.map is not equal to Array.prototype.map. R.map passes only the element to the map function, it does not handle sparse arrays and it doesn't handle fantasy land functors. If it wasn't for those differences then we we'd probably define R.map as R.invoker(1, 'map').

Yes I could build a library of functions that convert the immutableJS oo style to functional but then I would end up replicating a new version of Ramda that only works with ImmutableJS.

And how would your proposed plug ramda-immutable not contain a replica of Ramda that only works with ImmutableJS?

I'm wondering. Why do you want this in the first place? Why do you expect Ramda to integrate with ImmutableJS? ImmutableJS ships with their own methods for transforming their muteable data structures. Ramda functions are implemented on plain objects and arrays.

My take is that I would really like it if we could support the same sort of composable API on many data types:

map(square, [1, 2, 3]); //=> [1, 4, 9]
map(square, Maybe.Just(5)); //=> Maybe.Just(25)
map(square, Maybe.Nothing); //=> Maybe.Nothing
map(square, immutable.Map([1, 2, 3])); //=> immutable.Map([1, 4, 9])
var bacon = $('#input').asEventStream('change').map(evt => Number(evt.target.value));
map(square, bacon); //=> Stream, e.g. [64, 36, 49, 25, 9, 0, 81, ...]

The key word there is 'composable'. OO-style methods are not composable, or at least not composable in any easy manner.

So, while these points are important:

R.map passes only the element to the map function, it does not handle sparse arrays and it doesn't handle fantasy land functors.

to me they are very much secondary to being able to compose map or map(square) with other functions in a simple manner.

I am somewhat less concerned about Immutable than about Bacon, RxJs, Flyd and the like, as well as Highland, as it seems to me that much of the reason for using a library like Immutable is made unnecessary by Ramda's strong insistence on never mutating user data. But I would still like to support it if possible. It just hasn't bubbled to the top of my personal queue.

The key word there is 'composable'. OO-style methods are not composable, or at least not composable in any easy manner.

I hear this often. But it is not true. Chainable OO APIs are composable. Function composition is not the only sense in which things can be composable. I prefer function composition though. And to make the conversion we have R.invoker.

to me they are very much secondary to being able to compose map or map(square) with other functions in a simple manner.

Good point. I agree.

I would love to switch my team over to ClojureScript but it's a massive learning curve and we have an established JS codebase. I introduced ImmutableJS into our workflow as it solved a lot of issues we had regarding shared mutable state, and it simplified a lot of problems with React and Flux. It also got the team thinking about functional programming. So far ImmutableJS has been a massive success.
My next step is to move to a purer functional style and slowly train the team up on Ramda over the next year. Which brings me into conflict between Ramda and ImmutableJS.

I appreciate that Ramda does not mutate data but I don't see any downside in having a stronger immutable data structure as an option. I feel that Ramda and Immutable have a lot to gain from this union.

My options are to ditch ImmutableJS and rewrite a large codebase from OO to functional to use Ramda,
jump in and out of plain JS objects, find a way for Ramda to work with ImmutableJS, choose an alternative functional library or stick to the ImmutableJS OO style with maybe some Ramda mixed in.

paldepind seems to be suggesting that I do the latter.

I will put together a proof of concept for the alias system and see how it works, I'm happy to contribute to a system that is not ImmutableJS specific. So far I am enjoying using Ramda it's a really nice piece of technology :+1:

stick to the ImmutableJS OO style with maybe some Ramda mixed in.

I'm suggesting you replace the use of all methods calls with invokers.

I will put together a proof of concept for the alias system and see how it works, I'm happy to contribute to a system that is not ImmutableJS specific.

It sounds interesting :+1:

@DaleJefferson

I will put together a proof of concept for the alias system and see how it works, I'm happy to contribute to a system that is not ImmutableJS specific

I'm very much looking forward to seeing it.

@paldepind @CrossEye The alias idea seems to work well for a simple thing like prop, I can't see any problems in rolling this out to other methods.

I've implemented it as a decorator, I know we love adding more wrapper functions ;) But it should be as simple as adding _aliasWrap (or whatever we want to call it) to every function. I guess it's only things like map, prop, head that we want to have aliases for, I guess replacing curry or compose would not make sense.

// addAlias.js

var _aliases = require('./internal/_aliases'); // Evil singleton

module.exports = function alias(key, value) {
   _aliases[key] = value;
};
// _aliasWrap.js

var _aliases = require('./_aliases');

module.exports = function (method) {
  return function () {
    var args = arguments;
    var alias = _aliases[method.name]; // This won't work when minified, we will have to pass in a string name
    if (alias && alias.test.apply(null, args)) {
      return alias.method.apply(null, args);
    }

    return method.apply(null, args);
  }
}
// prop.js

var _aliasWrap = require('./internal/_aliasWrap');
var _curry2 = require('./internal/_curry2');

module.exports = _curry2(_aliasWrap(function prop(p, obj) {return obj[p];}));
// test.js

var assert = require('assert');

var R = require('..');

describe('Aliased prop', function() {
  it('works with immutable', function() {
    R.addAlias("prop", {
      test: function (p, obj) {
        return immutable.Map.isMap(obj);
      },
      method: function (p, obj) {
        return obj.get(p);
      }
    });

    var person = immutable.Map({name: "Dale"});

    assert.strictEqual(R.prop("name", person), "Dale");
  });
});

We properly want to do R.addAliases(immutableAliases) so we can define all of the immutable aliases in one object, but we might also want to add them one by one.
I guess we should be able to support multiple aliases for a given method, using the test function we can apply the first one that passes the test.
Modifying the global Ramda instance is not great, but i'm not familiar with Ramda to work out the best way of doing the new Ramda instance thing.

I've had a little look at Highland and what I have so far should work with Highland, I can't speak for the other libraries you have mentioned.

// Highland example

R.addAlias("map", {
   test: function (fn, list) {
      return highland.isStream(list);
   },
   method: function (fn, list) {
     return list.map(fn);
   }
});

var people = highland([{name: "Dale"}]);

assert.deepEqual(R.map(R.prop("name"), people).toArray(), ["Dale"]);

@DaleJefferson:

This is a great start!

Modifying the global Ramda instance does not sound very functional to me, but i'm not sure of a better way to do it at the moment.

I do think we'd have to find an alternative to that.

I have several questions: Why are the apply's in aliasWrap passed null? Shouldn't we have an object to pass to them? Or am I simply misunderstanding the code?

Can you think of a way to do this where an equivalent to aliasWrap is applied after the fact, and not integrated into the main implementation of the function? I would certainly find it a lot more palatable? I'm not sure it is possible, but if we could, that would be a huge advantage.

I'm not sure what you mean the object is in the args array along with the prop, we just pass the args to the alias method this way it should work for all signatures and the aliases have lots of flexibility in how they are implemented. The null is the thisArg which we don't care about. We are effectively delegating the responsibility of implementing the prop function to the alias, it has the same signature as the original prop method. We could in fact make the Plain JS implementation an alias. Ramda-JavaScript but that's going to far.

To apply _aliasWrap after the fact, we could keep the methods uncurried then at runtime we could build a Ramda instance by optionally applying the alias and then the curry.

// prop.js
module.exports = function prop(p, o) { return o[p]; } // no curry
// R.js
function createRamda(aliases) {
    return {
        prop: (aliases) ? curry(aliasWrap(aliases, require('./prop'))) : curry(require('./prop'));

This would also solve the nasty global state issue we could partially apply the aliasWrap function with the aliases.

Btw I don't think I understand Ramda's build system, is it custom? Have you considered letting WebPack build the umd?

the object is in the args array along with the prop, we just pass the args to the alias method this way it should work for all signatures and the aliases have lots of flexibility in how they are implemented.

That's probably for the best. I simply took 'method' too literally. I assumed you would call it as a method of the object as we're doing for current delegation.

To apply _aliasWrap after the fact, we could keep the methods uncurried then at runtime we could build a Ramda instance by optionally applying the alias and then the curry.

I think we could do something like this. I think it would take a significant amount of tinkering. For me, that tinkering would start with a version of dist/ramda.js, change the existing internal curry functions to be some sort of annotation, include an addAliases function, which works as you suggest, and make the existing API the result of calling addAliases({}). Once that was working, we could change the build to generate this style file.

Btw I don't think I understand Ramda's build system, is it custom? Have you considered letting WebPack build the umd?

We've considered various tools but ended up sticking with a custom build: scripts/build, using scripts/template.js and scripts\header, based on acorn and escoden. The main rationale is the fuller control of our output. The flagship output of our system is still dist/ramda.js, and we'd like that to be clean, readable, and not full of the cruft of browserify or webpack mergers inside. (Actually, I really know more about the browserify cruft, I would love to learn that webpack just manages to put together a cruft-free readable file that still contains our JSDoc comments, but I'm not hopeful.)

I think this is doable. I don't think it's easy. I also think it's worth doing. I would really love to have such integrations.

I have the same issues as the original request, an existing JS codebase that's leveraging both immutable.js and ramda.

I want ramda to produce and consume immutable.js objects.

Consumption can be done by supporting the es6 iteration protocol (maybe a newer version than we have already does this?), and creation can be accomplished with the 'delegation aliases' that @CrossEye mentioned. I would be happy to import an immutable-specific version of ramda. I'm not enthusiastic about a build-time approach. I think delegation should leverage es6 symbols, since that's a common way to express a 'protocol'.

Alternatively, object creation could be polymorphic on the type of collection argument. This is the way I've gone about wrapping ramda and immutable.js methods, but the incomplete wrapper creates a lot of confusion to inexperienced developers. When do we use ramda methods, when do we prefer immutablejs's native map, when do we use this homegrown and incomplete wrapper?

The same work could help extend ramda over clojurescript/mori objects, which would facilitate a hybrid codebase.

I am interested in combining these two as well. Are there any plans to address this issue?

I'd love to see a PR for this. I think this would be a big win. I also think there are a lot of decisions that would have to be made, many of them fairly difficult. For instance, this _sounds_ good:

Alternatively, object creation could be polymorphic on the type of collection argument.

But what would be the expected type of

R.zip(['a', 'b', 'c'], immutable.List([1, 2, 3])

Is it List<List>, ImmutableList<ImmutableList>, List<ImmutableList>, or (really unlikely, I guess), ImmutableList<List>?

So far most of my thinking has been around the simpler end of how we dispatch properly. The code posted by @DaleJefferson is one interesting approach to this, quite different from the current, fairly simplistic, approach. I could easily see adding support for the iteration protocol on the input, but, outside such functions as into, I'm not sure we really want to start thinking of polymorphic return types for the non-dispatched versions.. That scares me quite a bit.

So my answer is that I'm fascinated by this, but haven't yet seen something that really inspires me. A PR would put a stake in the ground, and give us something more substantial to discuss.

I'd be more interested to see how you can implement cursors while working with plain objects/list and ramda/ramda-fantasy. Last bit I need for my dream functional UI.

@kujon: Can you explain in more detail what you would want?

I'm hopping in this conversation a little late, but I've recently learned about the benefits of immutable.js and really want to use Ramda with it -- Why? Because Ramda is awesome ;)

I think ideally, everything would comply with FantasyLand. That way, R.map could call arg.map. This way you can create your own functors that work with Ramda! Whats the status on this front?

I suppose this doesn't solve everything though. R.assoc simply wont work with immutable.js without being rewritten.

Ramda should work well with FantasyLand implementations. All the type methods (until some recent additions, perhaps) specified by FantasyLand are pure functions in Ramda, that also dispatch to such a method if one exists.

But you're right that this only extends so far. assoc is designed around mutable objects. But if we had delegation-by-alias,I'm assuming that there is some rough equivalent in Immutable that we could target.

What do you mean by "delegation-by-alias"?

Immutable.js has .set instead of assoc.

https://facebook.github.io/immutable-js/docs/#/Map

I'm not sure if this would be a huge nightmare or not, but it would be really cool if these could all work together. I've been using flyd lately and its amazing how well it works with Ramda :)

What do you mean by "delegation-by-alias"?

Immutable.js has .set instead of assoc.

Well, that's just what I'm talking about. If we had some way to specify that for your case, assoc would delegate to set, and not simply the current behavior where any delegation we do have is based on shared names, then many more integrations would be possible. The work by @DaleJefferson above demonstrated one possible technique for this.

Finally got around to reading this whole thread.

as it seems to me that much of the reason for using a library like Immutable is made unnecessary by Ramda's strong insistence on never mutating user data.

@CrossEye, the main benefit of immutable.js is garbage thrashing. If you have a large data structure like a huge array, and you want to R.append to it, then you're creating an entirely new array and throwing away the old array. This creates a lot of work for the garbage collector and is the primary reason people used to think that immutable data was a bad idea.

I recently learned both clojurescript and elm in order to gain an understanding of why people love these so much and I'm not sure about elm, but clojurescript has these immutable shared data structures built in.

If it is because you don't like the object oriented API you can create invokers with R.invoker. Alternatively you might consider functionize.

@paldepind has so many cool libraries, its actually ridiculous. Simply using invoker is probably the safest solution so Ramda doesnt become bloated. But I think that in an ideal functional world, everyone would be using something like immutable.js as opposed to the mutable js native types....

I believe @kujon is referring to cursors as they're prescribed in clojurescript for use with Om. Theres a popular implementation here.

For us, additional benefits for immutable.js are fast and deep value equality, which works really well with react.js dom tree diffing, and forcing devs to use the immutable API to change state. Paraphrasing Rich Hickey, a discipline of immutability doesn't work across a team. That's what we get with just ramda. The things I least like about immutable.js are the OO API and constant marshalling to/from json arrays/objects, which is another hard-to-grasp concept for devs. A polymorphic ramda API would let the same code consume and produce both immutable collections and javascript ones, it would decouple ramda from the decision of which collections library to use.

Clojure's protocols are a great tool and example for this, I have also considered extending mori(the clojurescript collections and standard library usable to JS) to do the same, but unfortunately you can't tell clojurescript how to create collections of types it doesn't know about without forking the codebase, and even if I did it, it would make less sense in the larger system than it would to do the same with ramda.

Paraphrasing Rich Hickey, a discipline of immutability doesn't work across a team.

Not sure I understand what he's getting at -- immutability is bad?

The things I least like about immutable.js are the OO API and constant marshalling to/from json arrays/objects

Amen. Its confusing to me how such a functionally-minded concept could have such an object oriented api, lol.

@ccorcos no he's saying that immutability-by-discipline is unenforcable, hence the need for a language designed with immutability first (clojure). In practice, I see a lot of confusion about it in our hybrid ember/react codebase, when there was much less in my prior clojure work (except during interop with java code).

Yeah. I'm using angular which had all kinds of mutation...

immutability-by-discipline is unenforcable, hence the need for a language designed with immutability first

once again Mr. Hickey hits the nail squarely on the head.

@paldepind has so many cool libraries, its actually ridiculous.

Thank you a lot for the compliment :) I appreciate it. Also thank you a lot for your recent feedback. I haven't had a time to look at it yet unfortunately.

+1 to keep this thread alive

@afazio check out functionize -- I'm definitely going to use it once I get back to using immutable.js again

Very interesting thread - I think the topic of how Immutable.js and Ramda.js might be used together successfully on a project codebase is a very real question right now for adopters of both libraries.

Are there any updates or general consensus which approach typically works best in terms of combined usage, or whether baked in support is still actively being pursued? Yes, there is the R.invoker approach, but I'm not sold on this. At the moment, I am kind of using each library standalone, once a Collection is in immutable.js land it just uses that universe, and Ramda is a utility belt for native js land. Certainly not ideal.

Mori claims to be more performant.

These libraries arent small though.

I recommend @DrBoolean's recent post, Lenses with Immutable.js. Lenses are an appealing solution in many respects. There are performance concerns, but performance isn't often a problem in practice.

Very nice! I missed that when it came out.

@davidchambers - thank you for pointing out the blog, and ramda-lens package! Have downloaded and had a play around with the some of the examples. In case it interests anyone, couldn't find an npm published version of ramda-lens so added dependency to latest commit ( "ramda-lens": "git://github.com/ramda/ramda-lens#5ceba153752cf874ffb94b29743de96968b8db29"). Not finished going through all the examples given in code yet, but this appears to be just the ticket.

That doesn't have persistent data structures though, right?

It appears not to.

What about taking this as a starting point and fleshing it out? It would have persistent data structures and (I imagine) would work unmodified with Ramda. Ramda wouldn't make use of the persistent aspect but other code sharing the same data could.

A simple library that wraps around immutable. Tired of doing Map.get('item')? With proxy support, you can do Map.item, like accessing regular objects!

https://github.com/zackify/immutable-proxy

immutable proxy is pretty sweet!

I did it! It's viable! Check it out:

$ git clone https://github.com/NodeGuy/immutable-proxy
$ cd immutable-proxy/
$ git checkout List
$ npm install
$ npm run build
$ cd ..
$ cat > test.js << EOF
'use strict'

const assert = require('assert')
const Immutable = require('immutable')
const ImmutableProxy = require('./immutable-proxy/lib')
const R = require('ramda')

const lessThan2 = R.flip(R.lt)(2)

it('works with a JavaScript array', () => {
  assert(!R.all(lessThan2)([1, 2]))
})

it('fails with an Immutable.js List', () => {
  assert(!R.all(lessThan2)(Immutable.List([1, 2])))
})

it('works with an Immutable-Proxy List', () => {
  assert(!R.all(lessThan2)(ImmutableProxy.List([1, 2])))
})
EOF
$ mocha test.js

  ✓ works with a JavaScript array
  1) fails with an Immutable.js List
  ✓ works with an Immutable-Proxy List

  2 passing (13ms)
  1 failing

  1)  fails with an Immutable.js List:

      AssertionError: false == true
      + expected - actual

      -false
      +true

      at Context.it (test.js:15:3)

I merged @NodeGuy's changes and released 0.0.2 if anyone wants to try it. Did this project for fun one day, it's really cool to me that it might actually help you guys. Let me know if I can help make immutable-proxy better. I already added @NodeGuy as a contributor so he can push whenever.

@zackify whoa -- i didn't realize how awesome this is! Definitely include a link to ES6 Proxy on the README. I don't think most people know what that is.

Looks like what you have there should cover pretty much everything. It would be great to see some Ramda compatibility tests -- things like R.evolve could be tricky, but I think it should work.

It would be cool to add some performance reports in the tests too. Then we could try to optimize compatibility. For example, aliasing R.assocPath to Map.setIn.

Only problem is that Proxy isn't widely supported yet and the needs native support.

@ccorcos I made a new branch that actually lets you modify values like a regular object, (basically setIn) but I am not sure if it's what you would want. https://github.com/zackify/immutable-proxy/tree/feature/mutable

I'm confused whats different about that branch? Ah, I see. The problem is that obj.x = 10 will return a new object... That should cause problems...

Well I made it basically mutate the original immutable object so that it
works like normal. Feels weird of course haha.
On Thu, Aug 4, 2016 at 18:33 Chet Corcos [email protected] wrote:

I'm confused whats different about that branch? Ah, I see. The problem is
that obj.x = 10 will return a new object... That should cause problems...

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/ramda/ramda/issues/1367#issuecomment-237703484, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAbacC5nkokdv2aa1lx5vjkISYks7SAlks5qcmiegaJpZM4F25q4
.

I see. You need a setter still. And you can mutate a reference to the immutable object within a closure -- pretty sweet!

https://github.com/Legitcode/immutable-proxy/blob/master/src/map.js

Just catching up after vacation, but this is very cool stuff, both immutable-proxy and seamless-immutable!

seems like this is being solved elsewhere then

@ccorcos what's the state of the art in combining immutable and ramda 2017? Still no complete solution?

Very shocking. These are a perfect match. Immutable brings great performance gains to react/redux applications which otherwise are copying objects right and left to maintain immutability. I'm not sure why the ramda community hasn't seen the structural sharing of immutablejs as high priority for its major performance enhancement.

It's so important that perhaps it's worth considering throwing immutablejs's API out the window and just achieving the structural sharing implementation et al that immutable had under the hood but in ramda world. I'm not the biggest fan of the immutablejs API. It's bigger than is needed and offers too many ways to get what you want done.

It's so important that perhaps it's worth considering throwing immutablejs's API out the window and just achieving the structural sharing implementation et al that immutable had under the hood but in ramda world.

sanctuary-js/sanctuary-set#1 is a step in this direction. There's still a lot of work to be done before that pull request is merged, though.

very interesting. I'll check it out...@davidchambers does sanctuary fulfill the fantasy-land spec?

does sanctuary fulfill the fantasy-land spec?

The current npm release (v0.11.1) provides Either and Maybe types compatible with Fantasy Land v0.3.x. The next release (v0.12.0) will make these types compatible with Fantasy Land v2.1.x. My goal is to release v0.12.0 by the end of the month.

@davidchambers I'm starting to connect the dots between all these projects. Yours definitely looks promising. Along with Sanctuary, can I use for example ramda-fantasy's Future until/if your project has it?

Along with Sanctuary, can I use for example ramda-fantasy's Future until/if your project has it?

Absolutely. Other options are Fluture and data.task.

I'm not sure why the ramda community hasn't seen the structural sharing of immutablejs as high priority for its major performance enhancement.

The Ramda core team is very split on the whole notion of dispatching, which would probably be essential for useful inter-operation. And even if that's settled in favor of significant dispatching, we'd probably have to reintroduce some sort of aliasing, or allow users to do so in order to smooth over differences between Ramda's core and Immutable's API. I'm quite in favor of this, but it's far, far from trivial. And we need to make a real decision on dispatching first.

So I've pretty much given up on integrating Immutable.js with Ramda particularly because of the way Immutable.js works. Immutable.js does all kinds of manipulation of the underlying data structure, so using Ramda's methods are likely going to be much less performant than using the baked in methods.

The withMutations function could help but even that is likely going to be less performant.

Ramda does dispatch to prototype methods which works if the names line up, but I also think that this is a very odd functionality... In fact, if this is all you want to do, you don't need ramda at all!

const pointfree = (name) => (...args) => (last) => last[name](...args)
const filter = pointfree('filter')
const map = pointfree('map')
const reduce = pointfree('reduce')
const pipe = (fns) => (init) => fns.reduce((acc, fn) => fn(acc), init)

This dispatching functionality conflates Ramda's responsibilities and makes it hard to do things like lazy evaluation with generators.

@CrossEye I've been researching various old threads/issues and I see that.

From a high level business perspective, it seems that the community (and the community of would-be/soon-to-be functional javascript programmers) would benefit from serious project unification. Between Ramda, Ramda-Fantasy, Folktale, Sanctury, things like immutable-ext, etc it seems that one joint project with high level goals--if agreed upon--could provide some serious value to the javascript world. Basically it seems everyone wants to head in this direction with their React development, but don't have a complete set of unified tools to rely on. That said, Ramda is definitely carrying the flag--definitely in name--as the first major step toward functional development. I remember when I first heard about it a year and a half ago, and I didn't really expect it to be more than a niche tool, and now it's become the tool the "cool kid" javascript developers are flocking to. That's really something!

However, I also feel like what happens is people first hear about Ramda and they think that's all they need to do to follow the functional path. But then as they research what it would take to fully follow that path they get severely bitten by the complexity of monads etc which are required to go all the way. So instead of taking a half measure and going to Ramda, they don't do it at all. But if they knew they could invest their time in one complete set of tools (with a beautiful marketing website presenting it as such), they might be more inclined to bite the bullet. In addition, these people are also juggling possibly using Immutable, but then when they find out that the functional js tools aren't their yet, they get discouraged again. Perhaps I'm just sharing my own experiences, but I don't consider myself that abnormal--just a React application developer looking to enhance my practices while delivering more reliable code, while studying the obvious trending towards true functional programming emerging in the js world.

I mean this is everybody right now. I know I'm not the only one feeling this way and coming to the same conclusions based on research into other people's experiences and functional programming tutorials. For me, it just finally clicked--after a year of dibble dabbling and studying--and I've just begun refactoring my codebase to use Ramda. I plan to use Sanctuary and/or Folktale. My previous refactoring involved rewriting all my React components as functional stateless components and writing easy tests for them using Jest. It's all resulting in more reliable code than I've ever written before. People need to jump on this lol.

However without unification and a github.io style documentation site that feels as tight as something Facebook would produce, people are still scared. And I'm not talking just Ramda, but Ramda + Fantasy + Immutable + etc + documentation for usage with Redux and React. I think the time is now. Facebook has a monopoly on every one of these power tools that coms out. I can't even grasp how Immutable is still even a thing. It came from its importance in the functional world, yet has an OOP API, and yet there doesn't exist a proper answer to it in the wold of curried fantasy-land functions. The closest thing is actually Clojurescript's Mori, which is only partially point-free. So that's a no-go. After all, the mind-set of us functional newcomers is often this: if we're gonna invest the time and make the big switch, we want to do it all at once using the latest and greatest. I.e. take a full measure, not a half measure ("Breaking Bad" quote), get it done, and don't look back. On projects where that's possible, I've found that to be the most efficient approach. These are usually greenfield projects. Greenfield projects I'm forecasting are also the best to target from a marketing standpoint. They can more easily switch to any of this. Early adopters pioneer the way for any new application or tool. That seems to be correct in my experience.

So all that said, I think if a complete package could hit as hard as Facebook does with its tools: React, Flow, Jest, Redux (i.e. since they hired Dan Abramov), etc, there would be a huge marketing opportunity, which would be nothing but a boon to everyone because of the contribution that will occur as a result. RAMDA-COMPLETE paired /w REACT/REDUX

What would that look like? I'm guessing it would mean a Ramda that is incompatible with the original Ramda built from the ground-up with the fantasy-land spec in mind, structural sharing a la Immutable js, best in class lazy evaluation support, and perhaps Sanctuary's runtime checking. Observables? Anything I'm missing??

On top of that, docs geared towards usage with React and Redux usage would be key. The docs seem lacking: for example, better docs exist for Ramda-Fantasy but people in the community seem to be indicating that Folktale is more complete. While I'm sure there are reasons each developer took their own route, that's also a missed opportunity in collaboration. We need to reconcile those reasons, come to agreement and work together. At the end of the day first class docs will be the lynchpin. Like I said they need to show developers how to use these tools specifically in React. Everybody's kinda wondering how to use it in React, or they're mistakingly thinking they're getting the best of functional programming just by writing pure stateless function components. The "one true way"--and I'm not necessary saying there is one or that I know it--needs to be shown to them, and presented as such because being able to check off that you're doing things the right "one true way" attracts developers. That's just how Facebook presents all their tools. There's an opportunity to do the same thing in fantasy land :). So whether there is "one true way" or not doens't matter--we need to come up with the best formula we can come up with. I don't know how you guys feel, but looking at all you've done, I certainly feel like I have a sense of what it is.

Next: the documentation should also make use of Jest to show how testable the result is--because after all one of the biggest selling points of functional programming is just how functional it is. Developers will love seeing ramda-complete or whatever it ends up being called meshing perfectly within Jest. On a side note, I strongly believe the Ramda name is worth keeping. It's been burnt into the back of my skull since I heard about it, and, like a cyborg, I seem to have developed some AI within myself, which I can't control, which keeps track of its growing popularity. Just as Jest re-emerged to new greatness this year, Ramda could and should do the same, even if that means dropping compatibility. It's not like the old libs are going anywhere.

So seeing ramda whatever integrated with Jest--which itself in one fell swoop took over the react testing world--will do even more wonders for marketing. And then perhaps there's some integration with Flow, and I'm sure @davidchambers has thought about it, and maybe there isn't which is why he's gone with runtime checking. But if there is a potential there, it should be considered in our group of 3-8 top priorities.

Overall, the point is alignment with the tools that make up the "react stack" will insure Ramda's place among them. It also seems this will complete React's destiny toward functional programming. After all--and I'm only saying this from my own perspective--React has been what has exploded functional programming into the mainstream. It makes nothing but sense that the biggest player [Ramda] in functional javascript tools gets cosigned by the Facebook React Mafia, lest they do it themselves.

It's a missed opportunity if ramda world doesn't unite to fulfill this destiny which the React world has bestowed it. Call it "magpie developer" syndrome, call it whatever you want. It can be portrayed in such a pejorative light, or it can be portrayed as something very positive: proactive developers striving to be the best and build the best stuff, given the constraints they perceive (namely that they must do it in javascript). So ramda can be the light those magpies gravitate towards. It already is. Now it just needs to shine to its greatest potential.

That's all I have to say.

For my part I'm going to document in a series of blog articles the refactoring of my codebase to use Ramda and likely Sanctuary. There has emerged over the past year some great articles and videos about learning functional programming in general (the one on Egghead by Dr Frisby, aka Dr Boolean, is absolutely fantastic). This set of articles I also found highly useful: http://randycoulman.com/blog/categories/thinking-in-ramda/ . And the following book--though i didn't fully understand it at the time--planted some powerful seeds for me: http://learnyouahaskell.com . However there hasn't been enough connecting the dots between functional programming and Redux. And I think Redux is the key. Here's like the only one that comes up: http://randycoulman.com/blog/2016/02/16/using-ramda-with-redux ...and it's also by Randy Coulman. I actually didn't find it nearly as useful and in depth as his "Thinking in Ramda" series. What I wish I had right now is a series connecting the dots from:

1) the moment you retrieve your data in Redux action creators to
2) reducing it in your store to
3) connecting it to your components to
4) using it within your components.

That's a perfect 4 part series right there. And that's what I plan to write once I've mastered my process using your amazing tools. Ideally I'd also describe the direction functional programming within react is going, i.e. the stack of functional libraries I recommend, what they're missing, and what is in the pipeline. It would be nice to at least come to some consensus on what the community's wish-list is. Perhaps I just need to do some more digging. Either way, I look forward to learning from you guys to get a better history of the fragmentation that occurred, where you guys think it needs to go, and how to reconcile the efforts that already exist and new ones.

@ccorcos it seems structural sharing would have to be implemented from the ground up just for ramda and libraries that meet the fantasy-land spec, which is also the conclusion I came to above. I'd like to hear more from others regarding how we could achieve structural sharing in ramda + fantasy land libraries, even if not through immutablejs.

@faceyspacey You might have the wrong idea about what Ramda is and what Ramda isn't. Its a utility library and its generic. Its not going to cater specifically to something like Redux. However, if you have some ideas how to make that more compatible, definitely write about it, and if there are some functions you want, then submit a PR for create a ramda-contrib package.

On a side note, you remind me of myself when these things started to click with me. I was frustrated that there was all this awesome stuff (generators, immutable data structures, point free functions, monads and functors) but there wasnt a cohesive library that puts all of this together. I had lunch to Brian (Dr Boolean) and he told me to just build it -- there's a lot of low hanging fruit out there. Build things as you need them. If you've watched some of his Egghead videos, you'll notice that he never prematurely built something he didnt need. He started with Box and went from there. For guys like him who know all this stuff, its like a philosophy more than anything. That said, he's a huge PureScript fan where all of this stuff is built into the language. Same with Elm. Also, check out this fantastic article about zero-cost abstractions in Rust.

I'm with you about these grandiose ideas about a "standard library" that has all this stuff baked in for you. But there inevitably going to be opinions. And if we want to support Flow or something, thats a bunch more work to figure out. If you throw together a google doc with some of the features you want to include and which you want to exclude, I'd be happy to look it over and help you build something.

P.S. I build this little project for fun after my last comment :) https://github.com/ccorcos/fluent-pointfree

import pointfree from 'fluent-pointfree'

// pointfree is just the identity function
pointfree(10) // => 10

// but you can chain on it to build up a computation
const sumEven = pointfree
  .filter(x => x % 2 == 0)
  .reduce((a, b) => a + b, 0)

sumEven([1, 2, 3, 4]) // => 6

@ccorcos nice, i'll check out what you're up to with fluent-pointfree. ...I think based on what I'm reading in the threads there is agreement that there has been fragmentation and there is basically agreement to a direction for at least some of this to go. I never wrote anything about catering to redux. Nothing specific for redux needs to be coded at least. Rather, what's available just would benefit itself by becoming even more accessible to redux/react developers. Also, I'm only speaking in a ramda github thread because here is where the most discussion about these related projects is happening.

The first step seems to have been fantasy land, and from the looks of it, it looks like folktale or sanctuary is about to cross the finish line. Perhaps that's the only goal that matters right now. And perhaps folktale already has crossed the finish line, and just needs better documentation. Ramda-fantasy doesn't seem to have been taken as seriously unfortunately. On a side note, I do think the Ramda name is very important for marketing, and from that standpoint it's unfortunate that ramda-fantasy seems to not be the solution of choice.

In response to your suggestion that I put together a spreadsheet with essentially my wishlist--the list is short. I'm a practical dude. Structural sharing is the one and only thing on my list. In plain speak, I need to know I'm using both the most performant option available to me and the most maintainable/elegant one. I'm self-taught all the way--reads: my cs skills are lacking, though not non-existent--and this is something I'm highly interested in doing to grow my own skills. Time is not in abundance though. Maybe we could do this together as a side project?? I think you were the main one in the past that was also interested in immutablejs's structural sharing capabilities. We also know each other from the Meteor world. At least I remember you :)

But back to the idea of dreaming up the ultimate functional toolbelt from the ground up. If it was just about me, I'd be satisfied with the structural sharing thing. But it's not just about me. I have no needs for observables, but I'm pretty sure that's high up on many other people's list, and I'm not sure if Flyd checked that box. I've read many of the threads, and most of them are inconclusive, while leaving a long wish-list. So I think we could benefit from bringing up the discussion again about how to best integrate these other needs and work together (rather than each individually re-invent the wheel). However, I know I'm not the best one to lead it (though I don't mind opening it). Since you've been doing this longer than me, what's your dream toolset here? And in general, what do you consider the obvious next stage? It seems painfully obvious to me that it's structural sharing (likely without immutablejs involved at all, i.e. coded from the ground up) and observables using the same interface as the rest of ramda + fantasy land (with observables obviously bringing a few tricks of its own). How do you see it?

Wow, I haven't checked out Sanctuary in a while (probably a year) -- it really shaped up!

Structural sharing is the one and only thing on my list.

Then use ImmutableJS. It's awesome. It may seem object oriented because when you look at the code, they're using class, but at the end of the day, that's the most idiomatic way to do it in JavaScript. You can use a constructor that returns an object literal, but that can have performance costs since you have to reinterpret the function every time rather than reusing the object prototype. It also lends itself to extending via subclassing and so long as you're using it functionally (returning a new object from every method and no in-place mutation), I don't thing there's anything wrong with it. My perspective is use the right tool for the right job.

What's your dream toolset here?

I basically just want a standard library like Sanctuary where everything follows some convention like FantasyLand. But things start getting tricky when you go against the grain. Promises use .then rather than .map and .chain. ImmutableJS uses .get rather than .prop. Also, ImmutableJS is going to faster internal implementations for things like .map and .filter so we'll end up having to dispatch to prototype methods anyways. But lastly, I want lazieness for performance in operations like range(0, 100000000).filter(isEven).take(2) and that involve generators and whatnot. Lodash has some of this which is cool, but getting everything to work seemlessly together is hard.

@ccorcos regarding:

Then use ImmutableJS. It's awesome. It may seem object oriented because when you look at the code, they're using class, but at the end of the day, that's the most idiomatic way to do it in JavaScript.

I mean the goal is to do so in a point-free fantasy land style. as @davidchambers mentioned there is a PR for ordered sets for Sanctuary: https://github.com/sanctuary-js/sanctuary-set/pull/1 . It is possible.

So ways will just have to be found to do it as performantly as possible in fantasy land. Who wants a frankenstein project using partly oop immutable js and partly pointfree, not to mention any performance hits from ushering back and forth between the two. My opinion is that if it's at all possible to avoid frankenstein projects, it should be a top priority, if not a requirement, particularly for new greenfield projects.

We likely can find creative ways to implement structural sharing that are just as peformant--if not more so--than immutablejs.

@faceyspacey:

Well, your vision of Ramda' future is certainly more expansive than my own! :smile:

For instance, the notion of merging Ramda with Sanctuary makes some sense. I don't agree; I believe it's better to have competing ideas, to have different systems trying different ideas: the whole marketplace of ideas concept. But there is a reasonable argument to be made there. But the idea of merging with Ramda-Fantasy or Folktale makes no sense at all to me. Ramda has a fairly small, well-delineated role: a library introducing functions common in the FP world -- or otherwise useful -- into JS, things to work on existing data structures. It introduces almost no types of its own. Folktale and Ramda-Fantasy are entirely about creating (Fantasy-land compatible) types.

I've never thought of Ramda as even trying to become a one-stop shopping center for functional programming in JS. If nothing else, you have to understand how it started: it was a learning tool for @buzzdecafe and me. We used it to explore how well we could do some basic FP in JS. That it has turned out useful to others is gratifying, and has shifted our focus somewhat, but we are never trying to be the lodash of FP. Our concerns are very different.

So when you suggest a

Ramda built from the ground-up with the fantasy-land spec in mind, structural sharing a la Immutable js, best in class lazy evaluation support, and perhaps Sanctuary's runtime checking.

that also possibly deals with Observables, you are not talking about anything even like Ramda. You are talking about a massive library that complements Ramda in parts and conflicts with it in others. While Ramda's list and object mutation does structural sharing already and never mutates user objects (see functions like adjust, assoc, update, lensProp, lensPath, and their ilk), building out a library of data types to compete with Immutable makes no sense. As I mentioned before, there is a mixed feeling here about extending our dispatching to make Ramda work better with things like Immutable, but it seems unlikely to ever be seamless. And as to lazy evaluation: do you make that the default? If so, most likely all current users will need to change all their code to deal with fetching the final value from a lazy chain of functions. Fantasy-land integration, OTOH, is already fairly deeply embedded. We are not up-to-date -- we'll see if that can be fixed over the holidays -- but we do integrate well with Fantasy-Land compatible types.

You also discuss an entire tool-stack with which Ramda should inter-operate. React, Flow, Jest, Redux. While there are certainly plenty of JS developers using similar stacks, why would you privilege it over other uses? I personally am using none of these. At work, it's Node and Express or Koa with occasional Angular work, tested with Mocha. At home it's Node with either no framework or whatever one seems most interesting/convenient: Cycle is the current favorite. Testing is similarly variable: Tape, Ava, jsVerify, among others. Why should the Facebook stack be granted special status over these tools?


I'm not quite as negative as it sounds. I would love to see people develop such a library. I would be honored if they chose to do so on top of Ramda, or simply borrowed ideas or code from Ramda. I do think, though, that if they hitch themselves too closely to the Facebook stack, in a few years they'll be in the same position as libraries that hitched themselves to jQuery or Backbone or Angular.

Even more, I look forward to the articles on your refactoring. I think such posts are among the best things that can be done for the community around Ramda.

I am very impressed by that community. For whatever reason, Ramda has attracted much more than its share of very smart, helpful people. Their ideas have helped improve the library far beyond what @buzzdecafe and I originally contemplated. Usually the ideas are not as wide-ranging as yours, but I appreciate ones such as this too. Even small pieces of your suggestion could add a lot to Ramda and to the cause of improving the quality of JS by using FP techniques.

Nevertheless, I'm afraid you've bitten off far more than I want to chew.

@ccorcos:

I build this little project for fun after my last comment :) https://github.com/ccorcos/fluent-pointfree

I'm a little confused as to the utility of this. Is the only advantage that it's points-free?

That is, this has the same behavior, no?

const sumEven = val => val
  .filter(x => x % 2 == 0)
  .reduce((a, b) => a + b, 0)

sumEven([1, 2, 3, 4]) // => 6

Are there other advantages? (I haven't dug into the implementation using a Proxy yet.)

@CrossEye Thank you so much for taking the time to respond in depth. I guess I'm bringing up quite a bit to "chew" on when the project already has a long history discussing such things. Probably the most practical thing for me to do for now is just to focus on one thing, and probably what makes the most sense is the one that was the original basis for this issue: immutable js and structural sharing. You mentioned:

While Ramda's list and object mutation does structural sharing already and never mutates user objects (see functions like adjust, assoc, update, lensProp, lensPath, and their ilk), building out a library of data types to compete with Immutable makes no sense.

Do you mean that makes no sense for ramda, or at all? I'm having a hard time dissecting whether the pushback is just because of difficulties with ramda or because of its general utility. And if it's difficulties with ramda, are the difficulties with the implementation or changes to the interface?Simply it seems to me that if I pass an object to some utility function and modify one key, I don't want a completely new object back if at all possible, i.e. like immutable. Is ramda capable of that? And how could that feature in it of itself be nothing but a good thing.

So the only conclusion I can draw--pending the answers to those questions--is complete structural sharing would basically gut the current implementation and would be less performant for typical scenarios that ramda was originally made for (as various additional indirection would now be required). Would that be a correct characterization. Or do you see structural sharing also breaking the original API contract similar to what you mentioned about lazy evaluation? Or, am I just missing something, and structural sharing like immutable is completely a bad thing, or some other misunderstanding specific to an fp implementation.

@faceyspacey:

What makes no sense for Ramda is to try to create lots of types. It's not that it makes no sense at all for another library to do this. That might be quite reasonable. But it seems too far from Ramda's core.

Note that Ramda's mutation-like functions already have some structural sharing. We don't implement standard lists, which could share tails. And that would be something I could see being a very interesting addition to Ramda, although I don't know how it would sit alongside our Array implementations. But if you look at assoc, which is the basis for most of Ramda's object "mutation" functions, it shares as much of the source object as it can, and the individual elements of "mutated" arrays are shared by reference as well.

So if someone thought that an Immutable-like library designed to work well with Ramda was a good idea, I would have no objections at all. I was thinking Ramda would be the one to try to move in that direction, but little has happened toward that.

But if you look at the tests for assoc, you will see that there is already some structural sharing built in. I am not a user of Immutable, so I may be misunderstanding a bit, but I thought a big part of Immutable was its group of Collection types. That's where I don't see Ramda growing into something like that. I think of Ramda's job as operating on standard or custom types, but not as supplying such types.

So, in short, Ramda embraces structural sharing already where it can. The biggest gap is that it doesn't have head-tail lists, using only index-based arrays instead. Plugging this gap could be interesting, would certainly be controversial, but to my mind would definitely be worth investigating. But it makes little sense for Ramda to try to supply Map, Stack, Record, OrdererdMap, et al types. I think it's efforts are much better focused on working with any existing implementations.

I'm curious if others disagree with this analysis. @buzzdecafe, @davidchambers, @asaf-romano, @kedasho, @paldepind, @raine, @scott-christopher, @TheLudd?

@CrossEye quick idea: Most the immutable methods are the same for all object types, with a very small percentage of different methods between Map and List, and basically the set and ordered types are just faster for certain operations. Basically, a system can be built where ramda (or other library) doesn't require its users to specify types (if that's the main concern), but rather the implementation dynamically makes use of the type it receives. And I would add, perhaps we forgo--definitely to begin with--the concept of sets and ordered maps, i.e. only maintain a single list and map type just like javascript. Then internally implementation code simply determines whether it's dealing with an array or a map to determine which structural sharing algorithm to use. In short, based on what you just described, we're talking a research project to extend the structural sharing you're already doing to be as in depth and intense as possible, and maybe it's already there. That's all. I'll return with some findings. :).

@faceyspacey:

Please do investigate, but look at how our current dispatching works. Some of your work might already be done for you.

...However, you might also want to look at some of the discussions around limiting or eliminating dispatching: #1900, #1938, #1949, #1950.) It's not at all clear that we'll be keeping this. :frowning_face:

I would like to think that many of Ramda's current functions could one day be defined generically in terms of the FantasyLand specs (optimising for known built-in types where necessary). This would lower the barrier of entry for many other data types such as Immutable.js just by implementing a method like reduce.

@CrossEye

That is, this has the same behavior, no?

Yes, thats the same behavior

Are there other advantages?

Well you could continue to compose just by dotchaining...

filterEven = pointfree.filter(x => x % 2 == 0)
sumEven = filterEven.reducer((x,y) => x + y, 0)

Its just a little experiment... I'm not going to actually use it anywhere 😄

I found an alternative project that can do this via support for iterables: https://github.com/kevinbarabash/functify

Regarding Immutable itself (not Ramda), it's pretty easy to expose a curried interface, which lends itself well to use in Ramda compositions. This is a little example:

i.js:

import R from "ramda";

module.exports = R.mapObjIndexed(R.invoker, {
    toJS: 0,
    toObject: 0,
    toArray: 0,
    keys: 0,
    reverse: 0,

    entrySeq: 0,

    map: 1,
    filter: 1,
    filterNot: 1,
    sort: 1,
    sortBy: 1,
    groupBy: 1,
    get: 1

});

usage example:

    import I from "./i";
    import Transfer from './Transfer';

    // uploads is an immutablejs object
    const {
        pending = [],
        complete = []
    } = R.compose(
        I.toObject,
        R.map(R.compose( // I.map = lazy evaluation, R.map = eager eval
            R.map(([ key, upload ]) => <Transfer key={key} upload={upload} />),
            I.entrySeq
        )),
        I.groupBy(R.compose(
            status => status === "load" ? "complete" : "pending",
            I.get("status")
        )),
        I.sortBy(I.get("started"))
    )(uploads);

It's pretty cool doing it that way, as you get to use Ramdas sugar and functions with Immutables immutability and lazy evaluation etc. There isn't much to be gained from moving this sort of thing from userland to Ramda core, as you can already pretty much do it all.

Whoa, what a recipe! + 💯 @neftaly! That's what I would consider a satisfying answer to this question.

On thing I would add in terms of laziness is using withMutation for composition:

I.compose = (...args) => (value) =>
  value.withMutations(v => R.reduce((acc, fn) => fn(v),  R.reverse(args)))

withMutations is effectively lazy so you dont have to unnecessarily shuffle around and persist the internal datastructure of every intermediate value.

@faceyspacey: I feel you on trying to find the bigger picture. Fortunately I don't think it requires real concessions on the part of Ramda like @CrossEye feared.

Points you mentioned wondering about:

Redux reducers:

I agree figuring out Ramda with state management libraries like Redux are probably key to finding fantastic use for it in web dev -- and I'm saying this while I haven't even really gotten there, while my web dev already consists of Ramda calls.

RAMDA-COMPLETE paired /w REACT/REDUX

What would that look like? I'm guessing it would mean a Ramda that is incompatible with the original Ramda [...]

Nah, not incompatible -- Ramda doesn't need to make any concessions here, cuz it's doing its part right. I commented a bit on using Ramda with the likes of Redux here.

The point is just ensuring you can use point-free functions, as that's where Ramda will shine in cutting boilerplate. This is a matter of argument order, but considering currying, that's not something where Ramda could budge, so the sole question here is how to make Redux take functions like state => state or param => state => state, as those would correspond to Ramda's unary and binary functions respectively.

Redux expects state => action => state though. If we'd need to turn a binary Ramda function into that, why not just use R.flip?
Let's say we do our imports import { append, prepend} from 'ramda';, define our let fns = { append, prepend };, then fix them e.g. let payloadReducers = R.map(R.flip, fns). For unary functions you may need to still handle ignoring the other parameter (be it action or state).

For binary Ramda functions, in the article you linked, it shows a function:

const createReducer = (initialState, handlers) =>
    (state = initialState, action) =>
        propOr(identity, action.type, handlers)(state, action)

... that'd allowed you to write your reducers using an object:

let reducers = createReducer([], {
  ADD_TODO: (state, action) => ...
})

Now, let's say you put action say we used:

const createReducer = (initialState, handlers) =>
    (state = initialState, action) =>
        propOr(identity, action.type, handlers)(action.payload, state)

Note the swapped parameters. Also, Redux leaves all info in these free-style POJO Actions, but to use other types, e.g. simple ones like number, I'm assuming a single 'parameter' put into an Action's payload property.

Then you can do:

let reducers = createReducer([], {
  REMOVE_TODO: R.drop,
  APPEND_TODO: R.append,
  PREPEND_TODO: R.prepend,
})

Or, better:

import { drop, append, prepend } from 'ramda';
let reducers = createReducer([], {
  drop,
  append,
  prepend,
})

Yep. That drop spans the action name, the logic to convert action payload + state to a new state, and could even contain types for inference.

fantasy-land:

Cross-Eye:

Ramda should work well with FantasyLand implementations.

Structural sharing:

I'm not sure why the ramda community hasn't seen the structural sharing of immutablejs as high priority for its major performance enhancement.

The Ramda core team is very split on the whole notion of dispatching, which would probably be essential for useful inter-operation.

So that R.invoker appears like it may be able to help out for Immutable.js interop (minus having the same functions work on everything), but I think @faceyspacey's intent may not have been restricted to that.

Ramda's list and object mutation does structural sharing already

This is probably good then. I'd wondered a bit about similar room for improvement in performance though. So Ramda, as it stands, it about providing ways to operate on JS structures without mutating them.

Now, so does Immutable.js, which additionally appeared to have implemented some performance optimizations in this regard in the form of batching mutations. While I haven't fully grokked what's going on in their implementation of that, in my understanding, the point is to allow intermediate operations to function in a mutable way, as their input is guaranteed not to be reused, potentially saving some time on copying operations.

In the context of Ramda, I could imagine this as potentially relevant as well: within R.pipe / R.compose, I could imagine inputs of any functions after the first one may theoretically be safely mutated, as they are guaranteed not to be used more than just there.

Now, I'm not quite sure of an elegant way to implement this (my only idea is to say have R.pipe set a flag on functions it's about to call, where Ramda functions could check these flags on invocation, switch them off again, and use this as a signal to go into 'mutation mode'), but... yeah.

I could imagine that to be possible, and a cool feature toward users of Ramda, though I have no illusions this idea is remotely likely to find favor among those on the contributing side of Ramda. Consider it an idea for Ramda 10.0.0 and beyond (lower priority, and in search of a nicer solution) in case we've run out of other cool features to implement!

Lazy eval:

Given dispatching is already available in Ramda, this would seem like a matter of extending generators/iterators with methods (the likes of map/filter/head/take) so you could call into these with Ramda's point-free functions.

That said, I haven't found much of a use for this myself, which makes me curious. Would you mind elaborating on common use-cases for this you're foreseeing?

Curiosity aside, I have actually done something similar in the past -- you may want to check out this javascript-iterators repository. Not methods dispatchable by Ramda, but the currying for composition purposes could be fixed.

Type checks:

Compile-time: you wondered about options for Flow. There probably isn't automated conversion, but feel free to check out the TS typings for inspiration.

Run-time: you mentioned sanctuary, which definitely seems the next step from Ramda considering the plans to merge ramda-fantasy into it. If you're not using compile-time checks, this also seems like the best way to ensure your inputs to Ramda-like functions are good.

On the other hand, if you're already using compile-time checks, and the reason you'd pick run-time checks is because the checks cannot be performed at compile-time yet, e.g. because you're verifying data coming in over the wire, you might also be interested JSON Schema, which was essentially made with that purpose in mind.

The schemas can get verbose compared to TS/Flow, but it is fairly powerful. At this point you might wonder about conversion between these run-time/compile-time typings; options appear available to Flow, from TS, and 2 to TS.

Observables:

On this point I'd wonder, what is it you're looking for? Things that come to mind:

  • obs.map(R.whatever): already works
  • R.map(R.whatever, obs): already works by means of dispatching

So that'd leave... maybe function versions of e.g. RxJS's methods, curried, to allow point-free style on those as well. I'm not sure if this had been done, though until ES-next standardization of the Observable proposal, this would seem dependent on the implementation (e.g. RxJS), in which case it would seem too subjective / non-standardized for Ramda to take part in.

I started to try and see if I could do a variation of @neftaly's point-free Immutable functions above, and got like halfway through the almost 100 functions, then realized I wanted a better way, especially with consideration of talk that Ramda is considering doing less in terms of point-freeing everything. It occurred to me it might be possible to use Reflection to look up the method names and parameter lengths as used in his version, so tried to use those to generalize that to cover RxJS as well as say JS's standard library in one go.

...

Would I find it interesting to have Ramda-style point-free manipulation that'd include ES6+ classes (Promise, Iterator, Set, Map), and future additions (Observable)? Yeah. This does raise some questions since these aren't really hooking into Ramda's dispatching well so far (that Iterable's map() being a third-party function, Observable having a bunch of methods including a chain again named differently (mergeMap / switchMap)), but yeah. That does feel like the category theory story that Ramda as I percieve it has been selling.

Another library that is trying the solve the "Lodash/Ramda for iterables/async iterables" https://github.com/ReactiveX/IxJS

anyone tried this?

Immutable.Collection.prototype['fantasy-land/map'] = Immutable.Collection.prototype.map;
Immutable.Collection.prototype['fantasy-land/chain'] = Immutable.Collection.prototype.flatMap;
Immutable.Collection.prototype['fantasy-land/reduce'] = Immutable.Collection.prototype.reduce;

It seems to make it kinda work with ramda....but still far from ideal.

anyone tried this?

Never thought of it, but interesting...

It made it work for some of the functionality over here.
It'd be worth trying to blend this to what @DrBoolean's done with immutable-ext...so as to export an Immutable-Fantasyland kinda thing...

Here's a very simple solution based on @ccorcos 's pointfree.

It supports all Immutable functions and plays nice with Ramda (unfortunately @ccorcos 's pointfree has issues with fn.length and fn['fantasy-land/map'], etc.)

const I = require('immutable')

// call a method name on an object
const dispatch = name => (...args) => (obj) => {
  if (typeof obj[name] === 'function') {
    return obj[name](...args)
  } else {
    throw new TypeError(`${obj} does not have a method ${name}`)
  }
}

// Properties are invoker functions with arguments set in advance
const pointfree = new Proxy({}, {
  get: (target, name) => dispatch(name)
})

// *** USAGE *** //

const a = I.fromJS({ foo: '' })

console.log(
  R.pipe(
    pointfree.map(x => x + 'hello, '),
    pointfree.map(x => x + 'world'),
    pointfree.set('bar', 'baz'),
    pointfree.update('something', x => 500)
  )(a)
)
// Map { "foo": "hello, world", "bar": "baz", "something": 500 }

This version of pointfree sacrifices chaining to be able to interop with Ramda.

In relation to this thread and for the fun of it I've been experimenting with implementing data structures for immutable lists in JavaScript. I've experimented both with finger trees and relaxed radix balanced trees.

What is interesting is that according to the benchmarks I've created my implementations perform very well compared to existing libraries. So I've been thinking about turning the experiment into a full-featured immutable list implementation with a Ramda-like API. The idea being that using Ramda together with this immutable list should be as seamless as possible. For instance, my idea is that the functions that operate on the immutable list should have names and semantics similarly to those that Ramda offers for native arrays.

The current state of the experiment can be seen here along with some more details. I'd love to hear what people in this thread think of the idea and how best to make an immutable list implementation work well with Ramda.

@paldepind:

I think it's a great idea, though I'm not personally knowledgeable on what current implementations are like or how to beat their performance. This definitely appeared to be the missing link between Immutable.js (focused on performance benefits) and Ramda (in my eyes focused on pretty composable point-free API).

A combination like that may share quite a bit with the above-mentioned point-free approach of using Immutable.js itself as well though. I hadn't actually tried that (stuck with Ramda), but I think your project sounds pretty interesting.

Without having checked much, you could consider fast.js to your benchmark. I've no idea if they're (still?) competitive, but who knows.

The idea being that using Ramda together with this immutable list should be as seamless as possible.

As Ramda just iterates over arrays in old-fashioned loop fashion, if you want Ramda itself to work on your data structure, so as long as you expose length and a numerical index, it wouldn't realize it's different from a regular array. That said, just this won't net you the benefits of your superior algorithm implementations, and Ramda will just spit out old-fashion arrays back at you.

To actually have it trigger your more performant implementations though, I guess you'd need to use that dispatcher system so it'd know you're exposing compatible implementations.

@tycho01

Thanks a lot for the feedback. I am happy to hear that you like the idea :smile: I definetely agree with your points and I've been having similair thoughts myself.

I'm not personally knowledgeable on what current implementations are like or how to beat their performance.

For the few operations that I've implemented so far it seems to be going well (unless I've scewed up in my benchmarks). I will experiment with adding fast.js to the benchmarks. Including operations on arrays is a nice point of reference to have.

As Ramda just iterates over arrays in old-fashioned loop fashion, if you want Ramda itself to work on your data structure, so as long as you expose length and a numerical index, it wouldn't realize it's different from a regular array. That said, just this won't net you the benefits of your superior algorithm implementations, and Ramda will just spit out old-fashion arrays back at you.

My idea wasn't that Ramda's functions should work directly on the immutable lits. As you say, that wouldn't really work. Instead the idea is that the functions on the immutable list would map pretty much 1-to-1 with the functions that Ramda offers on native arrays. It would look something like this:

import * as R from "ramda";
import * as L from "@funkia/list";

R.compose(L.dropLast(2), L.map(fn), L.filter(predFn))(L.list(1, 2, 3, 4, 5));

So, basically, for each R.foo that operates on arrays there would be a corresponding L.foo that operates on the immutable list. That makes it pretty seamless but also adds a bit of explicitness about which data structure one is working with. At least that is my current ideas.

https://github.com/funkia/list

This is great to see. I think it's a very good idea. Mori tried this a while ago, but that seems to have been abandoned.

My idea wasn't that Ramda's functions should work directly on the immutable lits.

At the moment, if you exposed these functions as methods, they mostly would integrate directly with Ramda. But I would certainly understand if you're not interested in doing that.

@CrossEye

This is great to see. I think it's a very good idea.

Awesome. I'll continue on with the project then :smile:

At the moment, if you exposed these functions as methods, they mostly would integrate directly with Ramda. But I would certainly understand if you're not interested in doing that.

One problem I see is that it would only work on a subset of the functions. For instance, in the example above L.map could be changed to R.map. But L.dropLast could not be changed to R.dropLast because R.dropLast does not dispatch. So users would have to know and remember which functions dispatch and which doesn't. That seems a bit problematic to me. What do you think?

Man, List has really taken off! Congratulations @paldepind! You are a noble open-sourcer. Taking an issue and making a library out of it.

Thanks a lot @loganpowell 😄 I'm happy to see that people are interested in the library.

good job! wish EcmaScript would accept a standard library of immutable structures like that just like Scala. :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

esamattis picture esamattis  Â·  40Comments

entropitor picture entropitor  Â·  30Comments

sadasant picture sadasant  Â·  55Comments

allangomessl picture allangomessl  Â·  34Comments

Siilwyn picture Siilwyn  Â·  43Comments