Journey to developing a complete UI in JS
Hello,
I am mainly looking for discussing and critique about the path I am currently walking. Without further ado: I decided two weeks ago that I wanted to develop a new UI, but I wanted it to be a different journey and I wanted to have fun. I like javascript and I write React applications at work and in my private time and I like the way you 'develop' there. I thought it would be awesome to be able to write my own UI in javascript. Fast forward to today, I am capable of doing next (Might look weird, but this is basically ES6 + JSX syntax): https://gist.github.com/Nimaear/33cf...0cae3366e54f0a Which results in: Now this already looks like things are working, but I am really unhappy with how I translate the render method to a UI. Mainly the nesting of children doesn't really work like I want. Now my question here and mainly the thing I want to discuss is: How would you define a Component that can host nested Components in lua and what kind of structure would you put this in? My problem here is that I have taken maybe a too close approach to React and need to think out of the box. I have total control over how javascript gets translated to lua and would like some input here. p.s. I know it sounds a little vague, I appreciate the thoughts. |
And how, exactly, do you plan on using JS in WoW?
|
I am not planning, I am already doing it, but this part is not my problem as it already works: (I basically compile JS to lua). What I am having second thoughts about is how to build up the UI using components like React does (or something similar).
|
The problem with developing a cross-compiler is trying to support everything that can be done. In the end, you'll end up with a bloated mess that wastes a ton of resources and overall, contributes nothing. It may be a fun project to undertake to test your capabilities, but don't expect to be able to use this in the real world with anywhere near the same performance you would get writing natively in Lua.
|
Actually, the compiler itself is written in javascript and produces readable lua code. I had a very well performing ui written in lua before and this is actually at least as fast since the produced code resembles the final code very closely.
With this I can test different class implementations really easy by changing this in the compile step. My eventual goal is writing the same UI in a more readable way writing less code. I am 100% confident it will be as performant as anything out there if not even better since I can do some optimizations on compiler level automatically without my UI code becoming unreadable. My only problem right now is for loops concerning addressing lua tables (array indexes starting with 1 instead of 0 is the only thing that's hard to guess). I had a mindset of doing UI components like React does, but after playing another couple of hours with different implementations, I have the start of something that looks really promising. I might be nerding out about this, but I am going to continue on this path and publish a sample addon even if it's only for scientific purposes. |
Readable isn't the same as efficient. You can have readable code that nukes system resources. The fundamental problem is now you're running through two compilers/interpreters now. You're not running Java bytecode in a VM built in C. You're running a JS compiler/interpreter on top of Lua's equivalent of Java VM. Of course this is depending on how well the JS-to-Lua compiler is written and the code that is ingested, but there will always be overhead you will have to contend with.
|
Quote:
Now, to the OP I really don't know why would you do that to yourself, I mean I understand some transpilers like TypeScript, LESS, CoffeeScript, Babel and C# to JS, I completely understand why people would use these tools but JavaScript to Lua doesn't make sense to me at all outside to maybe research for few reasons that isn't really related to readability or efficiency, a JavaScript AST can be easily converted into Lua AST and you can actually produce a very efficient and readable Lua code but it also depends on the generator of the transpiler and optimizer if any. The issue I see with it is Lua is pretty simple language and there is many concepts in JavaScript that you can bring with you to Lua due to the nature of both languages being dynamic languages. I mean, the troubles that you need to go through to make things work doesn't worth it in my opinion and now you want to bring in React.js to WoW, a technology that aims to solve a specific problem with the DOM, a problem that doesn't really exist in WoW so why would you want to use it? however, to answer your question what you can do though is build yet another tool that trasnpiles React components into WoW's APIs... I don't think someone was that bored to create it yet. In short, I'll be honest with you and tell you, you should really, really, REALLY, did I said really? yes I did.. really abandon this idea and use technologies where they are better used in a context in which they solve a problem or were designed for it unless you want to find yourself solving problems for fun! that don't exist! like writing a tool that maps React components into WoW's APIs just because... I'm not saying it's bad but you really need to ask yourself the following question "Do I really want to build a UI for WoW or a technology that brings the web stack into WoW?" Anyway, good luck. :) |
Why I want to do this myself is pretty simple:
- I really like the declarative way of creating a UI. Lua usually ends up being unreadable and especially because there are so many different possible solutions to the same problem (take class based OOP for example), one persons code becomes hard to read to others. - I like coding with better tools and I want to switch out implementations easily without actually rewriting everything. This way I can 'compile' to a different class implementation without rewriting any of my code just to see the performance gain / loss. - Last of all, I really thought it was a fun project :O For clarity: I don't want to bring React to WoW at all. i just want to use the JSX syntax to create my 'components' because that really reads a lot easier. |
Lua reads pretty easily to me. /shrug No formal coding education here, just self-taught. I'm a music/math teacher. And I teach a coding class on Friday mornings where we do Lua.
But that's me. :) |
Quote:
JavaScript classes are just syntactical sugar, a mask to the prototypical inheritance that is the foundation of the language, whether you use "class" or a "function" to donate a class doesn't make it declarative so I really don't understand what declarative means, do you even refer to declarative programming? because that would be the wrong context to use it as it's not really dependent on the language you use. Quote:
Quote:
Quote:
|
Quote:
The comparison I'm making is like trying to run a Windows program through Wine on Linux versus running a native Linux version of the same program. |
I agree that it's the programmer's job to make code readable up to a certain point. You can write the most beautiful piece of art in assembly, but it's gonna be damn hard for anyone else to understand it right away. On the other hand of the spectrum, you could say : 'window with two buttons in the middle of the screen (which is probably possible to be deconstructed into an AST and finally converted to LUA)'.
I am trying to find a middle ground where it's gonna be easy for me (the developer) to write something that is fun to code, easy to read and easy to change implementation. I also know about the prototypical nature of both JS and LUA, but what I mean to say is this: When you are using a Class based approach to programming OOP, there's A LOT of different ways (both in LUA and JS) to achieve this. Of course you can be a purist and say: write it in the original language, it's good enough, but I can literally change all my code to use closure based class implementation instead of metatable based class implementation by changing one option in my compiler (This is not so easy in LUA since how you call instance methods will be different depending on your approach : class:method() vs class.method()) I know all of this all personal preference, but I wanted to share this weird journey either way. I would like to share the next piece of code (also available as gist for better readability: https://gist.github.com/Nimaear/33cf...0cae3366e54f0a) Maybe it's just me, but I really like how it reads compared to doing the same in LUA (even with a proper class implementation). I did find a way to get my original problem solved, so I am journeying towards creating my UI toolkit after which I will port my unitframes. Code:
import WindowWithTitle from 'UI/Window/WindowWithTitle'; Code:
do |
To me, personally, that Lua code looks like a mess. ;)
|
I feel sorry for whatever system that has to run that code. :p
In all seriousness. It's pretty much as bad as I described. Tons of bloat (memory/CPU usage) for only a frame with a couple buttons and text. I suspect that's just the beginning of it. We can't even see how __Modules is being generated either. The XML-based Widget API supports making virtual UI objects that act as classes would and can be used as templates by importing them. Defining these are an XML-exclusive feature, but we can easily do something similar by creating generator functions. |
My current project at work is all in React, and I agree that JSX is great ... for generating JavaScript applications.
I also agree with SDPhantom that that generated Lua code is massively cringe-worthy. It's bloated, convoluted, overly verbose, visually unappealing (those underscores everywhere!) and just generally hard to read and follow. Compare: lua Code:
Now there's no mystery variables, you're not creating a billion unnecessary tables and functions, and it's very clear to anyone who looks at it what's going on at any place in the code. |
Quote:
Some C++ compilers can transpile C++ into Assembly with a flag and the output is matched with a professional assembly programmer if not more at a time mostly because the optimizer can recognize patterns in the code and replace them with a faster implementation in the final output, there's no reason any other transpiler that takes language X and produces Y can't do the same regardless to the language but it also requires more research and analysis so I don't expect all compilers to have it, definitely not a transpiler of this kind. Just a note I'm not speaking about producing idiomatic Lua code but mostly on performance, the code can be terribly unreadable but performance can be great these aren't mutually exclusive because I totally agree that the output code he posted is ugly in many ways. Another good example is TypeScript, the compiler do a great job at producing both idiomatic and efficient JavaScript code but even then it isn't as readable as I would write myself. One of the things you learn in engineering is to measure, measure, measure and check your facts so maybe you're right but at the same time you likely to be wrong it's all up to the code and the benchmark god. :) Quote:
|
How pretty the produced LUA code looks depends completely what kind of 'class' implementation you choose and if you mimify the method names or not. I would also urge you to do performance and memory checking on the generated code before you judge it ;) My original goal wasn't for the LUA code to be readable at all. And again: if I had written the same thing in LUA (just think modules/packages (without polluting the global namespace), inheritance, class variables (not instance), super calls, method hooks and decorators,) the result would have been the same save the method names.
When it comes to space-time tradeoff of the generated LUA code, I chose time over space since I care about performance and the extra table entry per method to accomodate easy super calls is not that big of a price to pay. If you have concrete pointers to where I can improve the performance of the generated LUA code, I'd be delighted to hear them. On the other hand, Phanx's code is exactly what I meant: To visualize how the UI element is setup you are going through 32 lines of code just to realise it won't work (You can figure out why). Let's now say I want to split my window into two panels and all I had in the first one goes into second one and on the left one I want buttons. In Phanx's example, I'd have to go through the created ui elements that are a 'child' of my original one and change either the creation of all these elements or the parenting (SetParent) for each of the created UI element. In my original example, I would change nothing except the indenting after (I am referring to the JSX here, not LUA) Code:
<WindowWithTitle> How would you go around creating a UI component library/anything in LUA that allows you to do exactly this (with exactly I don't mean JSX markup, but the parenting/anchoring nightmare)? |
Quote:
Quote:
Quote:
Many of us here have already done research on the backend of Lua and know how everything runs on C-side. We know what to avoid to make sure our code is solid, clean, and smooth running. We aren't picking on your code just because we think it's ugly. We have years, some of us even decades of experience in Lua specifically that gives us insight on how the computer is going to approach it. If you're interested in trimming down your CPU usage, the most expensive Lua operations are function calls followed by table indexing. Indexing operations are what surprise a lot of people because of how much they can stack up. Accessing a global is an indexing operation on Lua's environment table. Metatables add at least two indexing operations each whenever they need to be accessed if __index is another table. If it's a function, then it's an indexing operation plus the function call. You should also keep an eye on memory usage. Garbage collection cycles are notorious for causing large amounts of framerate drops. Because of this, avoid creating dynamic tables and functions and releasing them very often. |
Quote:
Code:
{ Code:
{ Code:
How would you go around creating a UI component library/anything in LUA that allows you to do exactly this (with exactly I don't mean JSX markup, but the parenting/anchoring nightmare)? p.s. Just a minor note Lua isn't an acronym but a word so you actually need to write Lua instead of LUA. |
Something I just spotted, your backslashes aren't escaped.
Code:
prettyPrint("Components\Test.js:17", "You pressed ok") Code:
prettyPrint("Components\\Test.js:17", "You pressed ok") |
All times are GMT -6. The time now is 01:15 AM. |
vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI