You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This experiment tests the compatibility of Effect framework with Static Hermes, Facebook's ahead-of-time JavaScript compiler.
Summary
Effect works with Static Hermes in both interpreted and native binary compilation modes. This enables Effect-based applications to be compiled to standalone native executables.
Setup
Prerequisites (via Nix)
nix develop # Activates the development shell with all dependencies
Note: Static Hermes is currently ~3x slower than Node.js for Effect operations. This is expected since:
Static Hermes's typed compilation mode doesn't work with bundled Effect code (uses dynamic JS patterns)
The untyped native compilation provides modest improvements over interpreted mode
V8 (Node.js) is highly optimized for JavaScript execution
Limitations
Typed Mode Not Compatible
Static Hermes's -typed flag (for maximum performance with type annotations) is not compatible with bundled Effect code because Effect uses:
Computed property names in classes ([Symbol.iterator]())
Dynamic property access patterns
Complex type inference patterns
Private class fields
Optional chaining with calls
To use typed mode, you would need Effect to be rewritten with simpler patterns - which defeats the purpose of Effect's expressive API.
Missing Runtime APIs
The following need polyfills:
URL - Used by Effect's hash function
TextDecoder/TextEncoder - Used by Schema
AbortController - May be needed for cancellation (not tested)
Bundle Size
Effect binaries are ~3.4MB due to the Hermes runtime being linked in. This could potentially be optimized.
Use Cases
Static Hermes + Effect could be interesting for:
CLI Tools: Single-binary distribution without Node.js dependency
Embedded Systems: Effect in resource-constrained environments
React Native: (Native integration - the original use case for Hermes)
Edge Computing: Lightweight Effect executables for edge functions
Conclusion
Effect is compatible with Static Hermes when bundled with appropriate polyfills. While performance is currently slower than Node.js, the ability to compile Effect programs to standalone native binaries opens interesting deployment possibilities.
For production use, consider:
Node.js for best performance
Static Hermes for deployment simplicity (single binary, no runtime dependency)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This report documents a comprehensive compatibility experiment between the Effect framework and Static Hermes, Facebook's ahead-of-time JavaScript compiler.
Key Finding: Effect works with Static Hermes in untyped compilation mode, enabling Effect-based applications to be compiled to standalone native executables (~3.4MB). However, the typed compilation mode (which provides maximum performance) is incompatible due to Effect's use of advanced JavaScript patterns.
The binaries include the Hermes runtime, which adds ~2-3MB overhead.
Detailed Limitations
1. Typed Compilation Mode Incompatibility
The -typed flag fails with bundled Effect code. Here's why:
1.1 Computed Property Names in Classes
Error: ft: computed property names in classes are unsupported
// Effect uses this pattern extensively:classMyClass{[Symbol.iterator](){/* ... */}// ❌ Not supported in typed mode[TypeId]=TypeId;// ❌ Not supported in typed mode}
Tracking: No specific issue filed; this is a known limitation of typed mode.
1.2 Private Class Fields
Error: ft: unsupported class member ClassPrivateProperty
classSomeClass{
#privateField;// ❌ Not supported in typed mode}
Tracking: Private fields are an ES2022 feature; typed mode has limited modern JS support.
1.3 Optional Chaining on Calls
Error: ft: optional call expression not supported
someFunction()?.method()// ❌ Not supported in typed mode
Related Issue: facebook/hermes#403 (fixed for regular Hermes, but typed mode has separate implementation)
1.4 Dynamic Property Access on Functions
Error: ft: named property access only allowed on objects, found untyped function
functionfn(){}.constructor// ❌ Not supported in typed mode
1.5 Complex Type Inference
Warning: local variable may be used prior to declaration, assuming 'any'
Effect's heavy use of closures and the arguments object causes typed mode to fall back to any, losing type benefits.
2. Missing Runtime APIs
Static Hermes is intentionally minimal. These APIs are missing:
// polyfills.js - Required for Effect to work with Static Hermes// URL polyfill (used by Effect's hash function)if(typeofglobalThis.URL==="undefined"){globalThis.URL=functionURL(url,base){this.href=base ? base+url : url;};}// TextDecoder polyfill (used by Schema)if(typeofglobalThis.TextDecoder==="undefined"){globalThis.TextDecoder=functionTextDecoder(){};globalThis.TextDecoder.prototype.decode=function(bytes){if(!bytes)return"";varresult="";for(vari=0;i<bytes.length;i++){result+=String.fromCharCode(bytes[i]);}returnresult;};}// TextEncoder polyfill (used by Schema)if(typeofglobalThis.TextEncoder==="undefined"){globalThis.TextEncoder=functionTextEncoder(){};globalThis.TextEncoder.prototype.encode=function(str){varresult=newUint8Array(str.length);for(vari=0;i<str.length;i++){result[i]=str.charCodeAt(i);}returnresult;};}
Usage with esbuild
# Inject polyfills at bundle time
esbuild ./app.ts --bundle --outfile=./dist/app.js \
--platform=neutral --format=esm --inject:./polyfills.js
Caveats
The URL polyfill is minimal and doesn't implement full URL parsing
The TextDecoder/TextEncoder polyfills only handle ASCII; UTF-8 would require more complex implementation
For production use, consider using established polyfill libraries
CLI tools where single-binary distribution matters more than raw performance
Embedded environments where Node.js isn't available
Experimentation and prototyping
Contexts where startup time is critical (native binaries start faster)
❌ Not recommended:
Performance-critical applications (Node.js is 3x faster)
Applications using Effect's advanced typed features
Production React Native apps (not officially supported)
Optimization Tips
Bundle aggressively: Use esbuild with tree-shaking to minimize bundle size
Avoid typed mode: Stick to untyped compilation for Effect
Polyfill early: Inject polyfills via esbuild banner to ensure they run first
Test thoroughly: Some Edge cases may behave differently than Node.js
Future Outlook
Static Hermes is under active development. Key improvements that would benefit Effect:
Automatic type inference (#1349) - Would allow typed mode to work with existing JS libraries
Broader Web API support (#1072) - Reduce polyfill requirements
React Native integration - Official support would validate the approach
Conclusion
Effect works with Static Hermes, enabling Effect-based TypeScript applications to be compiled to standalone native executables. This opens interesting deployment possibilities, particularly for CLI tools and embedded environments.
However, significant limitations exist:
3x slower than Node.js for Effect operations
Typed mode incompatible due to Effect's dynamic JS patterns
Polyfills required for URL, TextDecoder, TextEncoder
Binary size overhead (~3MB minimum)
For most production use cases, Node.js remains the recommended runtime for Effect applications. Static Hermes is better suited for scenarios where deployment simplicity (single binary) outweighs performance requirements.