-
-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support esnext module format for tree-shaking purposes #246
Comments
I'm aware that this is possible, but I don't know any of the details. Can you recommend a resource for learning about this, @mrtnbroder? |
If you want, I could give this a shot myself. It's actually not that 'hard' to do, just tedious. But the effort is worth it. Actually, it's just looking at the modularity of your project and splitting parts of them into their own file/section, so one could It's a bit cumbersome to do this in es5 syntax, so I'd probably go the way of adding babel and a little if you want to learn about the module format in general I can recommend this section of "Understanding ES6" https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules |
Take a look at https://github.com/reactjs/react-router for a pretty good example of how this can be done, @davidchambers ! |
@mrtnbroder I may well be wrong on this, but is it true that a) It's as easy to split the file up and do My understanding is that splitting into files and doing the |
I do ❤️ the lack of any build step in this project, but I do appreciate the advantage this change would provide.
I'd love to see a proof of concept. Perhaps you could put together a patch to show this working for a handful of functions. This is all new to me, @rjmk, but I'm very interested to know whether your understanding is correct. |
@davidchambers I'd like to echo your sentiments on the lack of build step |
Correct. It just requires the @davidchambers whats wrong with a build step? It doesn't necessarily require a build tool like gulp or something. Babel would be enough to compile to ES5. It's a one-liner in the package.json file. Alright then, let me see if I can provide a simple patch. Will take a bit though, I am a bit busy atm ;) |
One can make things reasonably pleasant. Let's say one's using Make. One can have $ node_modules/.bin/mocha -- test/someFunction.js to test a specific change. One may be surprised that the tests are still failing. Oh, we forgot to include Another issue is that Make relies on file modification dates, which is sometimes problematic when changing branches. So sometimes one finds oneself needing to run $ rm -r dist && make && node_modules/.bin/mocha -- test/someFunction.js to ensure that the JavaScript file one is testing is up to date. These problems are minor, but losing five or ten minutes figuring out why changes are not taking effect does negatively affect my attitude towards a project. Another source of complexity is how to handle the generated files. One problem I encountered with CoffeeScript projects was that contributors would sometimes include changes to the generated file in their patches, even if the generated file was intended to be updated only at release time. This can be avoided via careful use of In Ramda's case, there's another source of complexity caused by the use of a custom build script. We must test both None of these problems is insurmountable, but having spent many hours working with JavaScript projects with build steps over the past several years, working on Sanctuary is a breath of fresh air. |
usually you add any build output to the |
Also, you can just So no |
👍 this! |
Almost done 😍 |
I have one major problem with the One idea that comes into mind is that the Another idae would be to add a flag like I think the best idea would be though to completely get rid of the I hope it makes sense to you what I mean, I'll push a WIP version in a moment to my fork: https://github.com/mrtnbroder/sanctuary |
The Ideally Sanctuary would not require knowledge of one's types, but #188 demonstrates that it does. Many Sanctuary functions are polymorphic. Take S.concat('abc', [1, 2, 3]);
// ! TypeError: Type-variable constraint violation
//
// concat :: Semigroup a => a -> a -> a
// ^ ^
// 1 2
//
// 1) "abc" :: String
//
// 2) [1, 2, 3] :: Array Number, Array FiniteNumber, Array NonZeroFiniteNumber, Array Integer, Array ValidNumber
//
// Since there is no type of which all the above values are members, the type-variable constraint has been violated. The answer is that each time we encounter a value of type
|
If we want tree-shaking and to play nicely with type checking, I think we would have to have an API change. One could imagine that either (a) all the functions need to have an env passed into them or (b) Then one would need an internal Sanctuary file, where one would basically import { _T, _either, ..., create, env } from 'sanctuary'
var myEnv = env.concat(...)
export var { T, either, ... } = create({ env: myEnv, checkTypes: true, functions: { _T, _either, ... } }) One could also imagine other, slightly different APIs. I think a better one would be import { T, either, ..., create, env } from 'sanctuary'
var myEnv = env.concat(...)
export default create({ env: myEnv, checkTypes: true, functions: { T, either, ... } }) But I don't know how that plays with destructuring |
Why does |
One could imagine Sanctuary taking in a It could export all the functions with them waiting for a import myDef from 'my-def'
import { map } from 'ramda'
import { T, either } from 'sanctuary'
export default (R.map(f => f(def), { T, either }) This might be a bit annoying when you're using most of Sanctuary, so it could also have a way of importing everything with the default environment. That might be the "Sanctuary" module and this stuff might be An entirely different approach might be to change |
sanctuary-js/sanctuary-def#74 is highly relevant to this discussion.
I don't think this would work. Here are two relevant snippets from Sanctuary's source:
I have a feeling there's a good solution out there. @Avaq and @rjmk have put forward several interesting ideas. Perhaps having each function take an environment as its first argument is the answer. One could then do something like this: const S = R.map(f => f(env), require('sanctuary')); It would be less pleasant, though, if requiring functions individually: const head = require('sanctuary/head')(env);
const init = require('sanctuary/init')(env);
const last = require('sanctuary/last')(env);
const tail = require('sanctuary/tail')(env); I'm not sure whether the above is something we want to support, though. I'm still getting my head around "tree shaking". |
That seems very invasive; I'm not against modularisation but this treeshaking stuff seems to spiralling out of control, I'd rather leave things as they are than go with any of the solutions proposed so far. |
@svozza I definitely agree treeshaking is not a good enough reason to do this. It seems to me something like this is definitely worth exploring though for composable environments and good dependency management. Creating environments with which one can extend the default environment is definitely possible, but it's hard to create functions which depend on a given environment while keeping as easy an install story.
I think I prefer taking
The API wouldn't have to be like that for 2 reasons.
// commonJS
module.exports =
{ I: require('./I.js')
, S: require('./S.js')
...
} or // ES6
export I from 'I.js'
export S from 'S.js' Though in the ES6 case, I'm pretty sure breaking the files up isn't actually necessary. This index file would be what one received from |
@mrtnbroder, perhaps you could open a pull request for the changes you have made so far? Don't worry if there are failing tests and whatnot. |
@davidchambers will do so asap. Need to refactor the commits a bit. |
Sorry about the delay, much work, wow, not much time. just wanted to inform you that I did not forget about it! |
No problem, @mrtnbroder. With at least two significant pull requests soon to be merged I just want to make sure you don't spend many hours on this only to find yourself with dozens of merge conflicts only a week later. By the way, I'll be living in Berlin from tomorrow. If you'd like to get together one day to work on this (or anything else) in person, contact me via email or Gitter. :) |
I'm still interested in this, but I'd like to keep the source code in a single file. |
I haven't yet seen a clear plan presented. I'm open to making changes to reduce the size of production builds, but every approach proposed so far has at least one significant drawback. |
I'm curious what the reasons for wanting to keep this in a single file are @davidchambers |
@MikaAK, see #246 (comment) for my reasons for opposing a build step and #246 (comment) for the difficulties |
@davidchambers Since Sanctuary does not support tree-shaking what about something like https://github.com/megawac/babel-plugin-ramda ? |
What I need to see is a concrete proposal, @kaushalyap. ;) |
@kaushalyap The problem is not just that Sanctuary hasn't been translated to modular JavaScript. It's mostly that Sanctuary only really exports one function: |
To solve this problem, I think the approach that presents itself would be to rewrite Sanctuary like this: // custom.js
import {create, TypeVariable} from 'sanctuary-def'
const a = TypeVariable ('a');
export const id = options => create (options) ('id') ({}) ([a, a]) (x => x)
// ...etc... // index.js
import * as custom from './custom.js'
import defaultEnv from './default-env.js'
const options = {checkTypes: true, env: defaultEnv}
export const env = defaultEnv;
export const id = custom.id (options);
// ...etc... A user can now // create.js
import * as custom from './custom.js'
export default options => ({
id: custom.id (options),
// ...etc...
}) Now a user can choose between the benefit of tree shaking, versus the benefit of not having to repeat themselves. It's quite a lot of work to achieve all that, however, and we're still not giving users the best of both worlds. |
I find this the telling point. Ramda has gone down the more complex build route, after starting with a single file. It has probably helped the library grow, but I regret it every time I do anything sophisticated with Ramda. Satisfying both the users who want to run Ramda on Node -- and hence have no problem with many individual files -- and those who run it in the browser and therefore really need a single tree-shaken version is an awful balancing act. I'm trying to mentally prepare myself to rewrite all of Ramda building/bundling stuff, and I still live in dread. I love Sanctuary's all-in-one-file choice, but I also love Ramda no-dependencies one, and I see little way to end up with both. |
It would be great if sanctuary could introduce a esnext version to support tree-shaking and a more modular approach.
The text was updated successfully, but these errors were encountered: