In Python, when you write if x > 5: print("big"), you’re using special syntax baked into the language. You can’t change how if works. You can’t compose it, pipe it, or partially apply it. You can’t pass if as an argument to another function.
But what if you could? What if if, for, and while were just regular functions?
This might sound like a theoretical curiosity, but it has real implications for how we write code.
Three Reasons This Matters
Consistency. Most languages treat control structures as special forms—exceptions to the normal rules. In languages like Rye and REBOL, they’re ordinary functions that follow the same patterns as everything else.
Flexibility. Functions can be composed, passed around, and combined. When control structures are functions, you inherit all those capabilities.
Extensibility. If if and for are just functions, you can create your own specialized versions for specific purposes. No part of the language remains off-limits.
Let’s see what this looks like in practice.
When Code Becomes Data
Here’s a conditional in Python:
Now compare this to Rye:
Look at that last if statement. It’s not special syntax it’s a function call. While functions print and do take one argument, if function takes two arguments:
- A condition that evaluates to true or false
- A block of code to run if the condition is true
You might wonder: “Won’t the block execute immediately when passed as an argument?” Here’s the key insight: in Rye, code blocks { ... } are values. They don’t evaluate until you explicitly tell them to. The if function receives the block as data and decides whether to evaluate it based on the condition.
When code is data, control flow doesn’t need to be special.
One Pattern to Rule Them All
In Python, every language feature has its own syntax:
In Rye, one pattern applies everywhere:
Every construct follows the same shape: a name, followed by arguments, some of which happen to be blocks of code. Instead of memorizing Python’s 35+ keywords and their unique syntaxes, you learn one universal pattern.
There’s no longer a meaningful distinction between “language features” and “library functions.”
Consistency & Flexibility
In Python, if and for are statements, not values. But in Rye they are function. and first of all you can compose functions.
prns prints a value with a space (no newline)
In Python using if statement, this would take multiple lines and a variable mutation:
Luckily, python has another special syntax where if keyword becomes an expression:
for _ in range(3 if temperature > 31 else 1): print("Hot!", end='') # Prints: Hot! Hot! Hot!While special syntax is usualy cemented in place, there are multiple ways to provide arguments to function calls.
Piping Into Control Flow
Functions in Rye can accept a first (or second) argument from the left, so the same applies to “flow control”-like functions of course. Read more about this in Meet Rye.
In Python, you can’t pipe into if or for because they’re not values, they’re syntax.
Applying functions
In Rye we can apply functions to it’s arguments.
?word - is a get-word, it doesn’t evaluate a function bound to the word but returns it word* - star at the end of the word causes function to take second argument from the left, not the first one
Partial Application
In Rye we can partially apply functions.
You’ve created a custom control structure by partially applying a built-in one. Try doing that with Python’s for loop.
Higher-Order Control Flow
We can pass function as arguments to other functions
Extensibility, You Are a Language Designer
Since control flow is just functions, you can write your own control structures indistinguishable from built-ins.
Unless and Until
TODO – add some text here
What are control flow functions anyway
When you accept this you see that there is no hard border of hard limit to what control strucutre like function you should have. You load them on library level and specific libraries can provide new ones or offer you functionality in shape that would usually be reserved to special forms.
Part of the base functions, but could be considered special
These are usually higher order functions (functions accepting functions) but in Rye they are more similar to for-loop.
Even external libraries can have their own functions that utilize blocks of code directly.
For example OpenAI library has a Chat\stream function that accepts a block of code into which it injects a part of string for each stream event, similar to for loop.
The Trade-offs
Performance
Python can optimize special forms at compile time. In Rye, if is a function call with runtime overhead.
Tooling Challenges
IDEs know what Python’s if, for, def are and can provide specialized support. When everything is a function, tools have less to grab onto. On the other hand everything is of the same type, so it si much simpler to make tools for a language like that.
But
With langauge like Python you have to optimize and provide tools for every specil syntax and special construct separately, while in Rye, you “just” have to make function calls as fast as possible, there is just one construct to optimize and provide tooling around!
Interested in learning more? Check out Rye and REBOL to see these ideas in action.