Factor: A Practical Stack Language
factorcode.org
factorcode.org
I saw Zed Shaw do a presentation using factor and decided to spend a weekend playing with it. It was the start of a two year journey that ended with me writing forth for microcontrollers. That was time I should have studied but was somehow always sidelined by other things.
I enjoyed Factor. Fixed stack-effect seemed a nice trade of flexibility for less oops.
I'd love to see someone start concatenative, but with a replace-the-world focus. So move quickly up-scale to single and multiple dispatch PICs, tabled WAM, clone V8 internals, link Z3, etc, etc. "If you're not able to implement other languages in bulk, and better than they are, you're still wedged". Sort of a LispMachine for an age where a much broader-than-cons foundation is possible/appropriate.
CS or EE is however my backup if I get injured or for some reason lose my job.
So with a gap working as a classical musician it might mean starting effectively as a (paid) intern, but it would still get your foot in the door.
I don't claim to be representative of all, or even most hiring managers though.
On the bright side, I learned a tremendous amount about lexing, interpreters, exceptions, tail-call optimization, data structures, garbage collection, etc., which opened up lots of possibilities in later projects. With the benefit of hard experience I would never recommend using a stack-based language for a large project, but as a learning tool it has some nice qualities.
I find that most of the bugs I write (which are many) are errors in dealing with mutable state. There is always one stupid little detail that leads to bad state. This has led me to dislike the programming language I write the most (probably python, despite really only wanting to write scheme) because it is mutation first. I was also sad to discover that I couldn't write self-recursive procedures in rust because they are never guaranteed to be actually recursive.
Did you get anything like that from your work with onyx? (And yes, I believe I have actually has a look at onyx about 10 years ago, back when trying to write poatscript).
I saw the same presentation back then and also spent a weekend with factor. It was a really valuable experience. The language was so different from anything I had previously used that I had to learn a new way of thinking. That was a great thing to go through.
Slava Pestov (the creator of factor) did a presentation on the language at Google back in the 2000s. A highlight from the talk was him explaining that there was a function 'boa' that you could call to construct an object 'by order of arguments'. He called it "a boa constructor." :)
[1] https://twitter.com/slava_pestov [2] https://developer.apple.com/videos/play/wwdc2022/110353/
The idea with binding expressions is the type of expression A and the type of expression B implement "reactions" with one another in order to form a binding expression when they are lexically adjacent.
65 mph
The type of `mph` defines a post reaction method with the type of `65` as an argument that results in type Rate. As I understand it this is concatenative, right?
Another example:
Money payment = 1.5M USD;
There are tons of these.
Concatenative programming in general feels like it should have a more prominent place in mainstream languages. Just my take.
[1] https://github.com/manifold-systems/manifold/tree/master/man...
1.5 USD = 1.5 * USD
65 mph = 65 * mile / hour
you can then use simple algebra to solve unit conversion and many other common deductive reasoning questions ("how many miles per gallon...?"), relying on the units to guide you to the solution.
There's probably a name for this, and it is indeed built into the SI notation (km/h vs mph).
Force f = 5 kg * 9.807 m/s/s; // result: 49.035 Newtons
Area space = (20ft + 2in) * (37ft + 7.5in); // result: 758 37/48 ft²
All unit expressions internally store amounts as SI units, which enables interunit expressions.
Length height = 6 ft + 4 cm; // mix SI and US units
out.println(height.to(ft)); // display any unit
You aren't limited to units. Generally, any concatenative sequence can be implemented. Like ranges:
IntegerRange range = 1 to 5;
Here the `to` identifier's type implements reactions to Number types to produce range types, which enables:
for (int i: 1 to 5) { out.println(i); }
See the manifold project's Unit and Science modules:
Units: https://github.com/manifold-systems/manifold/tree/master/man...
Science: https://github.com/manifold-systems/manifold/tree/master/man...
https://bluishcoder.co.nz/2020/04/18/fun-factor-libraries.ht...
Factor: A Practical Stack Languge - https://news.ycombinator.com/item?id=22053857 - Jan 2020 (21 comments)
Factor: An impressive stack-based language environment - https://news.ycombinator.com/item?id=17726634 - Aug 2018 (34 comments)
A Panoramic Tour of Factor (2015) - https://news.ycombinator.com/item?id=11377847 - March 2016 (6 comments)
Factor – A Practical Stack Language - https://news.ycombinator.com/item?id=10141410 - Aug 2015 (7 comments)
Factor – A practical stack language - https://news.ycombinator.com/item?id=9008472 - Feb 2015 (41 comments)
A panoramic tour of Factor - https://news.ycombinator.com/item?id=8750720 - Dec 2014 (10 comments)
Factor 0.97 now available - https://news.ycombinator.com/item?id=8548832 - Nov 2014 (18 comments)
Web scraping with Factor - https://news.ycombinator.com/item?id=7628055 - April 2014 (24 comments)
Factor's Visual REPL - https://news.ycombinator.com/item?id=1673792 - Sept 2010 (8 comments)
Factor programming language (modern Forth) - https://news.ycombinator.com/item?id=1623697 - Aug 2010 (19 comments)
Factor: Comparing Factor's performance against V8, LuaJIT and SBCL - https://news.ycombinator.com/item?id=1388540 - May 2010 (2 comments)
Replacing GNU assembler with Factor code - https://news.ycombinator.com/item?id=1076658 - Jan 2010 (5 comments)
Improved write barriers in Factor's garbage collector - https://news.ycombinator.com/item?id=884704 - Oct 2009 (2 comments)
A survey of domain-specific languages in Factor - https://news.ycombinator.com/item?id=854757 - Oct 2009 (1 comment)
Performance in Factor, Java, and Clojure - https://news.ycombinator.com/item?id=797378 - Sept 2009 (16 comments)
Factor programming language - https://news.ycombinator.com/item?id=782531 - Aug 2009 (5 comments)
Joy in Factor - https://news.ycombinator.com/item?id=772603 - Aug 2009 (13 comments)
Factor compiler improvements - https://news.ycombinator.com/item?id=738124 - Aug 2009 (1 comment)
Factor's implementation of polymorphic inline caching - https://news.ycombinator.com/item?id=629357 - May 2009 (9 comments)
Factor VM ported to C++ - https://news.ycombinator.com/item?id=600668 - May 2009 (10 comments)
Proof-of-concept Smalltalk implemented using Factor as a host VM - https://news.ycombinator.com/item?id=541836 - April 2009 (3 comments)
Factor: A practical stack language - https://news.ycombinator.com/item?id=279356 - Aug 2008 (1 comment)
Factor 0.88 released - https://news.ycombinator.com/item?id=1664 - March 2007 (2 comments)
>>>> Why? tedious for me w/o a join. Or is there a feature lurking somewhere? At a different level stuff like this it strike me as “… nothing to see here move on .. “
I also try to drop the ones that got comments but the comments were off topic or otherwise useless.
https://hn.algolia.com/?query=Why%20Concatenative%20Programm...
This web app is using XML for it's views: https://github.com/factor/factor/blob/master/extra/webapps/w...
> How is Factor different from Forth?
> Forth is untyped, not garbage collected, and close to the machine. For flow control, Forth code tends to use immediate words. Variables and arrays tend to be global, and programs aren't usually written in a functional manner. Factor is very different from this. It is dynamically typed, offering a high degree of reflection. Unreferenced objects are garbage collected with a generational garbage collector. Factor code is a little bit more distant from the machine, though the C FFI allows using words like malloc and mmap. For flow control, Factor generally uses quotations, allowing flexible higher order functions; parsing words are used mostly for definitions and data literals. Variables are dynamically or statically scoped (see below), and arrays are just objects which don't need to be treated specially.
Though you're right, forths are unusually commercial. Forth is old, and in some ways it stayed old. A lot of people that use it now have used it since the 80s, so they're quite used to paying for compilers.
There's also the fact that forths are used in very niche places, and the companies that use them are happy to pay up.
You'll find a very open community with really nice people though.
: parse-digit ( u -- u -1 | 0 0 )
{ DUP $30 $3a WITHIN } { $30 - TRUE }
{ DUP $41 $5b WITHIN } { $37 - TRUE }
{ DUP $61 $7b WITHIN } { $57 - TRUE }
{ ." parse-digit 1: " TODO } 3 CASE ;
: add-digit ( u u -- u flag )
." add-digit 0: " TODO
;
: parse-digits-0 ( u addr len -- u flag )
{ OVER B@ -ROT { parse-digit } 2DIP ROT
{ { SWAP base B@ * + } 2DIP 1 /STRING parse-digits-0 } WHEN
} { DROP TRUE } ?COND ;
: parse-digits ( u addr len -- u flag )
{ parse-digits-0 } { DROP FALSE } ?COND ;
: parse-prefix-if-exists ( addr len -- addr len 0|1|2 )
DUP {
OVER B@
{ DUP $2d = } { DROP 1 /STRING 1 }
{ DUP $7e = } { DROP 1 /STRING 2 }
{ DROP 0 } 2 CASE
} { 0 } COND ;
: parse-base-if-exists ( addr len -- addr len base )
DUP {
OVER B@
{ DUP $23 = } { DROP 1 /STRING #10 }
{ DUP $24 = } { DROP 1 /STRING $10 }
{ DUP $25 = } { DROP 1 /STRING %10 }
{ DUP $5e = } { DROP 1 /STRING ^10 }
{ DROP 2DUP $3a SCAN DUP } {
1 /STRING { NIP - 1- 10 BASE B! 0 -ROT parse-digits } 2KEEP
ROT { ROT } {
." parse-base-if-exists 1: " TODO
} COND
\ ." parse-base-if-exists 2: " TODO
}
{ 2DROP base B@ } 5 CASE
} { base B@ } COND ;
: with-saved-base ( x*i { x*i -- x*j } -- x*j )
base B@ SWAP DIP base B! ;
: apply-prefix ( u 0|1|2 -- n )
{ } { NEGATE } { INVERT } 3 SWITCH ;
: parse-number ( addr len -- u -1 | 0 0 )
{ parse-base-if-exists base B! parse-prefix-if-exists
{ 0 -ROT parse-digits } DIP
SWAP { apply-prefix TRUE } { DROP FALSE } COND
} with-saved-base ;
(This being an incomplete rewrite of the bootstrapped text interpreter's number parser; as I said, nowhere near ready.)