Finally, the search is over.
I've been looking for the perfect programming language for years, and Factor is the one with the most potential to become that.
Before I found Factor I was into Lisp and Scheme, and I was in love with the parentheses. But that's just because I did not know Forth - if I had known it at the time I'd be working on a language that is a cross between Lisp and Forth.
Well, Factor is that language.
For my first post I will translate the good old yin yang trick from Scheme. I was not surprised to see that this works in Factor at all, since it seems that Factor actually copies "the stack and environment", much like the way schemers pretend call/cc works when explaining it to people. [1]
What yin yang does is it prints this ad infinitum:
@*@**@***@****@*****@****** ...
Here's the Scheme code:
(let* ((yin
((lambda (cc) (display #\@) cc)
(call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc)
(call-with-current-continuation (lambda (c) c)))))
(yin yang))
And here's the Factor version with a safety trigger to prevent it from looping forever:
USE: locals ;
[let
0 :> counter!
[ ] callcc1 [| cc | "@" write counter
100 >
[ "force-stopped" throw ]
[ counter 1 + counter! ] if
cc ] call :> yin
[ ] callcc1 [| cc | "*" write cc ] call :> yang
yang yin continue-with ]
It will print something like this (without the newlines):
@*@**@***@****@*****@******@*******@********@
*********@**********@***********@************
@*************@**************@***************
@****************@*****************@*********
...
If you understand continuations you can read up on the docs for callcc1 and continue-with and immediately understand how this works exactly like the Scheme version (or how it can be made to do so if the safety trigger is removed).
[ ] callcc1 is the literal equivalent to (call/cc id) that you see above in its more verbose version.
continue-with is like Scheme's apply but only for continuations; it takes a continuation to be called, and one argument to be passed to it. Continuations and lambdas in Factor are not isomorphic, so we need a way to call continuations. arg k continue-with is equivalent to (k arg).
I'm using locals above to make it simpler to see, but the snippet would be shorter if I did away with the cc locals:
USE: locals ;
[let
0 :> counter!
[ ] callcc1 [ "@" write counter
100 >
[ "force-stopped" throw ]
[ counter 1 + counter! ] if
] call :> yin
[ ] callcc1 [ "*" write ] call :> yang
yang yin continue-with ]
[1] I believe only a few schemes implement call/cc with any copying at all. It is possible to get call/cc for free, i.e. its cost being the same as of a normal function call, and it is also possible that this way of implementing call/cc is actually easier than alternatives.