13 ms·
Wren: A classy little scripting language
- phyllostachys 12y agoI'm wondering how munificent feels about Squirrel[1]. It seems like Wren is looking to fill the same niche that Squirrel fits. So I guess my question is, how are they different, or, how is Wren better? [1] - http://squirrel-lang.org/ http://squirrel-lang.org/
- munificent 12y agoThis is a great question! I probably can't do the answer justice in a HN comment, and I don't know Squirrel as well as I'd like to, but here's some random thoughts. At a high level, Wren and Squirrel have a lot of overlap. They're both dynamically-typed embedded scripting languages with an eye towards games and a syntax that tries to look more familiar to someone from the C tradition. Some differences are: - Wren is much younger. That means it's much less mature, but it also has less baggage and cruft. - Squirrel is a good bit more complex than Wren in general. I'm trying really hard to keep Wren small and simple and have a minimal set of orthogonal, combinable features. Squirrel has a larger baked-in feature set. This means squirrel sometimes has multiple ways to accomplish the same goal: generators and threads, classes and tables, etc. With Wren I'm trying to get as much mileage from as few features as I can. (For example, Wren uses fibers for error-handling.) - Squirrels tables are pretty similar to JS objects and Lua tables, so some of Squirrel has that prototype-y feel. That's kind of cool, but it also brings in some stuff I don't like. For example, I think Squirrel has the problem that Lua and JS have where "this" may not be bound to the object that you think it is because it doesn't have a real method/function separation. In Wren, "this" does exactly what you want, even in closures. - Squirrel is written in C++. Wren is pure C99. - Squirrel is primarily ref-counted with an opt-in mark-sweep GC (to clean up cycles, I assume). Wren is straight mark-sweep (though I anticipate going to incremental at some point for latency). - Squirrel uses 8-bit or 16-bit character strings. Wren uses UTF-8 (or, at least, it will as that gets fleshed out more). If you want something stable and usable today with all the features you could want, Squirrel is probably a good fit. If you're looking for something that feels a little more minimal and hews to a more modern aesthetic, that's what I'm aiming for with Wren.
- phyllostachys 12y ago> Squirrel is written in C++. Wren is pure C99. Awesome. My primary interest in Lua and Squirrel was to embed a scripting language in a C environment on a microcontroller. This is perfectly doable (and has been done before) but Lua is particularly attractive because it is written in ANSI C. I think it is interesting how similar embedded systems devs and game devs view their code with an eye towards performance and size. It is nice because embedded systems typically don't use newer languages and the developers tend to never move away from C as the language of choice (sometimes it seems like everyone is stuck in the 80s or 90s).
- munificent 12y agoI believe (but have no proof) that Wren is more amenable to running on a microcontroller than Lua or Squirrel. Wren has a more compact representation for instances of classes and simpler/faster method dispatch. It's still duck typed, but it gets closer to statically-typed language where an instance is a simple in-place array of fields and field access is just a pointer offset.
- untothebreach 12y agoI love munificent's opinions on programming langugaes (wish he'd never given up on magpie...), and I can't wait to try this one out. Unfortunately it doesn't build under GCC on linux right now, so use clang if you want to try it out.
- tobz 12y agoFor those not familiar with munificent, you may recognize his moral conviction from one of his previous creations, Vigil: https://github.com/munificent/vigil https://github.com/munificent/vigil
- munificent 12y ago> Unfortunately it doesn't build under GCC on linux right now, so use clang if you want to try it out. Can you file a bug? I know another user ran into some problems with this too, but I thought we'd flushed them all out.
- untothebreach 12y agoBug filed and PR sent! :)
- munificent 12y agoMerged! BOO-YA.
- deleted 12y ago[deleted]
- Havvy 12y ago"There are lots of scripting languages out there, but many have unusual or non-existent object models. Wren places classes front and center." Does it make me unusual to find classical object models shouldn't be considered a 'usual' object model?
- nly 12y agoWhat would you consider 'usual'? Classification as a cognitive process can be seen throughout science and engineering.
- Havvy 12y agoI'd consider structs that implement various traits (to use Rust's terminology) to be more of a 'usual' approach to an object model. You can see the approach with mixins in many scripting languages, Rust's entire object model, Haskell's typeclasses, Elixir and Clojure's protocols, ect. ect. The fact that you can classify things into a classification tree does not mean that the classification is how things actually work. If it so happens that classification trees work for a problem, you can build them with trait inheritance, but structural inheritance hasn't been that useful IME, so the marrying of both types with classical inheritance just leaves a bad taste in my mouth.
- nly 12y agoTo have 'structs that implement various traits' you must first have structs. That is, objects with some known immutable shape. Implementing any kind of trait (interface) to an 'object' having a mutable structure (like the dictionaries used for modelling objects in most dynamic languages) would be tricky without exposing some crap to the programmer at the language level. Off the top of my head you'd need at least something like Perls 'bless'[0] coupled with Rusts strong protections against mutation. Whether you choose to define your classes in the form of (possibly intersecting) sets of objects having some particular trait, or set of traits, or in some taxonomic tree is fairly irrelevant. The difference is largely a matter of taste. I think the point Bob (munificent) is making here is that 'scripting languages' (whatever you consider those to be) generally don't give you anything. [0] http://perldoc.perl.org/functions/bless.html http://perldoc.perl.org/functions/bless.html
- pierrec 12y agoSeems like a very cool language. Fibers are really just coroutines, right? The `Fiber.run` method allowing you to completely wreck the stack seems a bit dangerous at first glance. The everything-is-a method approach is also really neat. For example, it's neater than C#'s half-arsed approach where you can have public fields but it's not recommended. However, I find there should still be an easy way of creating public properties. It's useful when all you want is a structure holding a bunch of values instead of a full-featured class. From what I can see, the shortest way of creating a public property in Wren seems to be: class Cat { new { _name = defaultName } name { _name } name=(value) { _name = value } } That can be a lot of boilerplate, especially when you need lots of public properties. But that's just a minor nitpick, of course. I'm looking forward to actually trying this language, but I'm not sure about its level of completeness right now. It seems like everything is pretty complete, apart from some parts of the documentation.
- munificent 12y ago> Fibers are really just coroutines, right? Yup! I picked "fiber" mainly because it's shorter. > The `Fiber.run` method allowing you to completely wreck the stack seems a bit dangerous at first glance. In most cases, you'll use "call", but "run" is the fiber equivalent of tail call elimination. > However, I find there should still be an easy way of creating public properties. I'm trying to keep things minimal, but, yes, I'll likely add something for that. Wren grew out of a prototype-based language called Finch and the latter has "property definitions" that let you define a private field and accessor in one go: http://finch.stuffwithstuff.com/objects.html http://finch.stuffwithstuff.com/objects.html > I'm not sure about its level of completeness right now. A lot of language features are implemented and everything that's implemented has pretty solid tests. There's still a bunch of more minor stuff to do, but what it really lacks is real-world usage. I've been hacking on it in isolation (because implementing the language itself is the real fun part for me), but an embedded scripting language needs to be embedded in some real apps to get the critical feedback needed to make it useful. To make some progress there, I'm starting to hack together a little game engine using Wren (think something like Löve2D), but I haven't put much time into that yet.
- soapdog 12y agoWren appear very cool but I think Finch is more my cup of tea. Anyone has feedback on Finch or an opinion on Finch vs Wren?
- pierrec 12y agoThis is from the linked website, in the Q & A page: Wren is a replacement for Finch to me. I gave it a new name mainly so that I can keep Finch around in case other people want to take it and do something with it. I don't have any intention to work on it anymore.
- munificent 12y ago> Wren appear very cool but I think Finch is more my cup of tea. If you like it, that's great! I think it has some neat ideas but I'm personally not that keen on prototypes these days. Finch could really use someone to own it. If you want to adopt it and do cool stuff with it, by all means do so.
- abcd_f 12y ago> The codebase is ... lovingly-commented [0] For what it's worth, I absolutely love reading this sort of spanning descriptive comments. It makes the code that much more interesting and gives it a healthy dash of personality. I used to work with a guy who'd have header files start with pagefuls of comments with ASCII diagrams, book references, complete design rationale and what not, only to follow with just a handful of rather terse declarations, which still would be immediately clear thanks to the preceding narrative. That was so damn beautiful. [0] https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433 https://github.com/munificent/wren/blob/46c1ba92492e9257aba6...
- icefox 12y agoThe main page fails to communicate how the language is going to solve a users problems. The main page (which is what most people will only see) list the following features: Wren is small. Wren is clean. Wren is fast. Wren is class-based. Wren is a scripting language. If something is big, slow and message I don't care if it solves my problem when nothing else can. The only bullet point that comes close is the one talking about it being 'class-based', but I almost never hearing someone say "moving the code to classes will solve this problem". If that it is class based why would I use it over (as mentioned in the second sentence) Lua (or any one of the scripting "languages" that are just javascript in tiny form where I don't have to do any learning)? It seems like an easy pass for the majority of people that only read the main page. What problems do users that need an embedded scripting language have? Just guessing, but probably #1 is the difficulty of integrating it into their existing project (and ease of extendibility with my app). Followed by language compatibility, control over garbage collection, and docs so I can spend more time on my app and less time mucking with the scripting language. I am guessing that something like these are the pain points that the main page should talk about.
- deleted 12y ago[deleted]
- deleted 12y ago[deleted]
- deleted 12y ago
- captaincrowbar 12y agoFor those who don't know, this is from Robert Nystrom, author of Game Programming Patterns: http://gameprogrammingpatterns.com/ http://gameprogrammingpatterns.com/ https://news.ycombinator.com/item?id=7634734 https://news.ycombinator.com/item?id=7634734
- JadeNB 12y agoI know it's not really substantive, but it's tweaking my OCD: why does 'small' in the list of adjectives in the marketing code block have an embedded newline?
- munificent 12y agoNo good reason at all. Removed it.
- samirmenon 12y agoThis seems like a wonderful language for game development. I wonder if there will be some kind of graphics support, or if I can use the C API (when it is finished).
- munificent 12y agoThe intent is that you'd use the C API. Or, with luck, someone will make a little game engine similar to Löve2d that bundles Wren along with a renderer for you.
- tonyg 12y agoI like the idea very much, and the brevity and clarity of the implementation is pleasant. However, there are a few problems with it: \\/"- \_/ wren v0.0.0 > var F = new Fn { | x | F.call(x + 1) } > IO.print(F.call(10)) Segmentation fault This shows two things. (1) it's not yet a "safe" language implementation; (2) it doesn't support proper tail calls. Both are fixable, of course, but the lack of proper tail calls is a Bad Language Smell. Its syntax is also a little strange. The following isn't accepted by the parser: var F = new Fn { | x | if (x == 0) { return x } else { return F.call(x - 1) }} IO.print(F.call(10)) but the same file with extra newlines is accepted just fine: var F = new Fn { | x | if (x == 0) { return x } else { return F.call(x - 1) } } IO.print(F.call(10)) It also makes me a little sad that var F = new Fn { 0 } returns 0, while var F = new Fn { 0 } returns null.
- munificent 12y ago> (1) it's not yet a "safe" language implementation; Not yet, but it's getting there! I have to fix this: // TODO: Check for stack overflow. On line 335 of wren_vm.c. The actual fix is pretty straightforward. You just store the maximum stack height with each function and check that that doesn't overflow on a call. I just haven't gotten around to it. Wren should be pretty good about most other boundary case behavior. If not (and like this issue too!) it's a high priority bug. Your scripting language shouldn't crash! > (2) it doesn't support proper tail calls. Both are fixable, of course, but the lack of proper tail calls is a Bad Language Smell. That's correct. I have implemented TCO before. Wren's predecessor Finch supported them. Fibers in Wren already support an analogous feature. My current thoughts are that I likely will support this, but it will be explicitly opt-in. Instead of having the tail call automatically be detected and eliminate the parent stack frame, you'll have some specific syntax to say "this is a tailcall". This is for two reasons: 1. Most of the time, having a useful stack trace is really handy for debugging. For better or worse, stack traces are a standard tool for most users to understand when their code does something wrong. Eliminating all tail calls hurts this with little benefit in the majority of cases where you aren't doing any recursion (mutual or otherwise) and will never blow the stack. Of course, you can do clever things to regenerate stack traces while still doing general TCO, but that adds a lot of complexity and simplicity of implementation is an important feature. 2. With a language that has a non-sexpr syntax, it can be very hard for a user to tell which call actually is the tail call. Consider something like: { return (foo.bar + !baz).call ? some.thing : other / wise } Can you quickly identify if this code won't blow the stack or not? If a user comes along and makes some tiny change to that statement, can they be confident their program won't start failing on larger inputs? Given that, I think it's good to visibly call out "Hey, there's a tail call here. It's important!" in the source code. I'm not sure what the syntax will look like yet, but that's my intent. var F = new Fn { | x | if (x == 0) { return x } else { return F.call(x - 1) }} Designing a syntax that's simple, expressive, and beautiful is tricky! Coming up with one that's easy to compile with a single pass compiler is even more tricky. In this case, because you don't have a newline after the "|", it is looking for an expression body. "if" is a statement, not an expression. There's a bunch of ways this could be improved: 1. Certainly, the error message should be better! 2. We could make "if" an expression. We could make everything an expression for that matter, though there's some pros and cons there. 3. If could allow statement bodies in single-line functions. > but the same file with extra newlines is accepted just fine: Yup. This is probably the most unusual corner of Wren's syntax. It's documented here: http://munificent.github.io/wren/syntax.html#blocks http://munificent.github.io/wren/syntax.html#blocks The constraints I'm working with are: 1. I wanted a terse way to define single-expression members and functions that return their result. 2. I'm not a fan of implicit returns in larger bodies. I don't like the idea of methods implicitly leaking the result of their last expression even when that isn't intentional, and I think it tends to lead to less efficient bytecode. So I tried to come up with a specific syntax for a single-expression body. Other languages use stuff like "=>", but I wanted to try to not add more punctuation. Since newlines are already significant in the language, I figured I'd try using the newline after "|" or "{" as the signal. So far, I personally like how it feels, but feedback from others like you is definitely helpful. If it rubs a lot of people the wrong way, it's easily changed.
- jayvanguard 12y agoMy usual reaction to these things is "does the world really need another language/framework?" But this is actually a cool little language with well thought out features. I can see it being very useful for embedding.
- jafingi 12y agoGreat work! Love the well-documented source. Will definitely look through it and try out the language. I have used Swift quite a lot in 2014, and loved the way to do ranges. But it's the complete opposite of Wren: 1...8 includes BOTH 1 and 8. Wren would not include 8. Is this an error in the documentation, or the actual design of the language? In the beta of Swift, 1..8 was not including 8. But I really like a change in the final version where it instead is done using 1..<8 It makes it much easier to discern between the two; 1...8 1..8 vs. 1...8 1..<8
- munificent 12y ago> Is this an error in the documentation, or the actual design of the language? It's the actual design. It follows Ruby and (I think?) Perl. Personally "..." feels more naturally inclusive to me since it has one more "." than ".." and thus one more item in the result. But I thought it would be more helpful to follow in the footsteps of existing languages. The Swift solution is definitely explicit but looks kind of ugly to my eyes. Syntax design can be hard.
- gjm11 12y agoI wonder about something like for (0 <= i <= 10) which has obvious (and transparent) variants for making one or both bounds exclusive. For bonus points for (10 >= i >= 1) etc. for looping downwards. You could have unbounded loops too for (0 < i) where you can tell which bit is meant to be declaring a loop variable because it's always the one after the inequality. For extra points, make mathematicians happy with things like this: for (0 <= i < j < 10) which does 45 iterations or for (1 <= i,j,k <= n) which does n^3 iterations, where again you can tell which bits are meant to be declaring loop variables: all but the first and last. These are less transparent because they don't make it clear what order things should be done in; they might also introduce the temptation to introduce fancy syntax to control this, to skip to "the next value of i", etc., which might be complexity that doesn't pay its way. This plays pretty well with filtering as in list comprehensions: for (1 < i < j < 100) where (isGood(i) && isBad(j)) { ... } One possibly-killer drawback to this kind of thing: it makes for a nice for-loop syntax but there's a lot to be said for saying that for-loops are always just iterating over some sort of collection, and turning these into collections might again require more syntactic complication than they justify. E.g., the simplest would be something like this: for (i in (i : 0 <= i <= 10)) which feels painfully repetitititititive with all those "i"s and doesn't generalize well to the fancy versions with multiple variables (but maybe those are a bad idea anyway). It's not hard to think of ways to reduce the repetition but they have their own drawbacks and in any case this comment is too long already. [EDITED to add: All this only makes sense if your syntax allows for "chained" inequality operators. But it really should anyway. (It looks like Wren's doesn't at present, maybe because it's hard to square with the principle that infix operators are always just single-arg method calls in disguise.)]
- ac2u 12y agoNot often I find a C project that can compile to JS via emscripten without too much work. http://i.imgur.com/XMZE04i.png http://i.imgur.com/XMZE04i.png If it takes off for game engines then it could be used for scripting canvas/webgl games too.
- munificent 12y agoNeat! I'd wondered about that but didn't have the time to try.
- ac2u 12y agoIt more or less worked out of the box with latest emscripten, the only thing I replaced was the call to getline with an inline version pulled from google code. More work needed to make the REPL more user friendly in HTML (emscripten uses browser prompts to get input when you follow basic instructions to output to HTML but could tweak to have a more user-friendly REPL experience). Additionally you can do things like the what has been done with the emscripten port of Lua and supply wren with a bridge interface to work with the DOM directly. From that it's a short path to having <script language="text/wren"> with a bootstrap script to parse them out and feed to the wren engine :-) Of course if it were as easy as typing this out i'd be doing it already!