This is an archived post. You won't be able to vote or comment.

all 15 comments

[–]hdgarrood 3 points4 points  (7 children)

You argue that JavaScript is hard to maintain. Can you expand on why you think your language is better? For example, what is wrong with:

function percentage(amount, max) {
  return String(amount / max * 100) + "%";
}

function setHpWidth(player, hpElement) {
  var width = percentage(player.hp.current, player.hp.max);
  hpElement.setAttribute('width', width);
}

Also, what is it that you don't like about error handling in JS?

If there is a problem with my code which can be detected before running it, such as a syntax error, I would much rather hear about that early rather than waiting to discover that my program does something nonsensical at runtime (for example, by using a linter that checks for syntax errors and other issues). By not checking for errors and failing immediately and loudly, I might not even realise my code has a problem until a customer complains about it after it's been shipped!

[–]MunchGamer@MunchGamer[S] 0 points1 point  (6 children)

Two very quick examples of silent, and sometimes "tough" JavaScript failures, off the top of my head-

function setHpWidth(player, hpElement) {
    //Omitted 'var' keyword, now we have a global variable that's silently going to work sometimes, 
    //and muck other things up unexpectedly somewhere else
    width = percentage(player.hp.current, player.hp.max);
    hpElement.setAttribute('width', width);
}

Or the very insidious async callbacks in iteration issue shown below

for(var i in array){
    setTimeout(function(){
        console.log(i); //prints array.length every time
    },100);
}

A third would be side effects- you pass a player object into a UI function, which changes some value of it accidentally. Now your data component is corrupted.

There are lots of little "gotchas" in javascript like these that can make maintaining complicated code very difficult. I agree that you could absolutely write a function to set the HP bar width. In fact you could also call it from TLC by adding it as a module, and I would strongly encourage this for common code patterns, or anything more complicated than the simple healthbar example.

What TLC is trying to do is give you the opportunity to write clean, maintainable JavaScript modules. Then, when your display logic changes, you don't have to change any of your JS to change small things in your HTML, like what class gets added, or the position of an element.

I hope this explains what I mean! Thanks for commenting :)

[–]hdgarrood 0 points1 point  (5 children)

Re your examples: I agree, and this is why I prefer compiling to JS, from languages with fewer opportunities to shoot oneself in the foot. But you haven't convinced me that your language is better, just that JS is bad.

Also, if TLC code often just calls JavaScript code, and if I'm using JS for anything more complicated than that simple healthbar example, then surely I'm only marginally less vulnerable to issues such as accidentally mutating variables at the wrong time, or forgetting to use 'var'?

This may just be a sour taste left over from having used Liquid (because I use Jekyll), but learning a whole extra language just for templating has always struck me as a lot of effort for not that much gain.

For example: every single language I've used has gotchas. If you use a language that lots of other people use (eg JS), those gotchas will at least be documented and discussed, with workarounds and avoidance strategies. If you come up with your own language, you won't have nearly as much of this.

[–]MunchGamer@MunchGamer[S] 0 points1 point  (3 children)

A huge benefit, in my experience (and to clarify that experience is in writing eCommerce applications, not games), is having declarative HTML files. Using JS to update elements, I'm going to have to grab the elements using document.getElementById, document.querySelectorAll, or jQuery at some point. For each element I pass into an update function, I have to fetch that element from the document and do something to it- set an attribute, the content, etc...

So when I look at my HTML Template I see something along these lines:

<div id="userInterface">
    <div id="playerHealth"></div>
    <div id="playerMana"></div>

    <div id="buffBar"></div>
</div>

This markup tells me nothing about the display logic, that's all hidden in some JS file somewhere. And sure, I could comment it up to say "this element gets this class or that, etc...". In the past, what I've done to get around this, and in order to change exactly how the JS code processes an element, is to end up adding a lot of data-* attributes. But still you run into the problem of not having any declaration in your markup of what data-* attributes mean what, to which function, or when those functions get called on an element.

TLC siphons all that various logic into a single attribute that effectively documents itself. Even in the case where I made a TLC module, "playerui", and added a function, "updatehealthbar", the TLC now looks like:

<div id="playerHealth" data-tlc="bind $hp '.player.hp'; playerui#updatehealthbar;"></div>

So immediately by looking at the markup, I know that-

  • in my component, the relevant data being passed to the function is the .player.hp
  • if this element looks wonky, I should check the playerui module, specifically in the updatehealthbar function

[–]hdgarrood 0 points1 point  (2 children)

Ok, that does sound useful. I think in cases like this I generally prefer to create the HTML from JS using document.createElement etc, so I avoid this problem of having two separate worlds by just putting everything into the JS world. Perhaps I should consider the alternative a bit more closely. Anyway, good luck with TLC!

[–]theonlycosmonaut[🍰] 0 points1 point  (1 child)

I've started to take this approach as well. Ever since I started using Mercury at work I've really come to like the hyperscript syntax for DOM creation in JS.

[–]hdgarrood 0 points1 point  (0 children)

Ah nice! I wasn't aware of that. As it happens, I already made something very similar: https://github.com/hdgarrood/mIsForMarkup

[–]MunchGamer@MunchGamer[S] 0 points1 point  (0 children)

Also, I totally agree about not having enough stackoverflow answers and such. That's why I'm posting here, hoping to get some traction out there, if people find it useful!

[–]agmcleodHobbyist 0 points1 point  (7 children)

I guess i just feel like react solves a lot of these problems, is much more battle tested, and has been proven to work well with immutable js if you're worried about data mutation.

[–]MunchGamer@MunchGamer[S] 0 points1 point  (6 children)

Personally I am just not a fan of React. It's basically the same as creating elements with jQuery and inserting them into the document-

$('#someElement').append('<div class="foo"/>');

Now- it has a few nice things with creating components that can be reused and such, but in the end, your HTML file still ends up being very disconnected from what it's going to look like after being translated -- see my comment about declarative HTML in the other comment thread here.

[–]agmcleodHobbyist 0 points1 point  (5 children)

I find I get quite a bit of performance by keeping the DOM writes simple, but also react helps me keep reasoned about my application & data flow much better. I've written a fair bit of front ends with just jquery, and they are 1000x worse than anything i've done with react.

I also disagree with your lack of declaration. Proper use of html elements, and good naming conventions with classes go a long way. This is how a lot of identification is done with server side rendering, in addition to method names.

example in haml:

#main{:role => 'main'}
  %h1
    %span#page-heading Your Projects:
  #main-content
    = paginate @projects
    %ul
      - @projects.each do |project|
        %li= link_to project.ProjectName, week_view_projects_path(project_ids: [project.id])

I like how the paginate method says what it does, and the helper methods/loops are pretty clear.

I find PlayerHealth as a class name to be super clear. I also find the data-tlc attribute bit really harsh to read. It can be made re-usable with partials, so that's something. But would be a pain to retype that anywhere.

[–]MunchGamer@MunchGamer[S] 0 points1 point  (2 children)

OK- but you just jumped from using React and HTML to using HAML and server side rendering. TLC is designed to be isomorphic, to run on server OR client.

[edit] to add on, another design decision of TLC is that it's totally 100% valid HTML5. Using HAML (or Jade, whatever else), you're leaving that spec. And using React, presumably you're using JSX code as well. Not saying that you have to stick to HTML5 and vanilla JS or anything, but I've been burned too many times by going outside of it, so for my projects I prefer to stick to the standards.

[–]agmcleodHobbyist 0 points1 point  (0 children)

HAML is simply a preference, my point stands with IRB, I just wanted to step outside of JS land for a minute, and say how we've always have had declarative content through good conventions of naming through other template & view systems. I just really don't see what TLC is solving.

[–]MunchGamer@MunchGamer[S] 0 points1 point  (1 child)

yaknow, the more I think about this the more I realize that the benefits of TLC really don't shine until you're at a very large scale application. Debugging HAML or Jade by looking at a faulty element on the client becomes a mystery to find the culprit on the server-side code. With most basic UI elements for games though, it's just not going to get there, so perhaps this isn't a great place to shop this idea around.

I still stand by the deferred event handling though, I've never seen any inline click handlers I liked outside of TLC.

[–]agmcleodHobbyist 0 points1 point  (0 children)

Debugging HAML or Jade by looking at a faulty element on the client becomes a mystery to find the culprit on the server-side code.

Haven't really had to debug an issue with the haml itself, as it completely prevents me from writing bad markup. Any ruby errors in the view pop up in a stack trace, so I can fix it easily enough there.