10 ms·
Forsp: A Forth+Lisp Hybrid Lambda Calculus Language
- um1 2y agoVery 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 https://dl.acm.org/doi/pdf/10.1145/181993.181999 and wonder if/how it relates.
- diffxx 2y agoThank 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 2y agoAlso 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 2y agowhat is cvbp? google yields nothing.
- alexisread 2y agoLooks 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 2y agoI 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 2y agoA friend mentioned RPL, but that didn’t seem to even have first-class functions… unless I’m mistaken?
- ceving 2y agoYou can put functions on the stack.
- agumonkey 2y agowith arrow syntax even << a -> << a a * >> >> 'SQUARE' STO I still can't believe how advance RPL calculators were
- thsksbd 2y agoThe 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-interpretation-of-computer-programs-spring-2005/video_galleries/video-lectures/ https://ocw.mit.edu/courses/6-001-structure-and-interpretati...
- thsksbd 2y agoIm 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 2y agoCool language. One modest suggestion: perhaps return 0 rather than 1. Feature request: addition and division as well as subtraction and multiplication ;)
- xorvoid 2y agoAddition 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 2y ago> 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 2y agoHa. Well I meant “add those operations to the C interpreter”, which would be like 4 lines of code.
- throw156754228 2y agoWow 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 2y agoI'd take a leaf out of Freeforth's compiler (https://github.com/dan4thewin/FreeForth2 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 2y agoI feel like simple reference counting + linear types would be a really good approach in this case.
- kazinator 2y agoWhat 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 2y agoI 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 2y agoIf you have time, could you explain the ycombinator and if parts a bit more? Been a while since I looked at this stuff
- tonyg 2y agoCould ^a be dispensed with? It looks like (a) is equivalent to it.
- dugmartin 2y agoI think the equivalent would be ('a) instead as (a) would resolve to "'result of application of a' push".
- tonyg 2y agoThe 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 2y agoSubtly 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 2y agoOperationally, 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 2y agoI 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 2y agoNice, anyone using this? Have practical feedback? Does this have potential to grow, or would most people just say 'use lisp'.
- FrustratedMonky 2y agoI 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 2y agoI really like the syntax and ergonomics - nice job @xorvoid.
- pbrhocwp 2y agoVery nice! Thanks a lot for the discovery.
- mkovach 2y agoSince 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 2y agoUser 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 2y agoForth+Lisp = Wet dreams of every programming nerd
- CyberDildonics 2y agoUntil you have to deliver something and you realize you love programming languages more than creating software.
- CrociDB 2y agonothing wrong with enjoying playing with your guitar more than actually making music.
- CyberDildonics 2y agoNothing wrong with it until it's time to make music and you need a normal guitar.
- James_K 2y agoI'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 2y agoForths 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 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 https://gforth.org/manual/Local-Variables-Tutorial.html
- anta40 2y agoMe 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 2y agoThere are Forth like languages, such as Kitten, that have variables and more. https://kittenlang.org/ https://kittenlang.org/
- James_K 2y agoDo they have any compelling reason why "List<T>" isn't just "T List"? That's how they write it in OCaml. Then comma separated lists, are they mad? I much prefer how Uiua does this.
- veltas 2y agoHere's why it's interesting: https://termbin.com/blz3 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.
- alexisread 2y agoHmm, really nice! I might have to steal all of this for my language (https://github.com/alexisread/minim/blob/develop/minim/minim.org https://github.com/alexisread/minim/blob/develop/minim/minim... really in flux ATM) Reminds me of https://pygmy.utoh.org/3ins4th.html 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.asm 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 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 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 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 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. :)
- im3w1l 2y agoAnother recent attempt at a language combining features of Forth and Lisp https://github.com/rickardnorlander/misc/tree/main/cursed_lang 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.
- deleted 2y ago[deleted]
- sevensor 2y agoWhat 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 2y ago> 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 2y agoSomeone could still call the libre implementation of Forsp Lithp so not all is lost.
- pmarreck 2y agoThis 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 2y agoA 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.
- pmarreck 2y agoIt's a delayed evaluation, as I understand it?
- alexisread 2y agoThat's the way I tend to read it. The fun bit then is applying context at eval time. Rebol allows you to use a block as a list/function/thunk, which is lazily evaluated. This changes the scoping in that you BIND a context to this thunk. It's a different take on process migration in that the context can be serialised with the thunk, or a new context applied at the process site. This is in contrast to lexically scoped lambda functions which then need to account for dynamic (free) variables somehow.
- kazinator 2y agoThere is a usage of thunk in the context of implementing a compilation strategy for call-by-name arguments. The usage survives into modern times, whereby lambda arguments are sometimes called thunks.
- 2y ago
- NackerHughes 2y agoThis 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 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 2y agoI am in love with 'fe', as well as the slightly-better IMHO 'aria' by the same author.
- xorvoid 2y agoThanks 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 2y agoReading 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 2y agoYou 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.
- dandanua 2y ago'$' is common for interpolation in many languages. It's a good choice. Awesome language, and it's definitely important from a theoretical point of view. However, you've missed the chance to call it "LiFo" :)
- quibono 2y agoInteresting. I'm not sure about $ and ^ either but I think I'd keep getting confused about which one of < and > meant pushing vs pulling
- dugmartin 2y agoMaybe + for push and - for pop?
- ungamedplayer 2y agoIf you like to work the other way I present https://github.com/garlic0x1/forth https://github.com/garlic0x1/forth forth in common lisp.
- tonymontana69 2y agoReminds me of the language from this paper, which also shows how to smoothly integrate I/O and state into such a style https://arxiv.org/abs/2212.08177 https://arxiv.org/abs/2212.08177