This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
SquidDev's profile picture

Tua - Feedback on syntax

Started by SquidDev, 30 May 2015 - 08:13 PM
SquidDev #1
Posted 30 May 2015 - 10:13 PM
A long time ago Dan posted a tweet describing a language called Tua. This is effectively a type-hinting sytem for Lua to enable better compile-time verification of Lua source. However nothing came out of this, and I thought it would be an interesting project to work on - hopefully enabling better development tools such as intellisense.

So I took this specification, fleshed it out and added some more features. It is partially based on TypeScript which is a similar project for Javascript. If you guys can have a read of the spec and give me some feedback on things that you think would look better then say! I guess I'm looking for feedback.

Thanks,

TLDR; Read this. Opinions?
Edited on 30 May 2015 - 08:14 PM
Creator #2
Posted 30 May 2015 - 10:17 PM
I actually just looked at the documentation, 5 mins ago. What a coincidence.

How would Lua benefit from this?
Lignum #3
Posted 30 May 2015 - 11:59 PM
How would Lua benefit from this?

Type safety. You can't accidentally assign a table to a number. Additionally, it seems that you can even define your own "interfaces" which allow you to make your own custom "object types" by defining what fields the table should contain. So even if you're trying to assign a table to a table, which is perfectly valid normally, if they have varying interfaces, it's not going to happen. All of this can help with finding bugs.
Creator #4
Posted 31 May 2015 - 12:02 AM
Like custom data types?
Lignum #5
Posted 31 May 2015 - 12:03 AM
Like custom data types?

Pretty much, yes.
Bomb Bloke #6
Posted 31 May 2015 - 12:52 AM
Erm, your "multiple returns" example doesn't appear to return anything?

In my view, compile-time testing of types isn't much use when it's already faster to just check it at run-time. If you got a type wrong, the script'll crash and tell you all about it. There are a few exceptions to this - for example, indexing into a string won't generate a specific error - but I doubt such mistakes are common. Likewise, if you can't tell what a variable is at a glance after a week or three away from your code, then you probably named it wrong.

(Some - eg Dan - like to prefix their variable names with a character indicating their type. Personally I think even that's overkill, but then, I used to use terms like "spoon", "nospoon", "thingy"…)

To me, the main benefit of static typing is speed, but you won't get that here because at the end of the day Lua is still going to offer dynamic types. Being able to set (and change!) types without having to jump through hoops is, to me, a great feature - artificially hindering that ease-of-use seems counter intuitive.
oeed #7
Posted 31 May 2015 - 01:20 AM
To be honest I'd rather a more Lua-like type declaration.

Something like:


local number a = 1
local boolean b = true
local string c = "hello"

And rather than having to use :auto I'd just leave it blank, so it functions as it currently does.


local a = 1 -- becomes a number
local b = true -- becomes a boolean
local c = "hello" -- becomes a string


A week or two ago I started looking in to an Object-Oriented Lua a bit like this, if you added it I'd use it in a heart beat.


object View inherits Object

  property string Text = ""

  function Initialise()
	super:DoSomething()
  end

  -- called when obj.Text is set
  private function __setText(text)
	self.Text = text
  end

  -- called when obj.Text is run
  private function __getText()
	return _Text
  end

  static function AStaticOne()
	self:AnotherStaticFunction()
  end

  private function OnDraw(x, y)
	super:OnDraw(x, y)
  end

end
Edited on 30 May 2015 - 11:28 PM
SquidDev #8
Posted 31 May 2015 - 08:51 AM
Erm, your "multiple returns" example doesn't appear to return anything?

To me, the main benefit of static typing is speed, but you won't get that here because at the end of the day Lua is still going to offer dynamic types. Being able to set (and change!) types without having to jump through hoops is, to me, a great feature - artificially hindering that ease-of-use seems counter intuitive.
You are right about the multi returns thing. Woops.

The aim isn't to speed the code up - obviously this won't have an effect at all. Nor is it to force users to use type checking - users can opt in and out of it. Partially this is just a fun project and partially this is a an attempt to get basic source analysis working before I start trying to infer types. One thing I think would be pretty cool (though not useful for most people) would be auto complete. Static types make this much easier. Your point about people knowing their way around the source base is valid - however we all make mistakes occasionally and several bugs that have taken a while to manifest themselves are because me calling methods with the wrong values.


local number a = 1
local boolean b = true
local string c = "hello"
I've considered it several times. I chose the colon syntax because it is slightly easier to parse I don't to check if there is another identifier afterwards and so change an identifier into a type definition and it also doesn't cover a function syntax very well. I might write have a go at this afterwards as most of the codebase will be shared.

And rather than having to use :auto I'd just leave it blank, so it functions as it currently does.
This is also a good point and what TypeScript does. I'd like to keep it backwards compatible but might add a flag to enable this.


A week or two ago I started looking in to an Object-Oriented Lua a bit like this, if you added it I'd use it in a heart beat.
Now we're talking! The end game for this project is OOP. I wrote some basic system which does actually compile and looks like this:

class Foo extends Bar has
  local Text = ""

  function new(whatever)
     self.thing = whatever
  end

end

a = Foo("things")
This is really primitive and will be fleshed out a lot. Static methods I will implement though I don't know about privates.
dan200 #9
Posted 31 May 2015 - 10:06 AM
Ooh, I forgot I experimented with this. My implementation got as far as implementing a limited subset of Lua (no metatables, i'm not sure how to reconcile those with strong typing), with the type checking rules applied. If I was to resume work on it now, i'd probably implement it as a typescript style cross-compiler: even if all it did was automatically add runtime checks, it'd be a big boon to lua development). Awesome to see someone else doing some work in this area!
ElvishJerricco #10
Posted 31 May 2015 - 10:11 PM
Now we're talking! The end game for this project is OOP. I wrote some basic system which does actually compile and looks like this:

class Foo extends Bar has
  local Text = ""

  function new(whatever)
	 self.thing = whatever
  end

end

a = Foo("things")
This is really primitive and will be fleshed out a lot. Static methods I will implement though I don't know about privates.

I've got a lot of experience with writing / implementing languages, and LuaLua has a pretty nice OOP system. Frankly, this could make LuaLua obsolete, which would be nice considering how ugly the language can be. So I can help you out with OOP.
oeed #11
Posted 31 May 2015 - 11:25 PM
Now we're talking! The end game for this project is OOP. I wrote some basic system which does actually compile and looks like this:

class Foo extends Bar has
  local Text = ""

  function new(whatever)
	 self.thing = whatever
  end

end

a = Foo("things")
This is really primitive and will be fleshed out a lot. Static methods I will implement though I don't know about privates.

I've got a lot of experience with writing / implementing languages, and LuaLua has a pretty nice OOP system. Frankly, this could make LuaLua obsolete, which would be nice considering how ugly the language can be. So I can help you out with OOP.
Yeah, I took a look at LuaLua, but the syntax just wasn't for me.

Provided that the syntax of this is actually like Lua, as in using keywords/tokens rather than square brackets, at symbols, colons, etc, I can see it being absolutely awesome.


Oh and one thing SquidDev, that does looks super neat, although Objective-C like properties are such a must-have. Essentially, that act like values, so, for example:

a = Foo("things")
a.Name = "oeed"
print(a.Name)

This, if implemented, calls functions on set (which is what I was getting at with __setText earlier, although I'm not sure about that naming) and get allowing you to manipulate them and do things on change. Objective-C requires an instance variable, or in this case a local variable, to do it. But if there were a way to do without that, essentially detecting if self.Text = was called within the setText method itself, it would be even better.
Edited on 31 May 2015 - 09:31 PM
SquidDev #12
Posted 01 June 2015 - 08:00 AM
I've updated the spec slightly - all I've done is merged alias and interface and renamed tiny to strict. Oh, and fixed the varargs example.

I've got a lot of experience with writing / implementing languages, and LuaLua has a pretty nice OOP system. Frankly, this could make LuaLua obsolete, which would be nice considering how ugly the language can be. So I can help you out with OOP.
I'd love help from anyone! Currently I'm working on cleaning up the Metalua compiler a bit and writing parsers for various syntaxes. Then I guess I'll have to move on to analysers (type inference, type checking, optimisations?) and code generation. Thanks to Metalua most code generation is complete but the Tua classes will require some thought out design - currently it just wraps it in a setmetatable call. Had another look at LuaLua (Lua^2 maybe?) and the OOP does look good. The syntax is a bit weird though. :)/>

Provided that the syntax of this is actually like Lua, as in using keywords/tokens rather than square brackets, at symbols, colons, etc, I can see it being absolutely awesome.

Oh and one thing SquidDev, that does looks super neat, although Objective-C like properties are such a must-have. Essentially, that act like values, so, for example:

I'm trying to use keywords as much possible however I kinda want to reduce 'keyword' noise - so there aren't so many new keywords it is impossible to name a variable anything (think variables called klass in Java). I do want to distinguish between properties and fields, I do like C#'s way of doing it:


int Foo { get; private set; } // Implicit property which can only be set inside the class. Generates a backing field called _Foo automagically.

private int bar;
int Bar {
  get { return bar; }
  set {
	 Console.WriteLine("Setting bar to " + value)
	 bar = value;
  }
}

I do prefer it to __setText however I'm not sure how that would work in a Lua style syntax. . An alternative would something like Java Beans' property - rewriting access to the field with get/set methods instead - though this wouldn't work if using Tua code outside Tua.

Also metatables/metamethods. Really not sure how to handle them. And Dan replied: wow! Thanks so much for you guys enthusiasm about this.
Edited on 01 June 2015 - 06:08 AM
oeed #13
Posted 01 June 2015 - 09:38 AM
Provided that the syntax of this is actually like Lua, as in using keywords/tokens rather than square brackets, at symbols, colons, etc, I can see it being absolutely awesome.

Oh and one thing SquidDev, that does looks super neat, although Objective-C like properties are such a must-have. Essentially, that act like values, so, for example:

I'm trying to use keywords as much possible however I kinda want to reduce 'keyword' noise - so there aren't so many new keywords it is impossible to name a variable anything (think variables called klass in Java). I do want to distinguish between properties and fields, I do like C#'s way of doing it:


int Foo { get; private set; } // Implicit property which can only be set inside the class. Generates a backing field called _Foo automagically.

private int bar;
int Bar {
  get { return bar; }
  set {
	 Console.WriteLine("Setting bar to " + value)
	 bar = value;
  }
}

I do prefer it to __setText however I'm not sure how that would work in a Lua style syntax. . An alternative would something like Java Beans' property - rewriting access to the field with get/set methods instead - though this wouldn't work if using Tua code outside Tua.

Also metatables/metamethods. Really not sure how to handle them. And Dan replied: wow! Thanks so much for you guys enthusiasm about this.

Yeah that's not bad, although as you said, I'm not so sure if it'd work with Lua, it'd possibly be a bit harder to implement too. At least based on the tiny research I've just done, it appears that JavaBeans requires you do only use functions for properties, or am I incorrect?

I think the most important aspect is to make it optional and also behave like a regular property.


Edit:

I was feeling inspired, so I decided to create a fully functional getter and setter OOP example. I'm now really wishing I used this in Bedrock, would've made it sooo much easier.

You can take a look here, I've commented the crap out of it, but I'm happy to explain anything. The actual usage is about line 180. It's basically just showing how I think it should work and want it to work, and also showing it's quite possible. Now, this code mightn't apply in the slightest when actually making the language, although I don't have a clue as I've no idea how that happens, but it might be helpful.

Here's a quick snippet:


Person = {
  name = "Default Name", -- doesn't actually have to be set, but this acts as a default'

  -- again, not sure about the naming convention here. the __ looked ugly really, although it did kinda make sense.
  setName = function(self, name)
  -- for example, let's capitalise the name
  -- I'm not sure if returning or setting the name is better than this though (see the comment in __newindex)
	self.name = name:sub(1, 1):upper() .. name:sub(2, -1)
  end,

  getName = function(self)
  -- and then tack an underscore on the front for the sake of it
	if self.name and #self.name > 0 then
	  return "_" .. self.name
	end
  end
}

local oeed = Person:new()
oeed.name = "oeed"
print(oeed.name) -- prints _Oeed

There were a few design choices I was unsure about, but now that I've spent a while making examples I'm a bit more confident with; open to changes though. I think the setName and getName style works well, it's not ugly unlike some of the others, it's really clear what it does and is also really easy to get the hang of. I chose to make it capitalise the first letter (so .name becomes .setName), sure this does allow for collisions, but who's using .name and .Name?

One thing I did have in Bedrock was detecting when any value changed, I've also added this in. Basically there is a 'global' getter and 'global' setter. It's always called first before the specific one, it's optional naturally. If you return false as the first return value it will run the specific setter, true and it won't. This is a lot more useful than it might seem, most notably in the GUI element example, when you change ANY value (such as the text, background colour, etc) it lets you know and you can then invoke a draw. This means no polling or having to call a draw function!

So yeah, I think it's pretty sweet to be honest. One notably useful example I realised with the GUI stuff, with the American spelling you simply make a setter and getter for them and redirect it to the British version (or vice-versa if you're so inclined). Again, I would've loved to have used this in Bedrock. Although I've been taking a break from CC, if this language does get made I'm totally making another GUI framework (hopefully Dan's gfx API is real to!!!). I've already got a name for it :P/>
Edited on 01 June 2015 - 11:40 AM
ElvishJerricco #14
Posted 01 June 2015 - 08:02 PM
Check out here for how LuaLua implements OOP. The idea is that you have an instantiator function to create objects, one unique to each class. That function's environment is set to the object's method table, and that method table has its index set to the super class's method table. The object and the super methods table are passed to the instantiator, providing all instance methods with upvalues for self and super (and when I need to update to Lua 5.2, I'll need to pass an _ENV upvalue as well, instead of using setfenv).

So you can sorta see a class declaration and instantiation as something like this:


function instantiator(self, super)
	function myMethod()
	end
end

obj = {}
methods = setmetatable({}, {__index=supermethods})
setfenv(instantiator, methods)
instantiator(obj, supermethods)

Where supermethods is defined recursively in the exact same way.

As for how properties are implemented, it's kinda complicated when examined in detail. But essentially, an object will define properties by adding an entry to its properties table. That table has a set of property names as keys, with tables holding the setter and getter as the value. Then, when the object is indexed, it sees if the property table has the index key as a property, and calls that getter. Same for the setter. Very clean implementation.

Also, the class hierarchy is kind of hilarious. It's copied from Objective-C. Classes are objects (so they can have static methods, and the |Class new| method), so they need a class to be instances of.

So each class has to have both a superclass, and a class that it is an instance of (its metaclass). This is even true for the metaclasses themselves. Each metaclass is a subclass of its main class's superclass's metaclass. And each metaclass is and an instance of the root metaclass. Each class is a subclass of the root class (LuaObject, NSObject), except for the root class itself (the only class without a superclass). Which means the root metaclass is a subclass of LuaObject. While LuaObject is an instance of the root metaclass.


+--------- Subclass of ---------+
|                               |
V                               |
LuaObject -- Instance of ---> root metaclass <------+---------------+
    ^                           ^                   |               |
    |                           |                   |               |
    Subclass of                 Subclass of         Instance of     |
    |                           |                   |               |
    |                           |                   |               |
