Skip to content
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

Side effects false #68

Closed
wants to merge 2 commits into from
Closed

Side effects false #68

wants to merge 2 commits into from

Conversation

jeetiss
Copy link
Contributor

@jeetiss jeetiss commented Jun 25, 2023

this is my second take on how to make library side effects free. (first one #63)

@jeetiss
Copy link
Contributor Author

jeetiss commented Jun 25, 2023

I tested the bundle size difference for sideEffects: false separately and it doesn't change anything at all, lol)

so I going to close this PR

@jeetiss jeetiss closed this Jun 25, 2023
@jeetiss jeetiss deleted the side-effects branch June 25, 2023 09:24
@steveluscher
Copy link

This is still worth doing. It's not that it will make this library smaller, it's that if @noble/hashes ever becomes unreachable in the application of which it's a dependency, that application's bundle will be able to reap the entire thing.

Without sideEffects: false, the bundler will assume that merely doing import { ... } from '@noble/hashes' mutates the environment in some important way (which it does not) and will keep all the code.

@jeetiss
Copy link
Contributor Author

jeetiss commented Jun 30, 2023

This is still worth doing. It's not that it will make this library smaller, it's that if @noble/hashes ever becomes unreachable in the application of which it's a dependency, that application's bundle will be able to reap the entire thing.

yes, but @noble/hashes contains side effects and it is not safe to drop it completely

there is a side effect:
https://github.com/paulmillr/noble-hashes/blob/main/src/utils.ts#L27-L32

@steveluscher
Copy link

The side effect being the thrown error?

@jeetiss
Copy link
Contributor Author

jeetiss commented Jun 30, 2023

yes, thrown an error on a module level

@steveluscher
Copy link

If all of @noble/hashes is dead code in the application that takes as a dependency, I think it would be appropriate not to throw that error, don't you?

@paulmillr
Copy link
Owner

So, how should the app behave on unsupported architectures then? Are you suggesting its methods should just corrupt data?

@steveluscher
Copy link

In the case described here the whole library would be reaped by the compiler. There would be no methods left to call.

@paulmillr
Copy link
Owner

I don't see how we can have one thing without another. isLE is side-effect.

Runtime in-hash checks for little-endianness can't be done, because that would screw up perf - which was very thoroughly tested.

@steveluscher
Copy link

steveluscher commented Jun 30, 2023

Here's an example.

import { bytesToHex } from '@noble/hashes/utils';

export function getRandomBytes() {
  const bytes = // Get some random bytes however you like.
  if (__DEV__) {
    console.log(`Generated random bytes: ${bytesToHex(bytes)}`);
  }
  return bytes;
}

When your compiler injects true for __DEV__ you get this compiled output:

import { bytesToHex } from '@noble/hashes/utils';

export function getRandomBytes() {
  const bytes = // Get some random bytes however you like.
  console.log(`Generated random bytes: ${bytesToHex(bytes)}`);
  return bytes;
}

When your compiler injects false for __DEV__ you get this compiled output:

import { bytesToHex } from '@noble/hashes/utils';

export function getRandomBytes() {
  const bytes = // Get some random bytes however you like.
  return bytes;
}

Even though every bit of @noble/hashes/utils became dead code, the compiler did not eliminate it. The reason is because it didn't find "sideEffects": false in the package.json.

If you add "sideEffects": false to the package.json, then the compiler will gladly remove the import in the second case.

export function getRandomBytes() {
  const bytes = // Get some random bytes however you like.
  return bytes;
}

@paulmillr
Copy link
Owner

@steveluscher I hear you, but you don't hear me. Adding sideEffects: false would remove isLE check and its error throwing on unsupported architectures. Yes, it's unfortunate that for some cases it would follow inefficient tree-shaking, but I don't see how the check could be executed otherwise.

@steveluscher
Copy link

I'm trying!

I don't understand why the isLE check should still run when the app has no need to produce hashes, as in the bottom example I gave.

This is separate from my general inclination for module factories to be side-effect free, which you know by now to predict from me, when you said this:

Runtime in-hash checks for little-endianness can't be done, because that would screw up perf - which was very thoroughly tested.

That's fair! I'd be curious if you could preserve the library's performance characteristics by doing something like this:

function getEndianness() {
  return new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44
    ? 'le'
    : 'be';
}
let endianness: 'be' | 'le' | undefined;

function hashStuff() {
  if ((endianness ||= getEndianness()) !== 'le') {
    throw new Error('Non little-endian hardware is not supported');
  }
  // Produce hash…
}

In any case, just adding "sideEffects": false would remove the endianness check in the sole case where endianness no longer matters.

@paulmillr
Copy link
Owner

paulmillr commented Jun 30, 2023

In any case, just adding "sideEffects": false would remove the endianness check in the sole case where endianness no longer matters.

As far as I understood, @jeetiss mentioned that adding sideEffects: false would always remove the isLE variable and the if-error from a bundle. That includes the case when hashes are used.

@steveluscher
Copy link

Nope! It would not, because it's used in that if() statement.

Compare this: (link)
To this: (link)

@steveluscher
Copy link

In short, "sideEffects": false has zero effect on tree-shakability inside a module, and everything to do with whether a module itself is reapable at the boundary between two modules.

@paulmillr
Copy link
Owner

If what you're saying is also true for other bundlers, then I see zero reasons not to add sideEffects: false to package.json.

@steveluscher
Copy link

Thank you! That's all I came here to achieve.

@steveluscher
Copy link

Do you want to put this PR back up @jeetiss, so that Paul can merge it, or would you like me to?

@paulmillr
Copy link
Owner

e37b278

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants