JavaScript Garden documents the most notorious quirks and pitfalls of JavaScript. Here's a deep analysis of each section and how Rip eliminates or mitigates these problems.
JavaScript Garden calls == a source of "hard-to-track-down bugs" due to type coercion. Rip solves this at the language level with two mechanisms:
Rip's == compiles to ===. There is no way to accidentally use loose equality. The entire coercion table from the Garden becomes irrelevant:
0 == "" # compiles to: 0 === "" → false
0 == "0" # compiles to: 0 === "0" → false
false == "0" # compiles to: false === "0" → falseRip's is keyword also compiles to ===, giving you a readable English alternative for identity checks:
x is true # compiles to: x === true
x is "hello" # compiles to: x === "hello"
x isnt null # compiles to: x !== nullBetween == always meaning strict equality and is/isnt for readable prose-style comparisons, Rip makes it structurally impossible to trigger JavaScript's type coercion. The entire weak-equality footgun simply doesn't exist.
The Garden calls typeof "probably the biggest design flaw of JavaScript" — it returns "object" for arrays, null, dates, regexps, and errors. Rip provides kind() as a stdlib global:
kind [] # "array" (typeof would say "object")
kind null # "null" (typeof would say "object")
kind /regex/ # "regexp" (typeof would say "object")
kind 42 # "number"
kind "hello" # "string"kind() returns the correct lowercase type name by using Object.prototype.toString internally — exactly what the Garden recommends as "the only reliable way" to determine types, but Rip makes it a one-word call.
The Garden devotes a full section to the five ways this can be bound, the "common pitfall" of nested functions losing this, and the self = this workaround. Rip addresses all of this:
@isthis.—@namecompiles tothis.name, makingthisusage explicit and concise- Inside components,
->auto-converts to=>— You never losethisbinding accidentally. The compiler handles it - Fat arrows (
=>) are first-class — When you need explicit binding outside components, just use=>
The Garden's example of the broken nested function:
// JavaScript — broken
Foo.method = function() {
function test() { this.value; /* wrong this! */ }
test();
};In Rip, this simply works:
# Rip — @value always refers to the outer this
Foo.method = ->
test = => @value # fat arrow captures this
test()The Garden warns extensively about var hoisting, missing block scope, and accidental globals. Rip compiles to let and const — never var:
- Block scoping is the default — Variables declared in
if,for, or any block are scoped to that block - No hoisting surprises —
let/consthave temporal dead zones; use-before-declaration is an error - No accidental globals — Rip always emits declarations; there's no way to accidentally create a global by forgetting
var
The Garden's classic bug:
// JavaScript — i leaks, subLoop clobbers it
for(var i = 0; i < 10; i++) { subLoop(); }
function subLoop() { for(i = 0; i < 10; i++) { } } // oops, global iIn Rip, for loop variables are always block-scoped. This class of bug is structurally impossible.
The Garden's infamous closure-in-loop problem:
for(var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 1000); // prints 10, ten times
}Rip compiles for loops with let, which creates a new binding per iteration:
for i in [0...10]
setTimeout (-> p i), 1000 # prints 0, 1, 2, ... 9No IIFE wrapper, no .bind() trick — the language just does the right thing.
The Garden describes arguments as "not an Array" requiring Array.prototype.slice.call(arguments) to use. Rip has proper rest parameters:
def foo(first, ...rest)
rest.map (x) -> x * 2 # rest is a real ArrayThe arguments object is never needed. Spread (...args) and rest parameters handle all use cases natively.
The Garden calls ASI "one of the biggest design flaws in the language." Rip uses significant whitespace — there are no semicolons to insert or forget. The Garden's terrifying example of merged lines:
log('testing!')
(options.list || []).forEach(...) // merged into one expression!This is structurally impossible in Rip. Newlines are meaningful. The parser uses indentation, not semicolons, to determine statement boundaries.
The Garden warns that for in traverses the prototype chain and requires hasOwnProperty filtering. Rip's iteration constructs compile to safe ES6+ equivalents:
| Rip | Compiles to | Safe? |
|---|---|---|
for k, v of obj |
for...of Object.entries(obj) |
Yes — own properties only |
for x in arr |
Index-based loop | Yes — no prototype traversal |
for x as iter |
for...of on iterables |
Yes — iterator protocol |
You never need to write hasOwnProperty checks in Rip.
The Garden warns that forgetting new leads to this being the global object. Rip offers Ruby-style .new():
user = User.new("Alice") # compiles to: new User("Alice")While Rip also supports new User("Alice"), the .new() convention makes it read as a method call, reducing the chance of omission.
The Garden discusses the confusion between undefined and null and the need for careful checking. Rip provides a family of operators:
| Operator | Name | Purpose |
|---|---|---|
x? |
Existence | x != null (covers both null and undefined) |
x ?? y |
Nullish coalescing | Default if null/undefined |
x?.y |
Optional chaining | Safe property access |
x!? |
Defined check | True if not undefined |
x?! |
Presence check | True if truthy, else undefined |
# Instead of: typeof foo !== 'undefined' && foo !== null
if foo?
p fooThe Garden says eval "should never be used." Rip simply doesn't have eval. There's no syntax for it, no backdoor. Code is compiled, not evaluated from strings. The setTimeout('foo()', 1000) anti-pattern is impossible.
The Garden warns about setInterval stacking calls when the callback blocks. Rip's sleep! (which compiles to await sleep(ms)) encourages the recursive-setTimeout pattern naturally:
loop
doWork!
sleep! 1000This is the exact pattern the Garden recommends — sequential, non-stacking delays — but expressed as a simple loop.
The Garden explains delete's inconsistent behavior with DontDelete attributes. Rip exposes delete but its class system and module pattern mean you rarely need to delete properties from objects manually.
| JS Garden Issue | Rip's Solution | Severity Eliminated |
|---|---|---|
== coercion |
== compiles to === |
Critical |
typeof broken |
kind() stdlib |
Critical |
this confusion |
@, auto => in components |
Critical |
var hoisting |
Compiles to let/const |
Critical |
| Closures in loops | Block-scoped let per iteration |
High |
arguments not an Array |
Rest params ...args |
High |
| ASI surprises | Significant whitespace | High |
for in prototype leak |
Safe for...of/for...in semantics |
High |
Forgetting new |
.new() syntax |
Medium |
null/undefined confusion |
?, ??, ?., !?, ?! |
Medium |
eval misuse |
Not available | Medium |
setInterval stacking |
sleep! + loop pattern |
Low |
Nearly every issue documented in JavaScript Garden is either structurally impossible in Rip (ASI, var hoisting, accidental globals, closures in loops) or eliminated by default (== → ===, kind() vs typeof, @ for this). The Garden is essentially a catalogue of JavaScript's historical mistakes — Rip compiles to modern JavaScript while making it nearly impossible to trigger any of them.