SomeClass -- Instance of ---> SomeClass_Metaclass --+               |
    ^                           ^                                   Instance of
    |                           |                                   |
    Subclass of                 Subclass of                         |
    |                           |                                   |
    |                           |                                   |
SomeClass -- Instance of ---> SomeClass_Metaclass ------------------+
Edited on 01 June 2015 - 06:04 PM
CrazedProgrammer #15
Posted 01 June 2015 - 08:23 PM
It would be cool if someone created a programming language that compiles into Lua 5.1 bytecode.
SquidDev #16
Posted 01 June 2015 - 09:12 PM
-snip-
I kinda want to avoid using environments to emulate self.* access. If anything I'll write a visitor that rewrites function and variable access to to use self.*. Not sure if I want to follow the idea of a 'base class' for all classes - I never really see the point of them. However the property implementation is nice. ATM classes are a low priority currently as I want to get some of the core working but I do want to move on to it ASAP, pull requests are always welcome though you obviously may want to wait a week or so.

For those who are interested the current class implementation is here (usage here). It implements static and local members - with locals being rewritten into the constructor which means you don't have the issue with python where tables were shared between classes. It is a kinda bad implementation so it will need a total rewrite.

It would be cool if someone created a programming language that compiles into Lua 5.1 bytecode.
This does compile into both Lua and Lua bytecode, though bytecode compilation is much slower. I kinda want to experiment compiling to JS (or maybe even some sort of VM bytecode - read Java or MSIL) as well just for fun - but as you can probably guess this isn't a priority. I think LuaLua also compiles to bytecode as does JVML-JIT.
Edited on 02 June 2015 - 10:40 AM