For those not familiar with munificent, you may recognize his moral conviction from one of his previous creations, Vigil: https://github.com/munificent/vigil
I'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.
To 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.
Seems 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.
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:
> 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.
This 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.
> 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.
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.
The 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.
I wrote a long reply, but HN apparently thinks I'm a spammer on this thread and ate it. :( Let me see if I can recall:
> If something is big, slow and message I don't care if it solves my problem when nothing else can.
This is true of a general purpose language. I'm sure most Ruby or Python users couldn't tell you the first thing about how big or complex MRI or CPython is. As long as it works, it works.
Embedded languages are a different beast. When you drop something like Lua or Wren into your application, you're taking a direct dependency on its source code. You will end up stepping into in your debugger, reading its comments, etc. You will be shipping the language implementation itself physically with your application.
When that's the case, having a small, clean implementation becomes a very important feature.
> 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".
Syntax is about usability, not utility. It's almost never a question of "Can I do this using classes." Everything is Turing complete, so you could use Brainfuck as your scripting language. The key question is, "Is it easier, more enjoyable, and less error-prone to use classes?"
If you're writing OOP-style code, which people increasingly do to script apps, I believe the answer is yes.
Here's an example of that kind of object-oriented programming in Lua:
Account = {}
Account.__index = Account
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
acc = Account.create(1000)
acc:withdraw(100)
Here's the same example in Wren:
class Account {
new(balance) { _balance = balance }
withdraw(amount) { _balance = _balance - amount }
}
// create and use an Account
var acc = new Account(100)
acc.withdraw(100)
I believe this is a real productivity and maintainability improvement, and productivity is exactly what scripting languages are for.
> Just guessing, but probably #1 is the difficulty of integrating it into their existing project (and ease of extendibility with my app).
Yup! That's also a key requirement (and one I still have a lot of work to do on.)
In terms of embedding, are you planning on supporting C++ classes? For example, with Luabind (and a lot of wrapper boilerplate) you can define a virtual class in C++, create a subclass in Lua, then use that in all the nifty polymorphic ways you'd expect.
In other words, will you have a more powerful C++ API on top of the plain old C API? Or is that a project for someone else, once Wren is a bit more mature?
That's a good question. Wren needs to support a non-C++ API because some potential users are using vanilla C. But I'm not at all opposed to also having a more class-based C++ API that wraps that.
Whether I implement or someone else and whether it ships with Wren are open questions, but I'm open to pretty much any answer for them.
I believe OP was pointing out an issue with the marketing... just copy a language as simple as Lua, but that feels natural to someone with an OOP background (or something similar) to your first bullet point!
Edit: looks like your original comment made it through, and this repeat may best be deleted. Keep up the good work.
Yeah I was really just trying to point out that the marketing on the main page can be improved. I get that OOP can be a selling feature, but the other four not so much. Using the main page to talk about how great it does integration, how great the docs and examples are, and the API compatibility story are all things that would be better for convincing users to go to the next page. Using the funnel model how many users only viewed the main page and never went further?
> Yeah I was really just trying to point out that the marketing on the main page can be improved. I get that OOP can be a selling feature, but the other four not so much.
It's a totally fair point.
One thing I'm trying to keep in mind is who I'm marketing to. Right now, since Wren is so young, the people whose attention I want are not end users. They'd have to be half crazy to use Wren in its current state in a real application.
The people that will take an interest now are those that are keen on new languages for their own novelty's sake, or that are interesting in contributing to Wren itself. That's the audience I had in mind, and I think the bullet points make a bit more sense when you consider that.
Definitely, when Wren gets more mature, it makes sense to refocus the initial verbiage on what it can do for and end user.
I totally agree about Lua and classes; this is a breath of fresh air.
I have played around with Lua for years, and I like it in theory, mainly because it has a nice implementation. The C API is especially nice (vs say Python).
But personally I think the language itself is ugly, and inferior to Python in almost every way. Python's classes are a bit annoying to me, but Lua's idioms for classes are even worse (and I never felt prototypes really lived up to their promise). I could never get myself to write anything more than toys with Lua; it was just cumbersome.
Wren looks like a really solid design. I think scripting languages went way too far to the "left" with prototypes and the dynamic behavior of classes (including JS, etc.). This seems a lot more sensible and usable. It looks a bit like Dart (AFAICT), which is probably no coincidence?
EDIT: I noticed that the embedding API is not fleshed out yet. Random drive-by ideas/questions:
- Are you going to model it after Lua as you did the parser? I don't think that's a bad place to start -- the stack-based model seems strictly superior to dozens of ad hoc methods like Python. And I believe Python itself is a lot cleaner than Perl, Ruby, v8, or the JVM. The Lua authors did a great job with this comparison paper: http://www.jucs.org/jucs_13_6/c_apis_in_extension
- I always wanted channels between C/C++ and a scripting language. That is, an idiomatic and supported way of running the scripting language would be in another thread. I think of it as a single-threaded "control plane" in a scripting language, and then multithreaded C/C++ for the "data plane". And you use Go-like channels to communicate between the two. I wrote something like this for Python and multithreaded C++ (which is a nice of making the GIL irrelevant). This can of course be implemented as an optional layer on top of a Lua- or Python-like API.
It is a compromise between 1) the "old style" C APIs where you have to write tons of glue, and then it ends up being auto-generated with some tool that nobody understands like SWIG; and 2) the new style like ctypes or LuaJIT where you have to duplicate type information in your source code. If you mess up or the ABI changes then you get crashes.
CFFI has a model where you paste in incomplete C declarations and it safely generates a binding, which I think is quite nice.
I guess it is a little C-specific. Not sure how it would interact with C++. I think both C and C++ are important use cases.
> I always wanted channels between C/C++ and a scripting language.
Wren is deeply built around fibers. It doesn't use the native C stack, so my hope is that it will be pretty easy to be able to suspend the interpreter to switch back to C and then resume it when you go back. All of the mechanism is there, I just have to see how it feels in practice.
You can build channels on top of fibers, so I'm really keen to play around with some different concurrency and communication models here and see what they feel like.
Seems like fibers/coroutines are a curiosity here, how useful are they supposed to be from a performance perspective? Example, say I have a thread with a wren vm instance running with a bunch of fibers inside, and a fiber calls back to a C function that's blocking (on io say) then this still blocks the entire thread.
Seems like there'd have to be a way to integrate with an event loop, and register a fiber with an event (edit or file descriptor/socket). Contemplating this makes me sleepy and grouchy ie it's very abstract, have you given any thought to this use case?
> Seems like fibers/coroutines are a curiosity here, how useful are they supposed to be from a performance perspective?
Think of them as a way to organize your program, not a way to optimize it. Fibers let you have multiple independent imperative flows of behavior that you can bounce between without having to return from one in order to get to the other.
Instead of always having to organize your behavior in a single rigid stack of "can't do X until done doing Y", you can have as many separate stacks as you want.
You can also hook them up to something like asynchronous IO where an IO operation will implicitly park a fiber and let others run. I did that using libuv in my other language Magpie [1].
That's also really nice, but it naturally requires more low level support and has some implications on how fibers are scheduled. In Wren, fibers are explicitly cooperative—you specifically choose which fiber to resume when you suspend one.
If you're parking on IO, you need some more implicit logic so the scheduler can automatically pick a fiber to resume. In practice, I think that just means that if all fibers are parked waiting on IO, the scheduler resumes whichever one's IO operation completes first.
I've come to subscribe to a school of thought that says a constructor should never do anything other than assign its arguments to fields. Perhaps validate them, i suppose. But not compute with them - if you want to do computation for the creation of an object, write a factory method.
For me and my fellow disciples, it would therefore be really nice to have a constructor syntax that does this implicitly. So instead of writing:
class Customer {
new(customerNumber, name, address) {
_customerNumber = customerNumber
_name = name
_address = address
}
}
We could just write something like:
class Customer {
new(customerNumber, name, address)
}
Or, if you like Scala:
class Customer(customerNumber, name, address) {}
I'm a Java programmer by day, and some thoroughly depressing fraction of the lines of code in my codebases are of the form:
private final AmazingObjectOfSomeKind amazing;
private final BrilliantObjectOfSomeOtherKind brilliant;
private final CrappyObjectOfAnyKind crappy;
public Foo(AmazingObjectOfSomeKind amazing,
BrilliantObjectOfSomeOtherKind brilliant,
CrappyObjectOfAnyKind crappy) {
this.amazing = amazing;
this.brilliant = brilliant;
this.crappy = crappy;
}
Users of other languages quite rightly point and laugh at me for this. Now, Wren doesn't have those types cluttering up the place, nor does it have the field declarations, but it does still have unnecessary verbosity, and that verbosity provides crevices for bugs to hide in (add a parameter to a constructor, forget to add a corresponding assignment). It might be nice to add a dash of syntactic sugar to eliminate this problem.
Another facet of this school's approach is that objects are generally immutable. So instead of:
> it would therefore be really nice to have a constructor syntax that does this implicitly.
Agreed. I'm on the Dart team at work, and in Dart you'd do:
class Customer {
int customerNumber;
String name;
String address;
Customer(this.customerNumber, this.name, this.address);
}
You do still have to declare them. I've thought about adding something similar to Wren. Since fields are already syntactically distinguished, it could be as simple as:
class Customer {
new(_customerNumber, _name, _address) {}
}
I've hesitated because I want to see how much this is really needed in real-world code. I'm trying to keep things as simple as possible at least as first.
> There's another bit of Scala sugar that is nice here - an automatic copy method which takes keyword arguments to override particular fields.
I'm not opposed to the idea, but it probably doesn't carry its weight. Wren is incredibly minimal. I like immutability a lot, but for scripting language where program size is relatively small anyway, I don't think it's quite as high a priority.
I 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?
This 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).
The 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.
I 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))
> (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:
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:
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 "=&g...
> 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.
In "Finding the Needle"[0], SPJ et al came up with a nice stack trace representation for GHC that handles recursion. Not sure if you've seen the paper, but it might be worthwhile if you haven't (section 4, specifically).
> 2. We could make "if" an expression. We could make everything an expression for that matter, though there's some pros and cons there.
I'm not sure if this is the place for such a far-reaching discussion, but I've never understood this point. What are the cons to making everything an expression?
Until recently I was firmly in the "statements are evil" camp. Now my perspective is a bit more nuanced. There are two facets to "everything is an expression":
1. Any grammatical production can be used in an expression.
2. Function/method bodies implicitly return the result of the last expression they contain.
I really like #1, though even expression-oriented languages limit this. There's still something akin to "operator precedence" at the "statement-list" expression level. If, for example, you look at Ruby's grammar, you'll see it has productions called "statements".
This is to rule out totally bizarre things like:
1 + while (foo) { ... }
for (for (for (...) {}) {})
So, even if you treat everything as an expression, there are still some limitations as to where different grammatical productions can appear.
In practice, I think the main thing you gain (in a language with an imperative-style syntax like Wren, Ruby, etc.) is the ability to have the right side of a return or assignment be a branching statement. Stuff like:
var fizzBuzz = if (n % 15) {
"FizzBuzz"
} else if (n % 5) {
"Buzz"
} else if (n % 3) {
"Fizz"
} else n
The other statements are less useful. I'm not even sure what the result of a looping expression should be, but I'm certain that I don't like CoffeeScript's answer (an array of all of the elements, which often gets discarded!). Break and return statements have no useful results. Variable declaration statements make it really hard to reason about scope when used in the middle of an expression.
All this means that I think loosening up the grammar to make every statement an expression is mostly only useful to make branching an expression.
The other half is having functions implicitly return the result of the last expression. I really like that in some functions for brevity, and really dislike it in others. This may not be rational, but creeps me out that lots of Ruby methods just leak random values.
For me, I'd rather have an explicit form for bodies that implicity return their result and have the default be to not do that. So that's what I did for Wren. A single-line body implicitly returns its result, but a multi-line one does not:
class Person {
name { "Fred" } // Implicit return.
sayHi {
IO.write("Hi, I'm Fred!")
// Returns null.
}
}
I like this more than always having an implicit return, so this addresses point #2 above. I'm not opposed to allowing branch statements to be expressions, but I worry that the confusion it causes may outweight the benefit, so I conservatively left it out for now.
Having a statement/expression distinction also makes the bytecode compilation a bit simpler. It's easier to tell which things emit values and which don't, but that's not hard to fix.
> I'm not even sure what the result of a looping expression should be
This is one of the reasons I like having proper tail calls be part of a language specification, incidentally :-)
Reason being that loops are then simple derived constructs, and their meaning (as expressions and otherwise) follows from their definition.
In the specific case of "while", the value of a while-loop-like form would usually be null/void, because of the one-armed-if it expands into.
Regarding implicit return values: in your example, the sayHi method would naturally return null if IO.write returned null, even with implicit return values. Having a strong library convention that imperative procedures return null seems to prevent programmers from trying to rely on the results of imperative procedures, even where implicit return values are part of the language. My support for this claim is only anecdotal; it comes from working with programs written in Racket, which has both implicit return values and a strong convention of returning void from imperative procedures.
> In the specific case of "while", the value of a while-loop-like form would usually be null/void, because of the one-armed-if it expands into.
Right, which doesn't make it very useful as an expression.
> Having a strong library convention that imperative procedures return null seems to prevent programmers from trying to rely on the results of imperative procedures
I have the opposite feeling (possibly because Wren doesn't have implicit returns). Since library calls are expressions, they may as well return something useful when possible.
For example IO.print() returns its argument. That's handy for debugging the result of an intermediate expression. Say you've got:
someMethod(anotherOne(arg))
And something weird is going on. Giving IO.print() a return value lets you inject it in the middle:
someMethod(IO.print(anotherOne(arg)))
without having to hoist the subexpression out.
> My support for this claim is only anecdotal; it comes from working with programs written in Racket, which has both implicit return values and a strong convention of returning void from imperative procedures.
Yeah, that makes sense. I think the important idea is to be consistent.
> I'm not even sure what the result of a looping expression should be
>Break and return statements have no useful results
The result of a looping expression could be specified by its break!
var myNeedle = foreach(var maybeNeedle in myHaystack) {
if (maybeNeedle.isTheNeedle()) break maybeNeedle;
}
if (myNeedle != null) print("I found " + myNeedle.toString());
I quite often find myself writing code like (in Java):
Fruit edibleFruit = null;
for (Fruit f: basket) {
if (f instanceof Banana) {
if (!f.isDiscoloured()) {
edibleFruit = f;
break;
}
} else {
if (!f.isMouldy()) {
edibleFruit = f;
break;
}
}
}
And getting rid of the nasty assign/break pairs by extracting a method:
Fruit edibleFruit = edible(basket);
Fruit edible(Collection<Fruit>: basket) {
for (Fruit f: basket) {
if (f instanceof Banana) {
if (!f.isDiscoloured()) return f;
} else {
if (!f.isMouldy()) return f;
}
}
return null;
}
But your idea would let me clean up without having to extract a method (here, in a hypothetical Java-with-value-bearing-break):
Fruit edibleFruit = for (Fruit f: basket) {
if (f instanceof Banana) {
if (!f.isDiscoloured()) break f;
} else {
if (!f.isMouldy()) break f;
}
}
You need to have a story about what happens if there isn't a break - if the loop exits normally. You could implicitly return null, but that's a terrible idea. Better would be to require else clauses on value-returning loops (a bit like the else clauses that Python allows on try blocks):
Fruit edibleFruit = for (Fruit f: basket) {
if (f instanceof Banana) {
if (!f.isDiscoloured()) break f;
} else {
if (!f.isMouldy()) break f;
}
} else null;
Yes, i'm still returning null here, but at least i have to do it explicitly, which makes me feel bad about it and think about finding a better alternative!
> For me, I'd rather have an explicit form for bodies that implicity return their result and have the default be to not do that. So that's what I did for Wren.
That strikes me as an excellent decision.
> A single-line body implicitly returns its result, but a multi-line one does not
That, i'm sorry to say, does not! Assigning different semantics to two passages of code whose only syntactic difference is in the composition of their whitespace seems like it might be a misfeature.
Is it too late to consider a louder way of marking the difference? How about:
name = { "Fred" } // implicit return
Or even just introducing a terser explicit return:
name { = "Fred" } // explicit but barfulous return
name { ^"Fred" } // explicit return, also homage to Smalltalk
> Assigning different semantics to two passages of code whose only syntactic difference is in the composition of their whitespace seems like it might be a misfeature.
I'll admit it's a bit iffy. However, newlines are already significant as statement separators, so this isn't totally outside the bounds of reason.
name = { "Fred" } // implicit return
This looks a little too similar to a setter definition to me. Also, it makes sense here, but would look really strange for a lambda:
people.map {= |person| person.name }
The typical solution is "=>", like:
people.map (person) => person.name
But then without a trailing delimiter, it's hard to chain multiple of these calls together, which is really common in list manipulation.
I'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?
This 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.
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).
I 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.
> 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.
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.)]
That starts to look fairly complex to me. In code that I've looked at, doing numeric iteration is actually not that common. The majority is just iterating over collections. Wren's grammar for loops is actually really simple. It's just:
for (<variable name> in <expression>) { ... }
That's it. Note that there's no special support for numeric ranges. Instead, ".." and "..." are just regular operator calls. The number type implements them to return a range object. That lets you do:
for (n in 1..10) { ... }
But also enables:
var range = 1..10
for (n in range) { ... }
And even:
var items = ["a", "b", "c", "d"]
var slice = items[1..2]
Making the range a first-class object lets you get a lot of mileage out of using it in different places, which is nice. In fact, a range derives from the sequence base class, so you can also do:
Yup, strongly agree that having a single iterate-over-collection construct and a nice notation for ranges has big advantages. (I did say that before, but I guess it was buried in among a lot of other stuff.)
The drawback is that the nice terse range notation leaves it unclear whether your ranges are inclusive or exclusive (and I conjecture that if you asked 100 experienced software people to guess which of 1..8 and 1...8 is which, you'd get something close to 50% jumping each way).
What I'd really like is a syntax for ranges that has clarity comparable to "for (0 <= i < n)" but terseness comparable to "0...n", without being too much a special case. Unsurprisingly, I've never yet seen one.
Here's a less-than-half-baked proposal, though. Probably too quirky for Wren.
1. Some syntax like [1 <= i < n] -- inequality operators, delimited in an appropriate way -- indicates a range object and, at least at parse time, carries around an indication of the name of the index variable. (Index variables if we allow fancy stuff like [1 <= i < j < n].)
2. "for <literal-range>" is syntactic sugar for "for (i in <literal-range>)" where i is the name of the variable. (If we allow fancy multi-variable ranges, this only works if your for-loop is able to bind multiple variables as with "for (i,j) in [(1,2),(3,4)]" in Python.)
3. Perhaps "for <literal-range> where <condition>" is likewise syntactic sugar for "for (i in <literal-range>.where(<condition>))" or something.
4. So far, this is less general than your .. and ... operators because it's all special-cased for integers. Except there's no reason why it needs to be. Perhaps, e.g., [a < i < b] invokes a magical method on a, which gets passed the upper bound b and an indication of direction and inclusivity at each end. My fancy multi-variable forms can be implemented in terms of this without any further magic, though not maximally efficiently.
[EDITED to add: It happens that I quite often have use for numeric iteration, and even for the fancy multi-variable forms I've described here. But I'm probably atypical.]
It 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!
65 comments
[ 2.9 ms ] story [ 47.8 ms ] threadUnfortunately 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.
Does it make me unusual to find classical object models shouldn't be considered a 'usual' object model?
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.
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
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:
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.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
> 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.
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.
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.
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/46c1ba92492e9257aba6...
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.
http://munificent.github.io/wren/qa.html#why-did-you-create-...
> If something is big, slow and message I don't care if it solves my problem when nothing else can.
This is true of a general purpose language. I'm sure most Ruby or Python users couldn't tell you the first thing about how big or complex MRI or CPython is. As long as it works, it works.
Embedded languages are a different beast. When you drop something like Lua or Wren into your application, you're taking a direct dependency on its source code. You will end up stepping into in your debugger, reading its comments, etc. You will be shipping the language implementation itself physically with your application.
When that's the case, having a small, clean implementation becomes a very important feature.
> 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".
Syntax is about usability, not utility. It's almost never a question of "Can I do this using classes." Everything is Turing complete, so you could use Brainfuck as your scripting language. The key question is, "Is it easier, more enjoyable, and less error-prone to use classes?"
If you're writing OOP-style code, which people increasingly do to script apps, I believe the answer is yes.
Here's an example of that kind of object-oriented programming in Lua:
Here's the same example in Wren: I believe this is a real productivity and maintainability improvement, and productivity is exactly what scripting languages are for.> Just guessing, but probably #1 is the difficulty of integrating it into their existing project (and ease of extendibility with my app).
Yup! That's also a key requirement (and one I still have a lot of work to do on.)
In other words, will you have a more powerful C++ API on top of the plain old C API? Or is that a project for someone else, once Wren is a bit more mature?
Whether I implement or someone else and whether it ships with Wren are open questions, but I'm open to pretty much any answer for them.
Edit: looks like your original comment made it through, and this repeat may best be deleted. Keep up the good work.
It's a totally fair point.
One thing I'm trying to keep in mind is who I'm marketing to. Right now, since Wren is so young, the people whose attention I want are not end users. They'd have to be half crazy to use Wren in its current state in a real application.
The people that will take an interest now are those that are keen on new languages for their own novelty's sake, or that are interesting in contributing to Wren itself. That's the audience I had in mind, and I think the bullet points make a bit more sense when you consider that.
Definitely, when Wren gets more mature, it makes sense to refocus the initial verbiage on what it can do for and end user.
I have played around with Lua for years, and I like it in theory, mainly because it has a nice implementation. The C API is especially nice (vs say Python).
But personally I think the language itself is ugly, and inferior to Python in almost every way. Python's classes are a bit annoying to me, but Lua's idioms for classes are even worse (and I never felt prototypes really lived up to their promise). I could never get myself to write anything more than toys with Lua; it was just cumbersome.
Wren looks like a really solid design. I think scripting languages went way too far to the "left" with prototypes and the dynamic behavior of classes (including JS, etc.). This seems a lot more sensible and usable. It looks a bit like Dart (AFAICT), which is probably no coincidence?
EDIT: I noticed that the embedding API is not fleshed out yet. Random drive-by ideas/questions:
- Are you going to model it after Lua as you did the parser? I don't think that's a bad place to start -- the stack-based model seems strictly superior to dozens of ad hoc methods like Python. And I believe Python itself is a lot cleaner than Perl, Ruby, v8, or the JVM. The Lua authors did a great job with this comparison paper: http://www.jucs.org/jucs_13_6/c_apis_in_extension
- I always wanted channels between C/C++ and a scripting language. That is, an idiomatic and supported way of running the scripting language would be in another thread. I think of it as a single-threaded "control plane" in a scripting language, and then multithreaded C/C++ for the "data plane". And you use Go-like channels to communicate between the two. I wrote something like this for Python and multithreaded C++ (which is a nice of making the GIL irrelevant). This can of course be implemented as an optional layer on top of a Lua- or Python-like API.
- I've only played around with this a little, but there is a newer CFFI package for Python: https://cffi.readthedocs.org/en/release-0.8/
It is a compromise between 1) the "old style" C APIs where you have to write tons of glue, and then it ends up being auto-generated with some tool that nobody understands like SWIG; and 2) the new style like ctypes or LuaJIT where you have to duplicate type information in your source code. If you mess up or the ABI changes then you get crashes.
CFFI has a model where you paste in incomplete C declarations and it safely generates a binding, which I think is quite nice.
I guess it is a little C-specific. Not sure how it would interact with C++. I think both C and C++ are important use cases.
Probably more convergent evolution than anything, but there are certainly similarities. :)
> Are you going to model it after Lua as you did the parser?
Yes, absolutely. My mantra is "when in doubt, do what Lua does", which works surprisingly well. I have so much respect for Ierusalimschy and company.
You can see the in-progress API here: https://github.com/munificent/wren/blob/master/include/wren....
> They did a great job with this comparison paper: http://www.jucs.org/jucs_13_6/c_apis_in_extension
Oh, man. This is perfect!
> I always wanted channels between C/C++ and a scripting language.
Wren is deeply built around fibers. It doesn't use the native C stack, so my hope is that it will be pretty easy to be able to suspend the interpreter to switch back to C and then resume it when you go back. All of the mechanism is there, I just have to see how it feels in practice.
You can build channels on top of fibers, so I'm really keen to play around with some different concurrency and communication models here and see what they feel like.
> I think both C and C++ are important use cases.
Me too.
Seems like there'd have to be a way to integrate with an event loop, and register a fiber with an event (edit or file descriptor/socket). Contemplating this makes me sleepy and grouchy ie it's very abstract, have you given any thought to this use case?
Think of them as a way to organize your program, not a way to optimize it. Fibers let you have multiple independent imperative flows of behavior that you can bounce between without having to return from one in order to get to the other.
Instead of always having to organize your behavior in a single rigid stack of "can't do X until done doing Y", you can have as many separate stacks as you want.
You can also hook them up to something like asynchronous IO where an IO operation will implicitly park a fiber and let others run. I did that using libuv in my other language Magpie [1].
That's also really nice, but it naturally requires more low level support and has some implications on how fibers are scheduled. In Wren, fibers are explicitly cooperative—you specifically choose which fiber to resume when you suspend one.
If you're parking on IO, you need some more implicit logic so the scheduler can automatically pick a fiber to resume. In practice, I think that just means that if all fibers are parked waiting on IO, the scheduler resumes whichever one's IO operation completes first.
[1]: http://magpie-lang.org/
For me and my fellow disciples, it would therefore be really nice to have a constructor syntax that does this implicitly. So instead of writing:
We could just write something like: Or, if you like Scala: I'm a Java programmer by day, and some thoroughly depressing fraction of the lines of code in my codebases are of the form: Users of other languages quite rightly point and laugh at me for this. Now, Wren doesn't have those types cluttering up the place, nor does it have the field declarations, but it does still have unnecessary verbosity, and that verbosity provides crevices for bugs to hide in (add a parameter to a constructor, forget to add a corresponding assignment). It might be nice to add a dash of syntactic sugar to eliminate this problem.Another facet of this school's approach is that objects are generally immutable. So instead of:
We would write: (Although in fact, a bank account might be one of the cases where we wouldn't do this, but never mind)That gets old as soon as you have a few fields. Like:
There's another bit of Scala sugar that is nice here - an automatic copy method which takes keyword arguments to override particular fields. Like: Might something like that might be useful enough to justify its weight?Agreed. I'm on the Dart team at work, and in Dart you'd do:
You do still have to declare them. I've thought about adding something similar to Wren. Since fields are already syntactically distinguished, it could be as simple as: I've hesitated because I want to see how much this is really needed in real-world code. I'm trying to keep things as simple as possible at least as first.> There's another bit of Scala sugar that is nice here - an automatic copy method which takes keyword arguments to override particular fields.
I'm not opposed to the idea, but it probably doesn't carry its weight. Wren is incredibly minimal. I like immutability a lot, but for scripting language where program size is relatively small anyway, I don't think it's quite as high a priority.
https://news.ycombinator.com/item?id=7634734
Its syntax is also a little strange. The following isn't accepted by the parser:
but the same file with extra newlines is accepted just fine: It also makes me a little sad that returns 0, while returns null.Not yet, but it's getting there! I have to fix this:
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:
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.
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
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 "=&g...
> 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.
In "Finding the Needle"[0], SPJ et al came up with a nice stack trace representation for GHC that handles recursion. Not sure if you've seen the paper, but it might be worthwhile if you haven't (section 4, specifically).
[0]: http://research.microsoft.com/en-us/um/people/simonpj/papers...
I'm not sure if this is the place for such a far-reaching discussion, but I've never understood this point. What are the cons to making everything an expression?
https://mail.python.org/pipermail/python-ideas/2008-Septembe...
1. Any grammatical production can be used in an expression.
2. Function/method bodies implicitly return the result of the last expression they contain.
I really like #1, though even expression-oriented languages limit this. There's still something akin to "operator precedence" at the "statement-list" expression level. If, for example, you look at Ruby's grammar, you'll see it has productions called "statements".
This is to rule out totally bizarre things like:
So, even if you treat everything as an expression, there are still some limitations as to where different grammatical productions can appear.In practice, I think the main thing you gain (in a language with an imperative-style syntax like Wren, Ruby, etc.) is the ability to have the right side of a return or assignment be a branching statement. Stuff like:
The other statements are less useful. I'm not even sure what the result of a looping expression should be, but I'm certain that I don't like CoffeeScript's answer (an array of all of the elements, which often gets discarded!). Break and return statements have no useful results. Variable declaration statements make it really hard to reason about scope when used in the middle of an expression.All this means that I think loosening up the grammar to make every statement an expression is mostly only useful to make branching an expression.
The other half is having functions implicitly return the result of the last expression. I really like that in some functions for brevity, and really dislike it in others. This may not be rational, but creeps me out that lots of Ruby methods just leak random values.
For me, I'd rather have an explicit form for bodies that implicity return their result and have the default be to not do that. So that's what I did for Wren. A single-line body implicitly returns its result, but a multi-line one does not:
I like this more than always having an implicit return, so this addresses point #2 above. I'm not opposed to allowing branch statements to be expressions, but I worry that the confusion it causes may outweight the benefit, so I conservatively left it out for now.Having a statement/expression distinction also makes the bytecode compilation a bit simpler. It's easier to tell which things emit values and which don't, but that's not hard to fix.
This is one of the reasons I like having proper tail calls be part of a language specification, incidentally :-)
Reason being that loops are then simple derived constructs, and their meaning (as expressions and otherwise) follows from their definition.
In the specific case of "while", the value of a while-loop-like form would usually be null/void, because of the one-armed-if it expands into.
Regarding implicit return values: in your example, the sayHi method would naturally return null if IO.write returned null, even with implicit return values. Having a strong library convention that imperative procedures return null seems to prevent programmers from trying to rely on the results of imperative procedures, even where implicit return values are part of the language. My support for this claim is only anecdotal; it comes from working with programs written in Racket, which has both implicit return values and a strong convention of returning void from imperative procedures.
Right, which doesn't make it very useful as an expression.
> Having a strong library convention that imperative procedures return null seems to prevent programmers from trying to rely on the results of imperative procedures
I have the opposite feeling (possibly because Wren doesn't have implicit returns). Since library calls are expressions, they may as well return something useful when possible.
For example IO.print() returns its argument. That's handy for debugging the result of an intermediate expression. Say you've got:
And something weird is going on. Giving IO.print() a return value lets you inject it in the middle: without having to hoist the subexpression out.> My support for this claim is only anecdotal; it comes from working with programs written in Racket, which has both implicit return values and a strong convention of returning void from imperative procedures.
Yeah, that makes sense. I think the important idea is to be consistent.
>Break and return statements have no useful results
The result of a looping expression could be specified by its break!
That strikes me as an excellent decision.
> A single-line body implicitly returns its result, but a multi-line one does not
That, i'm sorry to say, does not! Assigning different semantics to two passages of code whose only syntactic difference is in the composition of their whitespace seems like it might be a misfeature.
Is it too late to consider a louder way of marking the difference? How about:
Or even just introducing a terser explicit return:I'll admit it's a bit iffy. However, newlines are already significant as statement separators, so this isn't totally outside the bounds of reason.
This looks a little too similar to a setter definition to me. Also, it makes sense here, but would look really strange for a lambda: The typical solution is "=>", like: But then without a trailing delimiter, it's hard to chain multiple of these calls together, which is really common in list manipulation.But this is actually a cool little language with well thought out features. I can see it being very useful for embedding.
[1] - http://squirrel-lang.org/
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.
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).
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
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.
This plays pretty well with filtering as in list comprehensions:
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: 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.)]
The drawback is that the nice terse range notation leaves it unclear whether your ranges are inclusive or exclusive (and I conjecture that if you asked 100 experienced software people to guess which of 1..8 and 1...8 is which, you'd get something close to 50% jumping each way).
What I'd really like is a syntax for ranges that has clarity comparable to "for (0 <= i < n)" but terseness comparable to "0...n", without being too much a special case. Unsurprisingly, I've never yet seen one.
Here's a less-than-half-baked proposal, though. Probably too quirky for Wren.
1. Some syntax like [1 <= i < n] -- inequality operators, delimited in an appropriate way -- indicates a range object and, at least at parse time, carries around an indication of the name of the index variable. (Index variables if we allow fancy stuff like [1 <= i < j < n].)
2. "for <literal-range>" is syntactic sugar for "for (i in <literal-range>)" where i is the name of the variable. (If we allow fancy multi-variable ranges, this only works if your for-loop is able to bind multiple variables as with "for (i,j) in [(1,2),(3,4)]" in Python.)
3. Perhaps "for <literal-range> where <condition>" is likewise syntactic sugar for "for (i in <literal-range>.where(<condition>))" or something.
4. So far, this is less general than your .. and ... operators because it's all special-cased for integers. Except there's no reason why it needs to be. Perhaps, e.g., [a < i < b] invokes a magical method on a, which gets passed the upper bound b and an indication of direction and inclusivity at each end. My fancy multi-variable forms can be implemented in terms of this without any further magic, though not maximally efficiently.
[EDITED to add: It happens that I quite often have use for numeric iteration, and even for the fancy multi-variable forms I've described here. But I'm probably atypical.]
http://i.imgur.com/XMZE04i.png
If it takes off for game engines then it could be used for scripting canvas/webgl games too.
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!