Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Factor: A Practical Stack Language (factorcode.org)
163 points by slondr on July 24, 2022 | hide | past | favorite | 45 comments


Be careful. Factor is a gateway to lower level stack based languages.

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.


Hah, you could have done worse. The double-dose introduction to stack-based programming via HP calculators and Adobe PostScript in the 90s led me to spend on the order of 3000 hours developing Onyx (https://github.com/canonware/onyx), which started off as a PostScript clone minus graphics, and ended up a thing of its own.

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.


Fun stuff! I find that the more I find out about PL research and what I myself like in programming, the more I feel constrained in many languages.

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).


Oh the flip side, if you told that story in an interview I'd hire you on the spot.


Currently you probably wouldn't. I am a classical musician.

CS or EE is however my backup if I get injured or for some reason lose my job.


I wouldn't hire you for a leadership role then, but it wouldn't stop me from hiring you. It is damn hard to find someone capable to delve deep into technology like that without hand-holding, and walk away with a working command of such obscure knowledge. People like that can be taught and trained to work on whatever project I am working on.

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.


Imagine being able to say "I feel concatenative this afternoon" and easily find active interesting efforts to contribute to for a few hours, solo or pair or team. Dreams for the newborn github included support for let's-meetup-and-hack realtime dynamic community formation, but I've not yet seen the like.

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.


> I saw Zed Shaw do a presentation using factor and decided to spend a weekend playing with it.

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." :)


I too have been "nerd sniped" by factor before... it's quite cool.


This is how you get hired at weird (good way) places you never forget, though.


Do you have any links to things you have done or any interesting resources for Forth on microcontrollers?


Have you found #forth-hardware-projects and #mecrisp?


This is almost 15 years ago, and I didn't have a reliable internet connection, so sadly no. There was two of us in a small computer group in Germany trying to figure things out, mostly.


There’s also a pretty active Forth channel on Discord


In case anyone else was curious, Slava Pestov is now at Apple working on Swift. Top of his twitter[1] is a WWDC22 talk "Design protocol interfaces in Swift"[2] (but video seems 1x-only - I don't see a copy on yt - there's a transcript).

[1] https://twitter.com/slava_pestov [2] https://developer.apple.com/videos/play/wwdc2022/110353/


He is also the person behind the JEdit editor (or at least one of the main guys).


Ha! I implemented "concatenative" programming for Java as Binding [1] (or Unit) expressions, but had no idea. Seriously, never came across that term before this post.

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...


If you see units as multiplicative factors:

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).


This is already implemented with manifold's unit expressions, but is made richer and more readable via concatenative features. For instance, '1.5 * USD' is not as readable as simply '1.5 USD'. However, unit expressions leverage operators for dimensional arithmetic:

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...



It is my understanding that the JVM itself uses a stack model at its core.




Related (others?):

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)


Could probably add any number of discussions of concatenative programming, most notably “Why Concatenative Programming Matters”.

https://hn.algolia.com/?query=Why%20Concatenative%20Programm...


A great read, thank you.


I wanted to reply like what is below but got cold feet. But thanks.

>>>> 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 think most (? certainly many) readers like the links to past discussions. If it were just a list of past submissions, most of which didn't get comments, I'd agree with you.

I also try to drop the ones that got comments but the comments were off topic or otherwise useless.


I used to play around with Factor in 05-10ish. Had all of a semester of C and a few physics labs worth of Matlab under my belt so I never actually managed anything interesting in it but god damned if it hasn't ruined me for every other language.


Those examples look really weird and cool.

This web app is using XML for it's views: https://github.com/factor/factor/blob/master/extra/webapps/w...


Concatenate languages are fun, but damn if that isn't an annoying UI. It's gated with some kind of captcha, and it made me go through 40+ rounds, with every few selected images resizing and shifting the captcha.


There shouldn't be any captchas. Do you mean on the website, like Cloudflare captchas? Which country/vpn/whatever are coming from?


On the website, like a Google captcha. I clicked through to see what the macro story was like and was hit with a captcha. USA, no VPN.


Just took a look on desktop. I'm just inadvertently being sent to edit links with empty content when looking at some of the language features, and edits are recaptcha protected.


Another recovering FORTH addict here


Made me realize webassembly is almost a concatenative language


One of the text forms is totally stack based while the second is quite lispy. And, according to the official grammar, you can mix the two forms willy-nilly — aside from the function header which needs to be in the lisp form IIRC.


The 'lispy' one is actually just a stack language written using sexps.


Damn, that indeed looks interesting. Could someone give here a Factor vs Forth tl;dr? Forth appears to be tailored for embedded systems, but the velociraptor is growing quick on me.


From the FAQ [0]:

> 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.

[0] https://concatenative.org/wiki/view/Factor/FAQ/Why%3F


Does anyone know of a Forth-like language that would be similarly low level, but allowing for nested word definitions for more HOF-centric approach?


I'm developing one at https://git.sr.ht/~remexre/rtf -- it's nowhere near ready for general use, but idiomatic code looks like:

    : 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.)


Also Forth appears to be heavily corporate. Except for GNU's Gforth compiler, tools appear to be commercial.


There's no one forth. A huge number of forth users end up writing their own forth, with different semantics, because it can be easier that way.

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.


There are many many free Forths, across almost all architectures. Dxforth, ciforth, FIG-Forths etc.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: