Lunar, a superset programming language of Lua 5.1

scripting

#1

Hello everyone! For the last two and a half months, I’ve been working on Lunar. Lunar is a superset programming language of Lua 5.1.

That means Lunar can take your existing Lua project, and extend it with Lunar’s features!

This project is still early in development! We are basically releasing a “demo” version of Lunar.

Lunar implements proper classes, self-assignment operators, and lambda expressions. More to come later! You can learn Lunar by reading the language documentation, which has examples too.

Features

Classes

class Account
    constructor(name, balance)
        self.name = name
        self.balance = balance or 0
    end

    function deposit(credit)
        self.balance += credit
    end

    function withdraw(debit)
        self.balance -= debit
    end
end

local account = Account.new("Jeff Bezos", 500)
print(account.balance) --> 500
account:deposit(250)
print(account.balance) --> 750
account:withdraw(300)
print(account.balance) --> 450

Self-assignment operators

No more of those annoyingly verbose a = a .. b! Now you can write a ..= b instead. ..=, +=, -=, *=, /=, and ^= are all supported.

Lambda expressions

Note: Syntax is debatable and understandably so. All I ask right now is for you to try it out.

local Players = game:GetService("Players")

local minimumAgeLimit = 7
local isNewPlayer = |p| p.AccountAge < minimumAgeLimit -- implicit return, only a single expression

Players.PlayerAdded:Connect(|p| do
    if isNewPlayer(p) then
        p:Kick("Sorry! Your account must be " .. minimumAgeLimit .. " days or older to be allowed in.")
    end
end)

Examples

I created a project in Lunar, which is an object oriented admin script named Commander. Note this is not meant to be production ready at all. It serves two purposes: to test usability of Lunar, and to exist as a case study.

How do I try out Lunar?

Currently we don’t precompile binaries, so you’ll need to follow these instructions. Absolutely do ask questions! I will be compiling these questions and making a wiki for it.

You’ll need a .lunarconfig file in your project folder so Lunar knows which folder to target! Assuming you have project and spec, you should have include = { "project", "spec" } in your .lunarconfig file. Also assuming you want the output folder to be named dist, then you need to add out_dir = "dist" as well. Both of these variables are required for Lunar.


closed #2

This topic was automatically closed after 1 minute. New replies are no longer allowed.


opened #3

#4

It seems to me that this is an off-site project. You may want to change the logo since you can get in trouble for using one of the Roblox’s trademarks.


#5

That’s totally fair. I’ll work on a new logo as soon as a decent design comes to my mind! Originally it was meant to be Roblox specific, but I felt that this had the potential to be used by other non-Roblox projects.


#6

If you want to, I could try to come up with a design tho you’d have to look for someone to make it since I won’t be having access to a computer for a while.


#7

How you think implement this to ROBLOX?


#8

If you were asking how to get the compiled code into Roblox: You’ll need a syncing program for that. I personally use Rojo.


#9

This sounds good I hope to see this working in the future. I really like your good idea the idea of an exention to the programing langauage (Lua). However, one thing you could change is to not use the roblox logo and change it to something else. I hope for the best of luck for this project in development and I hope your project will help us do great things that simplfiy our scripting.


#10

@Krunnie @VDS06 We have a new logo now! @Elttob (Twitter: @ImElttob) and I worked on it together. I came up with the idea and he created the logo design.


#11

This looks like it could be interesting! Though, I have to ask: is there any particular motivation to making lambda notation in Lua beyond syntactic sugar? Unsigned functions are already supported and easy to make, so it feels weird.

If you want to make a genuinely good superset of Lua though, I would suggest static typing. Sugar is good and well, but one of the main problems with serious development with Lua is that it’s dynamically typed out the wazoo. This is the same issue that spawned Roblox.ts (a TypeScript transpiler for Roblox) and Zeuxcg (I believe, please correct me if I’m wrong) to try to make Lua statically typed as a hack week once upon a time. Dynamically typed languages get complicated for larger codebases.

Also, on a more directed question, how does type respond to classes in Lunar? I assume you’re breaking them down into tables, but if you can’t distinguish between the class types it makes a bit of the built in OOP meaningless.


#12

@Dekkonot I personally dislike having to explicitly state types because I think it slows down the workflow for no reason
If the type is non-obvious and necessary to know, you can always optionally add a comment
This kind of works nicely too, because when it is the case that you do need to comment the type, you also probably need to comment on more of the features of the function


I think it would be cool if you added ++x,x++,--x,and x--
also it would be very nice if you could switch comments to use // instead of --
(maybe an option to disable backwards compatibility for these kinds of features)

int floor division (written as // in lua 5.3) is also very nice, along with the lua 5.3 bit library

I would really enjoy a syntax that is briefer than all the lua keywords (then,repeat,until,do, etc), but still avoids having to use the shift key

if you are going to be doing these things, please let me know because I’d probably like to switch my framework to use lunar : P


#13

Thanks for asking! Yes, there are more reasons beyond syntactic sugar going for Lunar. One major point is to increase productivity, but we also want to add static typing and better language integration with editors through language server, including intellisense.

  1. Regarding lambda expressions, you’re right, it’s easy to create anonymous functions in Lua. However, one of the ideas floating in my head is the methods you see in functional programming such as map, filter, etc. For example, assume you want to kick players whose AccountAge is less than 7, you could very easily pass predicates around like so: Player:GetPlayers():filter(|p| p.AccountAge < 7):foreach(|p| p:Kick("not allowed yada yada"). This would get verbose fast using anonymous function, never mind annoying to write repetitively. This is just one use case though, but maybe it’s a dumb idea. ¯\_(ツ)_/¯

  2. As for static typing, that is actually in the pipeline! We technically support typed members right now, but obviously we do not perform type checking yet. I do agree with you there, dynamically typed languages do not scale well in a team setting, which is one of the goal Lunar aims to solve.

  3. We do break down classes into two separate tables, one that holds static members and another that holds instance members. Checking whether an instance is an instance of T is something I have been thinking about, but haven’t needed to tackle it… So I haven’t yet thought this through so I’m not sure about pros vs cons here… Ideally we do want to provide a method of asserting whether an instance is of type T at runtime, including polymorphism.

Keep in mind everything is not set in stone and my mind can be changed given constructive criticism. Hence I’m posting this despite it being very early, because I want Lunar to work with people, not against them.


#14

We’ve decided to keep a lot of the Lua syntax as-is since we want to allow people to be able to migrate their code at their own pace (such as moving their classes to our new class declaration), while still being able to run it all through Lunar. In Lunar, it is still easier to effectively do that than Lua itself through x += 1 compared to x = x + 1. Also, we cannot add an option to switch the syntax, here’s a few reasons why not:

  1. It’s hard work internally to make sure both options work at the same time. We already have a ton of tests for existing syntax, with more to come with new implementations.
  2. You’d lose the ability to properly use any tools we create for Lunar, such as language support for VSCode (soon™).
  3. This would harm the ability to run pure Lua, and we would like to avoid that to ensure Lunar stays as a superset.

We’ll try to add as much as we can that makes sense and helps people out that matches how Lua would maybe do things, but we also would like to implement things that make it easier to do something, even if to do so you have to use symbols (such as our lambdas).

As for types, one of the goal of Lunar is to add type inference, which means you don’t always need to declare it. There is however a few cases where you want types to be enforced at compile time, so that’s a pretty big necessity. Hopefully our implementation for types will actually help people who are unfamiliar with it, not run into much trouble with it, and maybe even be easier to work with!


#15

When you put it like that, lambda notation makes a bit more sense. Are there plans for having filtering tables and whatnot build in, or were those soley for example?

You might just be able to get away with something as simple as a IsA method or a ClassName property to check the type of created classes to be honest. Lua’s standard for OOP and Roblox’s especially essentially point to that solution.

@ChasingSpace You didn’t specifically address whether x++ or the sort would be added. Is that a no, then? I couldn’t tell if you were talking about changing the comment syntax or all of it.

I’ll see if I can convert a project over to Lunar and report back with how it performs. I’m personally excited for what the future may hold for it.


#16

Sorry, that response turned into multiple things. We won’t be implementing x++ or x-- because the latter interferes with the comment syntax, and we want to maintain the entire Lua 5.1 syntax, just with new additions on top of it. It wouldn’t make much sense (at least to me), to implement x++ but not x--

Lunar should support all Lua 5.1 syntax, so hopefully everything goes well with converting your project over!


#18

Well, it’s just an idea at this stage. Like I mentioned earlier, we want to increase productivity, so it could make it’s way in. We would have to solve a few problems with it after the foundation is done. Conditional extension methods like this requires type information which we don’t yet do.

Yeah, it could be as simple as IsA until you realize that requires a base class to inherit from. It’s a problem to tackle on later.


#19

This is very interesting… How can we make lunar code and then run it on Roblox? And does it actually compile everything to a new file or does it simply parse the contents of the file?


#20

Currently, it compiles source files to a new file that can be specified with a compiler config file .lunarconfig
example:

include = { "lunar", "spec" }
out_dir = "dist"

The plan is to bundle a roblox compiler with lunar that works similar to roblox-ts, and can be synced to roblox using rojo or other syncing plugins we may support in the future.


In other news, I just released a new import and export syntax!
You can now use

export class A end
export function B() end
export C = "Hello"

which should compile to

local A = {}
A.__index = {}
function A.new()
  return A.constructor(setmetatable({}, A))
end
function A.constructor(self)
  return self
end
local function B()
end
local C = "Hello"
return {
  A,
  B,
  C,
}

Along with

from 'path.to.my_module' import A as MyClass, B, C, * as MyModule

which should compile to

local MyModule = require('path.to.my_module')
local B = MyModule.B
local MyClass = MyModule.A
local C = MyModule.C

Note that this is still experimental and subject to change
I would not recommend using lunar in any major projects until we have published our first release. We plan on bootstrapping lunar after the initial release.


#21

Are you thinking of adding access modifiers to class attributes/methods (or wherever else it is applicable), like in java? It could be helpful on teams when, for example, you have to make sure a function is called or some restrictions are applied through a set method instead of directly assigning to the variable. It could be done with just the local keyword, and you could make up more specific keywords if you plan to add an inheritance feature.