[HN Gopher] Forsp: A Forth+Lisp Hybrid Lambda Calculus Language
___________________________________________________________________
Forsp: A Forth+Lisp Hybrid Lambda Calculus Language
Author : xorvoid
Score : 201 points
Date : 2024-06-10 12:47 UTC (1 days ago)
(HTM) web link (xorvoid.com)
(TXT) w3m dump (xorvoid.com)
| um1 wrote:
| Very cool. I like that both lisp and forth were "discovered" and
| that this cvbp is more fundamental than both(?!). This reminds me
| of [pdf] https://dl.acm.org/doi/pdf/10.1145/181993.181999 and
| wonder if/how it relates.
| diffxx wrote:
| Thank for sharing that paper. I am currently implementing a
| compiler that has very similar ideas to those presented in it.
| It's very different syntactically, but the mental model is
| quite similar. It's simultaneously encouraging and discouraging
| to see your ideas reflected back to you from 30 years in the
| past :)
| xorvoid wrote:
| Also thanks for sharing; looks interesting. I'll read this when
| I have time and re-comment then. Off the bat: Forsp variables
| are definitely not linear.
| boltzmann-brain wrote:
| what is cvbp? google yields nothing.
| tonyg wrote:
| It's a typo for CBPV, Call-By-Push-Value.
| boygobbo wrote:
| I think that was meant to be CBPV (Call-By-Push-Value)
| alexisread wrote:
| Looks complicated to reason about, but really interesting.
| Freeforth implements a similar mechanism to eliminate swaps via
| register renaming, obv. the focus is different in that
| Freeforth tries to still be an optimised stack, rather than a
| VLIW processor. I guess you could look at this paper as a
| vector processor, similar to the way APLs handle things with
| function composition. In that, you'd need the compiler to take
| care of things as managing it manually would be a nightmare. I
| guess the best way forward would be a specialized (forth) word
| lib which invokes the compiler to manage the top N stack cells
| for the duration of the word.
| thsksbd wrote:
| I been wanting to make a forth+lisp language for a while now -
| inspired by HP's RPL (its so close to being a lisp!).
|
| Super cool
| xorvoid wrote:
| A friend mentioned RPL, but that didn't seem to even have
| first-class functions... unless I'm mistaken?
| ceving wrote:
| You can put functions on the stack.
| agumonkey wrote:
| with arrow syntax even << a -> << a a * >>
| >> 'SQUARE' STO
|
| I still can't believe how advance RPL calculators were
| thsksbd wrote:
| The HP engineers were good.
|
| First HP engineers made RPL, then they attended Sussman and
| Abelson's intro to CS class. I think Sussman once said
| that, if RPL had anonymous functions it'd be a perfect
| lisp.
|
| They were real good.
|
| https://ocw.mit.edu/courses/6-001-structure-and-
| interpretati...
| thsksbd wrote:
| Im not a CS guy, so I dont know what "first class" means.
|
| I can do whatever to a function. I can use it as an argument.
| I can return it from another function. I can make a function
| that modifies functions.
|
| All that w/out doing anything out of the ordinary day to day
| programing.
| diffxx wrote:
| Cool language. One modest suggestion: perhaps return 0 rather
| than 1. Feature request: addition and division as well as
| subtraction and multiplication ;)
| xorvoid wrote:
| Addition can be implemented as:
|
| (0 swap - -) $+
|
| Division is much harder, but doable also. Generally Forsp in
| the camp of minimalist languages so minimizing the number of
| primitives seemed more interesting.
|
| If you want to play around yourself, it's trivial to add those
| operations.
| bowsamic wrote:
| > Division is much harder, but doable also.
|
| > If you want to play around yourself, it's trivial to add
| those operations.
|
| Now you're using "trivial" like a mathematics professor!
| xorvoid wrote:
| Ha. Well I meant "add those operations to the C
| interpreter", which would be like 4 lines of code.
| throw156754228 wrote:
| Wow he even provides an interpreter in C. How would a compiler
| implementation for this language foreseeably differ from the
| interpreter given, does anyone know? Trying to learn about this
| more.
| alexisread wrote:
| I'd take a leaf out of Freeforth's compiler
| (https://github.com/dan4thewin/FreeForth2): It implements a
| repl via single-pass (as with all forths) compilation of an
| anonymous block. Scoping in forth is dynamic ie. what's on the
| stack, and lexical in the dictionary ie. new words can shadow
| old ones.
|
| Stacks have great GC properties vs. an actual GC, but for more
| complex scenarios, and dictionary management, some sort of GC
| would be preferred. I've not looked at the forsp source yet,
| but it may need a more general (realtime) GC, possibly via a
| linear-types system.
| packetlost wrote:
| I feel like simple reference counting + linear types would be
| a really good approach in this case.
| kazinator wrote:
| What has Lisp semantics and a stack with "call by push value" and
| whatnot is any one of the stack-based virtual machines used for
| compiling Lisp over the past 60 years.
| nickcw wrote:
| I like this very much!
|
| pop is kinda like ! in FORTH and push is like @ so I think $foo
| would be more FORTHy as !foo and ^foo as @foo
| tangjurine wrote:
| If you have time, could you explain the ycombinator and if parts
| a bit more? Been a while since I looked at this stuff
| tonyg wrote:
| Could ^a be dispensed with? It looks like (a) is equivalent to
| it.
| dugmartin wrote:
| I think the equivalent would be ('a) instead as (a) would
| resolve to "'result of application of a' push".
| tonyg wrote:
| The page says that "`(foo bar) force` is the same computation
| as `foo bar`" and that "`^a force` is the same computation as
| `a`". So then `(a) force` should be the same computation as
| `^a force`.
| xorvoid wrote:
| Subtly different. Originally I didn't have ^a, but you do in-
| fact need it. ^a means "push with no evaluation" and (a) means
| "build a closure" that computes a".
|
| If you push, pop, push, pop, push repeatedly you'll be wrapping
| the value in lots of closures. The value is no longer a value
| too. You'd have to "force" it to get back the value.
|
| Semantically quite different. If you're going all Turing
| tarpit, could you survive without "^a"? Maybe. But you'd be
| suffering for sure.
| tonyg wrote:
| Operationally, you don't have to do the wrapping: since (a)
| is equivalent to ^a, you can substitute the implementation of
| the latter when you see the former.
|
| Though if `(a)` is _observably_ "no longer a value" of course
| then that's an issue. Which may or may not be a _problem_ :-)
| xorvoid wrote:
| I thought of that. But then you're making the semantics
| complicated. Say you really did want a (a) closure. Now you
| can't have one, and you have to resort to more tricky
| wrapping. Not worth it.
| FrustratedMonky wrote:
| Nice, anyone using this? Have practical feedback?
|
| Does this have potential to grow, or would most people just say
| 'use lisp'.
| FrustratedMonky wrote:
| I only ask because there are hundreds of cool niche languages
| now. And, even thought this looks cool, time is time, and I'm
| just wondering if is worth committing any effort to
| learning/using/playing with.
|
| Is there any feeling that this has legs?
| dugmartin wrote:
| I really like the syntax and ergonomics - nice job @xorvoid.
| pbrhocwp wrote:
| Very nice! Thanks a lot for the discovery.
| mkovach wrote:
| Since it is written in C, we need to write COBOL and FORTRAN
| interpreters with it, make sure the manpages are in Latin, and
| we'll hit the ancient languages nerdvonia.
| lanstin wrote:
| User viewable docs should be Anglo-Saxon and dev docs should be
| Latin (or Attic Greek?) Publicity materials should be medieval
| Arabic, and the secret documentation that isn't shared should
| be in Mandarin.
| CrociDB wrote:
| Forth+Lisp = Wet dreams of every programming nerd
| CyberDildonics wrote:
| Until you have to deliver something and you realize you love
| programming languages more than creating software.
| James_K wrote:
| I've never really got Forth. It's good for sending commands to a
| printer, but actually programming in it seems like a drag. I get
| that it's technically the same as lisp code, just backwards
| without the parens, but adding the parens just makes it easier to
| understand. Adding variables is smart and would make it more
| tolerable but I would still rather work in lisp.
| gergo_barany wrote:
| Forths do have local variables. (Cue debates on what makes a
| stack-based Forth-like language a "Forth".) Here is the
| description of Gforth's support for its own extended locals and
| the Standard Forth locals:
| https://gforth.org/manual/Locals.html (Cue debates on whether
| Standard Forth is a Forth and whether it is standard.)
|
| When I learned Forth about 15 years ago, we used the { } syntax
| described here, not the newer(?) {: :} syntax:
| https://gforth.org/manual/Local-Variables-Tutorial.html
| anta40 wrote:
| Me too. For some reason, Lisp is kinda easier for me. And if I
| need to go low level: assembly.
|
| I read Forth was popular among embedded devs during 80/90s.
| That's not my field, though.
| wolfadex wrote:
| There are Forth like languages, such as Kitten, that have
| variables and more.
|
| https://kittenlang.org/
| veltas wrote:
| Here's why it's interesting: https://termbin.com/blz3
|
| There you go, now it has parens, just backwards. Can even use
| parens instead of braces if you want, I just didn't want to
| overwrite comment syntax.
| Y_Y wrote:
| I copy your code below, because it's so terse and marvellous
| and I don't want to deprive anyone who assumes otherwise and
| doesn't click on your link: : >BRACES
| 1 CELLS BRACES +! BRACES @ ! ; : BRACES>
| BRACES @ @ -1 CELLS BRACES +! ; : {
| DEPTH >BRACES ; : } DEPTH BRACES> 1+ <>
| ABORT"unbalanced" ;
|
| And the example "backwards lisp" expressions you give as test
| cases: { 1 2 + } . { 5 { 1
| 2 + } * }
| eschneider wrote:
| Forth is awesome in situations where you've got a small
| environment, with crap tools, and you need to do some iterative
| development. In a pinch, you could hand assemble a Forth kernel
| and just burn it on an EEPROM and away you go. There are
| usually nicer alternatives today, but when you had to build
| your tools up from nothing, it was a very reasonable option.
| dreamcompiler wrote:
| Correct. You can build a Forth compiler from hand-typed hex
| machine code. It will be a bit easier if you have an
| assembler handy, but that's not essential. Trying to build a
| Lisp compiler this way would be madness.
| bunderbunder wrote:
| I guess you could say that syntactically it's like lisp, only
| backwards and without the parens. But the deeper truth is that
| concatenative programming and procedural programming (or
| functional, I suppose, depending on your preferred lisp flavor)
| are very different paradigms that lead to fundamental
| differences in how you structure your code.
|
| I haven't used Forth in anger for over two decades now, but in
| some ways I do miss how the language felt even more hackable
| than a good lisp, while simultaneously being easy to reason
| about at a low level. That said, the RPN and stack manipulation
| definitely make it a bit of a brain teaser, especially at
| first. In that sense it reminds me of Prolog and APL and, for
| that matter, Lisp: they're languages where the greatest joy of
| learning them is the way they force you to think in very
| different ways, and how that can promote epiphanies about
| computation.
|
| I would need to spend more time grokking this language to
| really understand, but it appears that the author has
| effectively achieved something that's semantically equivalent
| to Forth, but has a lot of syntactic commonality with Lisp.
| That's rather compelling to me from a "eliminates the first and
| most noticeable stumbling block for newcomers" perspective.
| That said, I'm having to repress a bit of a desire to react the
| same way parts of the Racket community did when Matthew Flatt
| announced his infix syntax project. Is a key part of Forth's
| concatenative nature lost when the syntax itself is no longer
| concatenative? I don't know, and I'll admit it's very doubtful
| that I'll spend enough time with Forsp to find out. But it's an
| interesting thought, all the same.
| qludes wrote:
| The coolest thing about Forth to me wasn't Forth itself but
| that it was a part of the OpenFirmware standard:
| https://standards.ieee.org/ieee/1275/1932/ I never understood
| why that standard just died and wasn't adopted for something
| like ARM SBCs where the built in debugging ability would come
| in useful.
| astrobe_ wrote:
| The comparison between Lisp and Forth that is often made is
| very shallow. It's more than just prefix vs postfix. Lisp takes
| garbage collection for granted, Forth is designed to avoid any
| complication and doesn't even have heap allocation.
|
| The lack of local variables and named parameter is also a
| consequence of this search for ultra-low complexity, in
| dialects that follow the traditional style (the one of the
| creator of Forth, Chuck Moore).
|
| The readability argument is one that should always be taken
| 1024 grains of salt. You say Lisp is more readable, yet you'll
| find some people to say that it's "Lots of Insipid and Stupid
| Parenthesis" and that prefix notation is unnatural. You could
| also find positive opinions about APL. It is just a matter of
| training and habits.
| alexisread wrote:
| Hmm, really nice! I might have to steal all of this for my
| language
| (https://github.com/alexisread/minim/blob/develop/minim/minim...
| really in flux ATM)
|
| Reminds me of https://pygmy.utoh.org/3ins4th.html except the
| third instruction is execute rather than quote.
|
| In the spirit of building from scratch, I'd like to highlight
| sectorforth/milliforth
| (https://github.com/fuzzballcat/milliForth/blob/master/sector...)
| - they implement as little as possible to get a working system
| ie. just the basic fetch/store ops, numerical and I/O.
|
| The parser can probably be reduced - there's a nice paper
| detailing Cognition (a forth dialect, but the parsing could be
| broken out into a lib -
| https://ret2pop.nullring.xyz/blog/cognition.html) where using a
| couple of crank operators, you can implement custom syntax in a
| forth-style way (as opposed to say PEGs).
|
| In terms of variables and scoping, Dreams (another forth dialect-
| http://elilabs.com/~rj/dreams/dreams-rep.html) uses mianly
| dynamic scoping, and builds it's structs (C-style structs) using
| the standard forth struct words. This allows it to be hard-
| realtime though as you don't need to walk the lexical tree to
| bind variables. You can also early bind like with CBPV. I guess
| this is also similar to REBOL's BINDology
| (https://github.com/r3n/rebol-wiki/wiki/Bindology)
|
| Lots of overlap here with these things. Just for completeness
| there's also dynamic variable handling (over lexically scoped
| structures) with wat (https://github.com/GiacomoCau/wat-
| js/tree/master) though this is not realtime.
|
| Is it possible to make the closures dynamically scoped by default
| here? I've not had time to think about this properly. I like the
| fact that eval is lazy here like in forth or REBOL, rather than
| eager as in lisp - the idea of passing around blocks/thunks
| appeals wrt realtime (well with dynamic scoping) and parallelism.
|
| The env per-thread I guess could be compared to the forth
| dictionary? Dreams abstracts this by virtue of it's (token)
| threading model which appears useful for task switching, objects
| and the like.
|
| I'd also like to highlight able forth which has: Compiler-only
| design (like Freeforth) - Word-classes (like Retro) - A
| straightforward bootstrap process using a minimal set of seed
| words (like seedForth) - No (ZERO) special forms, not even
| integers, and a consistent model for adding literals without the
| complexity of i.e. recognises - A single flat word-list that can
| be freely manipulated and composed of other word-lists - No
| separate [assembly] code words - Explicit optimizations (ONLY)
| i.e. explicit tail-call elimination
|
| I've only started looking into able forth so can't really comment
| on it much, but the no-special-forms appeals here.
|
| Sorry, random thoughts here, I'd like to discuss further when
| I've had time to digest your language. :)
| xorvoid wrote:
| Happy to discuss. Email address can be found on my website.
| im3w1l wrote:
| Another recent attempt at a language combining features of Forth
| and Lisp
| https://github.com/rickardnorlander/misc/tree/main/cursed_la...
|
| Someone even asked the same question ' seems
| redundant. 'x could just be (x)?
|
| though for that language the answer was that yes it's exactly the
| same.
| sevensor wrote:
| What I like about this is how neatly it reduces everything to a
| small set of concepts. Much like lisp and forth, but not the same
| as either. It's exciting to think that there may be more of these
| out there, waiting to be discovered!
| kitd wrote:
| > _It 's a hybrid language combining Forth and Lisp, so naturally
| it's called Forsp_
|
| Shame. Missed a golden opportunity to call it "Lithp" :)
| qludes wrote:
| Someone could still call the libre implementation of Forsp
| Lithp so not all is lost.
| pmarreck wrote:
| This looks really neat, although I'm still wrapping my head
| around it! It's funny how things that are even more fundamentally
| simple than what has been discovered, can be (initially) harder
| to reason about!
|
| So you could presumably also write a Lisp interpreter AND a Forth
| interpreter using it?
|
| (Might as well write a Turing machine interpreter too for the
| trifecta... assuming one can decide on the syntax...)
|
| That plus some additional functionality to make it more usable in
| the general sense (adding math functions, string manipulation,
| maybe some basic I/O) and it might be a VERY interesting
| instructional tool.
|
| I've heard the term "thunk" but I forget what it means...
| kazinator wrote:
| A thunk is a piece of glue machine code that adjusts some
| pointer (which is imagined to make a "thunk" sound) and then
| calls the real code.
| NackerHughes wrote:
| This is incredible. I'll be tinkering with this for a while,
| guaranteed. As the advantages of combining Lisp and Forth in this
| way are slowly revealed to me it's like unlocking parts of my
| brain to interact with each other that never have done before.
|
| Pretentiousness on my part aside, this is a pretty mind-blowing
| concept. The interpreter being in less than 1000 lines of
| (comprehensible) C is all the more commendable (most of these
| minimal languages turn out to be some monolithic Rust thing or
| similar, which kinda defeats the entire purpose imo).
|
| Excited to take a closer look at the source to see how the
| various data structures etc. are laid out in memory. I won't be
| able to resist making comparisons to my current/other favourite
| mini-language 'fe' (https://github.com/rxi/fe), a sub-1000-line
| Lisp also written in C. If you haven't seen that one I'd
| definitely recommend checking it out.
|
| Thank you for putting this online. Happy hacking!
| incanus77 wrote:
| I am in love with 'fe', as well as the slightly-better IMHO
| 'aria' by the same author.
| xorvoid wrote:
| Thanks for the kind words. But if you're looking for fancy
| data-structures, you won't find them. Haha. It's just a single
| object type (sum-type/tagged-union) and lots of cons cells. In
| other words, just a minimalist Lisp.
| pierrebai wrote:
| Reading the example, I really wish the syntax for push and pop
| had been "<foo" (push foo on stack) and ">bar" (pop from stack
| into bar). I find the choice of $ and ^ not obvious. Especially
| since, for me, ^ implies popping, not pushing.
| xorvoid wrote:
| You can fork and fix that with sed. Haha.
|
| I decided on using sigils and then my thought-process drifted
| to Perl/Php, so $ naturally felt like a variable definition
| (which naturally must pop from the stack). I guess ^ is "push
| 'up' to stack". But it's all really arbitrary irrelevant
| syntax. YMMV.
| quibono wrote:
| Interesting. I'm not sure about $ and ^ either but I think I'd
| keep getting confused about which one of < and > meant pushing
| vs pulling
___________________________________________________________________
(page generated 2024-06-11 23:01 UTC)