💾 Archived View for idiomdrottning.org › define-vs-let captured on 2023-04-19 at 23:14:35. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
➡️ Next capture (2024-02-05)
-=-=-=-=-=-=-
Obviously let bindings are awesome because their scope is immediately clear as opposed to a define that’s gonna keep on cluttering the top level forever.
One neat thing about defines, though, is how they bind in two ways.
Obviously, you can think of the name itself as a binding, like how these two examples resemble each other (except for scope):
(let ((foo (+ 12 18))) ...) (define foo (+ 12 18)) ...
That’s basic. The next level is the function args are also implicitly bindings!
(define (bar foo) ...) (bar (+ 12 18))
Now, if you already know Scheme, you’re gonna say “Duh, we know that”. But the neat part is applying this in the context of refactoring, inlining and extracting!
If you are an inliniac like me, you’re letting your program’s own evolution determine your abstraction levels, letting your own code show you which things need to be bound and which things don’t, instead of trying to decide that on your own.
Now, the ninja move is to let those bindings, along with any of your “zebra” bindings, live on the arg name level, like in the last example above.
Tired:
(let ((zebra (striped horse (donkey tail)))) (when zebra (frobnicate leaves)))
Wired:
(when (striped horse (donkey tail)) (frobnicate leaves))
Inspired:
(define (friendly zebra) (when zebra (frobnicate leaves))) (friendly (striped horse (donkey tail)))
The awesome thing about this style is that this makes the binding itself the interface. This makes the code super flexible and reusable.♥
A classic example of this pattern is this old, often independently reinvented thing that’s useful when hunting bugs:
(define (show x) (pretty-print x) x)
You can wrap any expression in a call to show and both display and get the value. Lambda the ultimate let.