6 ms·
Spry: a programming language inspired by Smalltalk/Rebol and written in Nim
- chdjdjdnc 10y agoI really, really like the concept, but I strongly dislike the syntax. Excessive punctuation is the enemy of code readability. Someone need to make lisp without the parentheses, where scoping can also be managed by indentation like in python. Throw in some strong imperative programming so that it's a lisp masquerading as C (because purism sucks), and you'd have one very powerful language
- 616c 10y agoWisp (aka Whitespace Lisp; actually Scheme but whatever): http://www.draketo.de/english/wisp/shakespeare http://www.draketo.de/english/wisp/shakespeare OpenDylan (I have heard it referred to HN as a Lisp without parens more than once): http://opendylan.org/ http://opendylan.org/ And Rebol, not being lispy by the standards of few/some/many, is insane with macros from what I gather. Someone posted Red, a Rebol analogue that is an open source attempt to duplicate its expressiveness out of admiration. http://www.red-lang.org/2015/12/answers-to-community-questions.html http://www.red-lang.org/2015/12/answers-to-community-questio... I am confused though because this is says macros are not there. I obviously don't use it, but the last HN thread about it made it sound crazy. https://news.ycombinator.com/item?id=11364447 https://news.ycombinator.com/item?id=11364447 Anyway, re the strong imperative thing, someone posted Bone Lisp a couple days back, but this will not please you, it is classic parens Lisp, sans GC (I read but still have no clue how that works).
- agumonkey 10y agoIIRC OpenDylan maintainer expressed regrets about the syntas a few times. An historical burden from some Apple decisions long ago.
- alayne 10y agoI still have Apple's Dylan book where it has Lisp-like syntax somewhere. The infix they came up with is ugly. Dylan was supposed to be used for Apple's Newton handheld, but was too late and basically died in the 1990s. I'm no expert, but I recall it was basically a dumbed down Lisp with OO extensions and module support that was changed to a dumbed down Algol with OO extensions and module support.
- adf 10y agoI have the books for Harlequin Dylan, but the language never got traction. It was basically an infix lisp with focus on hygiene (because it was basically a Lisp 1), and the possibility of sealing modules (making it impossible to alter / inherit from them ). This IIRC; it's a long time ago...
- SamReidHughes 10y ago> Excessive punctuation is the enemy of code readability. So why is a standard trope about Lisp that with some acclimation the parentheses disappear? This is something that depends on what you're used to, and in some cases identifiable characteristics of individuals' visual processing, it's not a universal truth.
- shrugger2 10y agoIt's really easy to tell when people make these comments about Lisp with little to no knowledge about lisp, because they always revolve around "lisp without parens" as if that makes ANY sense at all. Indenting lisp code is not a semantic feature of the parentheses, it's something lisp programmers use for convenience. Python indentation serves a completely different purpose, as indenting a line can completely change how a function operates. Furthermore, "purism sucks" is probably just bait anyways. Bless you, you poor thing.
- akkartik 10y agoIt's a toy like several others mentioned in this thread, but this language I worked on has ultra powerful lisp macros (f-exprs), guesses parens from indentation, provides infix, and supports imperative programming as well as Arc: http://akkartik.name/post/wart http://akkartik.name/post/wart. Here's an example showing off all these features: https://gist.github.com/akkartik/4320819 https://gist.github.com/akkartik/4320819
- dkersten 10y agoSo, if you leave out parentheses it uses indentation and switches to infix?
- akkartik 10y agoNo, the two are independent. The rules for parentheses: 1. Multi-word lines without leading parens are grouped with later indented lines. 2. Indentation is not sensitive inside parens. More info, unpacking the implications: https://github.com/akkartik/wart/blob/47e3572a29/004optional_parens https://github.com/akkartik/wart/blob/47e3572a29/004optional... Infix works by using a disjoint set of 'operator characters' that isn't available to prefix symbols. More info: https://github.com/akkartik/wart/blob/47e3572a29/006infix https://github.com/akkartik/wart/blob/47e3572a29/006infix
- kevin_thibedeau 10y agoTcl is lisp without parens. Doesn't get much love these days but a still a serviceable workhorse that has been gradually modernized in the last few releases.
- jrapdx3 10y agoThanks for mentioning Tcl, in a lot of ways it resembles Lisp/Scheme, and with addition of tailcall, apply, coroutines the resemblance has increased over time. It remains very useful and versatile, and Tcl/Tk may still be the easiest way to create a GUI application that will run on several platforms.
- reikonomusha 10y agoLisp can already be written like C. Most of the Lisp crowd doesn't give into any notion of "purism". The parentheses of lisp provide a canonical serialization format. What you're actually asking for is "don't represent lists with brackets." Even Python uses brackets (and commas) for lists.
- 6502nerdface 10y ago> Someone need to make lisp without the parentheses [...] Throw in some strong imperative programming so that it's a lisp masquerading as C This actually describes the R programming language pretty well. A lisp with C-like syntax and vectorized primitives.
- jsmith0295 10y agoI agree that symbols tend to make code harder to read, but I find that it's the number of different symbols that mostly cause issues, and that a few symbols that are used in simple and obvious ways, like parens and curly braces, don't tend to make code more difficult to read.
- oconnor0 10y agoOne person's excessive punctuation is another's dream.
- riffraff 10y agoI think the problem is more the example than the syntax, it basically has keyword arguments (foo: bar), square brackets for lambda ([echo :x]) and :foo for block arguments. I.e. this 1 to: 5 do: [echo :x] is pseudopython's loop(from=1, to=5, do=lambda x: echo x) loop(1, 5, lambda x: echo x) which have comparable punctuation, mostly trading "," for ":" . Being smalltalkish, you also get extra characters when a block is used for control structures, but that saves on the need to have two different syntaxes for single and multi-line/expression lambdas or N syntaxes for blocks and control structures. How do you see that handled in your ideal language?
- gokr 10y agoYes, correct. It's worth noting that echo is a prefix function so Spry supports both infix (first argument is on the left) and prefix functions, and if there is a series of keywords the parser will rewrite the keyword call to a prefix/infix call like this: array at: 1 put: 2 ==> array at:put: 1 2 Thus a keyword call is purely syntactic sugar.
- gokr 10y agoAnd also, the block [echo :x] doesn't use declarations of parameters - instead the ":x" is an operation that pulls in the next argument from the call site and stores it into the local binding x in the closure. Almost Forth-ish, but there is no stack.
- pmontra 10y agoRuby, another language in the Smalltalk lineage, did without the [ ] at the cost of the "end" keyword to terminate blocks, but there is also the equivalent {} notation, recommended for one liners. A Python indented Ruby would probably able to do without [ ] but I should think about whether there are some unresolvable ambiguities with that approach. I won't recommend that personally because syntactical spaces introduce bugs. I run into one a few days ago when I moved code around a file and forgot to fix the indentation of a few lines. Luckily this one cost me only five minutes. Many people like Python though. Overall I like the terseness of 1 to: 5 do: [echo :x] instead of the more verbose loop(1, 5, lambda x: echo x) # Python (1..5).each do { |x| echo x } # Ruby (1..5).each do |x| # Ruby again echo x end But all those : are annoying: they increase the noise/signal ratio. I'd even do without the | | in the multi line Ruby version. I wonder if the compiler could add the : automatically by matching what it sees with the signatures of the defined function. 1 to 5 do [echo :x] Then, why you need :x and couldn't use x without the : ? Furthermore, how do I know how the name of the block argument if I didn't write the function. Isn't that an unnecessary coupling between the name chosen by the developer of the library and my code? I probably didn't understand everything is going on here. Edit: it has been explained in another comment in the parent's thread. And there are so many commonly used languages that use { } to define code blocks and [ ] for arrays. I'd stick with the majority for an easier onboarding of developers. The possibility of defining pseudo keywords thanks to the infix/suffix arguments is great. That's probably anathema in the Python world (only one way of doing things) and also in Go's. It should be acceptable in Ruby's. I'd prefer something like this func (n)to(m)do(blk) { x = n ... do blk x ... } The order of arguments and function names is clear because you read them from left to right without going through two different lists. Is anybody using Spry for real world programs?
- gokr 10y agoJust curious about what you refer to as "excessive punctuation"? You mean blocks []? There are no statement separators (!) in Spry and the grammar is very, very minimalistic. You could argue in some ways Spry is "a Lisp without parenthesis", and although I like indentation (Nim uses it) for scoping I also find Smalltalk VERY readable. Also, it's not yet apparent but the syntax/grammar of Spry (and homoiconicity) makes it very suitable for DSL construction, very much similar to Rebol/Red.
- sebastianconcpt 10y agoIs close to english :)
- progman 10y ago> Someone need to make lisp without the parentheses, where scoping can also be managed by indentation like in python. Throw in some strong imperative programming so that it's a lisp masquerading as C (because purism sucks), and you'd have one very powerful language Funny, that's an almost exact description of Nim :-) http://nim-lang.org http://nim-lang.org
- xaduha 10y agoI think I'll stick with Red.
- adamnemecek 10y agoWhat are you using it for if I may ask? And how are you finding it?
- xaduha 10y agoIt's not in a workhorse languages category yet, but in a hobby languages, alongside Perl 6. I'm interested in Parse dialect, seems very powerful. Wasn't a user of Rebol previously, but I like that Red mostly reimplements it, without straying to the sides.
- gokr 10y agoAnd yeah, Parse is very interesting. I would like to do something similar in Spry but perhaps borrowing more from PetitParser (a very nice parser combinator library in Smalltalk from Lukas Renggli)
- gokr 10y agoAnd if you like Rebol you should. Spry is nowhere near as evolved as Red (which is darn impressive) and Spry is also moving towards Smalltalk more than Rebol. I also think there is quite a bit of difference in philosophy. Rebol/Red loves adding tons of hardwired datatypes into the language, while Spry is more Lisp/Smalltalkish in minimalism. Also, Rebol shunned OO which Spry doesn't (Red... not so sure). But shunning OO is popular these days :)
- knocte 10y agoDynamically typed? Ok I'll move on thanks.
- gokr 10y ago(author of Spry) Just a few notes: I like the cleanliness of Lisp, but I have always found it hard to read. That's one reason for Spry to have a bit more syntactic elements than Lisp. Spry supports both prefix and keyword style function calls, although as a Smalltalker by heart I favour keyword style. Finally, Spry is still an evolving experiment. Next up is the OO model which I hope will be interesting.
- epidemian 10y agoThe language is quite interesting! The responses on this thread have been quite a letdown so far though :( You mention that the OO model is not yet implemented, however, there are a few snippets of code on the homepage that look like OO. I can understand that when you implement `select:` as an infix function then `[1 2 3] select: [something]` is dispatched to that function instead of using polymorphic dispatch on the "receiver". But, how are things like `clone` or `whileFalse:` implemented? Are they primitives? Are they using some other primitives under the hood? I'm asking because while syntactically it looks like Smalltalk, i don't see how it could work the same way if there's no OO system under the hood. In Smalltalk, something like `whileFalse:` would be implemented as a method on the condition block, which, in turn, would execute itself and decide whether to recur or not by sending an `ifTrue:` (or `ifFalse:`) message on the resulting boolean. And the polymorphic implementation of that `ifTrue:` message on booleans would ultimately cause the loop to stop or not. In other words: there's no primitive "if", nor loops, on Smalltalk; it's message sending all the way down. Do you envision having some similar all-encompassing design principle on Spry too? Also, kind of side note: i noticed that you're using the same syntax for blocks and lists. I understand this is something inherited from Rebol/Red, but i haven't used that language, so i don't really grok how that works. It's not something that most mainstream languages share, do you think you could dedicate some part of the language manual to explaining how can lists and blocks be "the same thing"? :D
- gokr 10y agoThanks! And oh, for longer discussions do pop in on gitter, gokr/spry. Some attempts at answers: Yes, the select: there is not polymorphic, it's simply a function (as of right now). clone is a primitive function, can be seen here: https://github.com/gokr/spry/blob/master/src/spryvm.nim#L1804 https://github.com/gokr/spry/blob/master/src/spryvm.nim#L180... (search for "clone" on that page to find the Nim implementations for the various basic node types) whileFalse: is also a primitive and can be seen here: https://github.com/gokr/spry/blob/master/src/spryvm.nim#L1904-L1911 https://github.com/gokr/spry/blob/master/src/spryvm.nim#L190... So yes, these are examples of primitive Spry functions that are implemented in Nim. Regarding the larger question, you can implement control structures in Spry, but they are obviously quite a lot slower. Also, it's worth noting that whileTrue: and friends are normally in Smalltalk implemented using special case bytecode tricks (jump). But you are correct in wondering about polymorphism, and Rebol doesn't AFAIK have anything like it - Carl shunned OO when creating Rebol. In Spry I am working on a concept of tags and what I call polyfuncs. Any node can have one or more tags added to it and functions can have multiple sub functions dispatched over based on these tags. The basics of this already works: https://github.com/gokr/spry/blob/master/src/sprytest.nim#L458-L459 https://github.com/gokr/spry/blob/master/src/sprytest.nim#L4... Hard to read I guess, but it basically creates a polymorphic function "inc" that has two different sub funcs selected based on the tag of the receiver. So about "design principle" - both yes and no. Spry is more "shallow" than typical Smalltalks because Spry is being implemented much more tightly together with Nim. This is by design, it makes it MUCH easier to make primitives and to interface with Nim and thus also C/C++. It's already trivial to do, and a lot of the examples show that. But I also want Spry to feel similarly "coherent" as Smalltalk does. And yes, definitely will describe the block/list thing in the manual. Too bad this HN post happened before the manual is out there, but oh well. But generally this is what homoiconicity means - code is in the same format as the core data structures. You can thus create and manipulate code as lists - just as in Lisp.
- nikolay 10y ago"funci", really?
- gokr 10y agofunci for infix. func for prefix. May end up changing that.
- nikolay 10y agoIt's a terrible choice!
- gokr 10y agoBut hardly important :) I may actually end up not needing the distinction, but if so, then I think a good convention on documenting funcs is needed. Well, needed anyway. :)
- nikolay 10y agoI want my programing language to be as close to natural English as possible. Just voicing an opinon. There's a niche market for as cryptic languages as possible, too!
- sebastianconcpt 10y agoJust curious, are you working in one? link?
- dockimbel 10y agoGoran, congrats for this release and the nice website! It is interesting to see where mixing ideas from Red/Rebol and Smalltalk can bring us to. What are the main pros/cons of using Nim as the underlying language?
- gokr 10y agoHey! Thanks. Not me who put it here on HN though :) Regarding Nim - IMHO the main big pro is the fact that I can piggy back on the GC, use several of Nim's features (dynamic dispatch using methods for example), target all three of C/C++/js. And finally, I can use the very good C/C++ wrapping capabilities of Nim. As you may know wrapping C++ is ... very hard, but if you let Nim compile via C++ it works great. I also find Nim very, very nice to work in. And yeah, I hope Spry will show some interesting results of mixing Rebol/Red ideas with Smalltalk. And I do need to study Rebol/Red more. ;)
- gokr 10y agoAlso, Nim has very promising support for threads, threadpools, async stuff etc etc. And when I rewrite the Spry interpreter to be stackless I hope to be able to use a lot of those things too.
- apricot13 10y agoFor a moment I thought dreamweaver was making a comeback! https://en.wikipedia.org/wiki/Spry_framework https://en.wikipedia.org/wiki/Spry_framework
- desireco42 10y agoThis looks super interesting to me :) something I really thought we do need. I will be looking into how Spry develops in the future.