-
Notifications
You must be signed in to change notification settings - Fork 1
Functions
There are types of function in Fith: named and anonymous. They have roughly the same semantics; the difference is in how they are created and invoked.
To demonstrate, here's a named function that adds one to its argument:
: increment ( x -- x+1 ) 1 + ;That's a lot of strange-looking syntax. Let's break it down.
: begins a function definition.
It must be followed by the name of the function,
which in this case is increment.
The thing in parentheses that follows the name is called a stack effect. It shows how the function transforms the stack. It takes the form:
( before -- after )
where before and after are sequences of words representing the top few places on the stack.
For example, here's the stack effect of swap, which swaps the top two values on the stack:
( x y -- y x )Note that by convention, the right end of each stack diagram represents the top.
After you've declared the name and stack effect, you write the function as if the name of the function were replaced
with its contents every time it was invoked (which is pretty much what happens).
For example, in our increment function, we assume that the argument is already present.
To close your declaration, simply write the ; word.
In case you're wondering, the : and ; syntax was inherited from Forth.
Anonymous functions have syntax similar to PostScript.
You begin a block with { and end it with }.
There are no explicitly written stack effects for anonymous functions.
With that in mind, here's a version of the increment function from earlier,
implemented anonymously:
{ 1 + }After these words are executed, the top of the stack will be a function object.
You can run function objects with the ~ word.
So
3 { 1 + } ~is the same as
3 1 +There's one case we haven't really covered. What if you want to be able to call an anonymous function like a named function? In other words, to be able to do this:
{ 1 + } ???
## ...
3 incrementWhat would you put in place of ????
Fith has a word for that: the :: word.
It works like a regular variable assignment ->,
but it calls the object it refers to instead of pushing it to the stack.
So you can do what we did above:
{ 1 + } :: increment
## ...
3 incrementYou may have heard this phrase used before, probably in relation to Haskell. But it is very relevant to Fith as well. In an applicative language like Python, functions have explicitly named arguments:
def mul_plus_one(a, b):
return (a * b) + 1Whereas in Fith, such a function would be idiomatically defined as:
: mul_plus_one ( a b -- a*b+1 ) * 1 + ;The only mention of arguments is in the stack effect. This is point-free style: not explicitly naming your operands, instead defining functions only in terms of other functions. If you want to learn more about this kind of thing, the Haskell wiki has some interesting information.
In Fith, it is idiomatic to write pointfree functions. In Python, you'd implement the GCD function something like this:
def gcd(a, b):
while b:
a, b = b, a % b
return abs(a)Note that it's full of variable references. This is Python's way, and that's okay. It works for Python. But if we try to replicate that in Fith, it's ugly and confusing:
: gcd ( a b -- gcd ) -> b -> a { b } { a b mod b -> a -> b } while a abs ;Eugh. The body of that while loop is frightening. Here's what an idiomatic GCD function looks like:
: gcd ( a b -- gcd ) { dup } { tuck mod } while drop abs ;Not a single variable needed, and yet it makes more sense; it's also more concise.