JavaScript for Nexus Newbies Part 3: The Matrix Refactored

edited April 2016 in Client Help
Part 1: http://forums.achaea.com/discussion/4584/javascript-for-nexus-newbies-part-1
Part 2: http://forums.achaea.com/discussion/4596/javascript-for-nexus-newbies-part-2-my-first-not-so-simple-alias

Today, we're going to take the Oocly package we made last week and we're going to rearrange things. We're not going to add any new functionality - instead we're going to focus on organising it into a more sensible form (programmers call this refactoring).

We're going to learn a basic framework for making packages that will make them significantly easier to write, modify, etc. Thus far we've been in that weird phase of learning where what we're doing technically works, but isn't actually how "the pros" do it. After this week, your packages will look basically like my packages. Edit: After the beginning of next week. We have one more thing to cover.

This is going to be a hard week. Take it step by step. This is where we hit the stuff that's going to be a little more tough to understand. Take it step by step.

Once we've gotten our hands dirty with all of this, we'll be well-positioned to get our hands filthy next week on a more substantive problem involving GMCP and some actual combat utility.

Before we start, make sure you've finished Part 2. Everything we're going to do today uses Part 2. You should have three aliases done for OOC tells, OOC replies, and OOC retells.

Quick Tip

You may not know this, but your browser probably has a way for you to just type in JavaScript and see what happens without having to make and run aliases or whatever. This can be really useful for testing basic things about JavaScript that you're not sure about.

In Chrome, ctrl+shift+j will open the console (you may have to click the Console tab at the top) and then you can just type things in and it'll act just like they were part of the page's JavaScript. Type 5+5, hit enter, and you'll get 10. If you want to print something to the console, just pass a string to the console.log function just like we did with send_command! Handy!

Variables

We're going to start today by talking about variables.

There are a lot of analogies for variables. None of them are perfect. We'll start ours a little lower than usual though and I think that might make it a little less imperfect.

First, we need to talk about memory. When we talk about memory, we're not talking about your hard drive, we're talking about your RAM. Your computer's memory is a lot like a post office with a million little slots (actually, it has about a billion little slots per gigabyte of RAM in your computer!).

Every one of those slots has an address, exactly like a post office box. If you want to get at the stuff in that box, you tell the postal worker that address. If you're going to need to get at the stuff in the box later, you need to remember that address (How do you remember the address? Why you put it in a post office box of course!).

But we have some problems. All the boxes are pretty small and some of our stuff is going to take up more than one box. We really don't want to deal with that. We also don't want to have to type or remember the addresses since they're really long and not very meaningful. We don't want to deal with phone numbers, we want our phone to have a named contact so we never have to think about whatever the random number the phone actually uses to call them is.

That's where the concept of a variable comes in. When we use a variable, we'll ask the post office to store something for us, tell it the name we want to use, and they can get it for us if we give them that name.

The way we tell the post office we want them to get ready to store something is by declaring a variable. Declaring a variable is like signing up for this storage service and the only thing we're required to put in the form in JavaScript is the label we're going to use for the thing we're storing. We do this in JavaScript with let and it looks like this:
let my_first_variable;
The main restriction is that our labels can't have spaces in them. You can write it like myFirstVariable instead of my_first_variable (or almost any other way you want that doesn't use spaces), but almost everyone agrees the underscore is easier to read. Variable names shouldn't be capitalised - you can technically capitalise them, but every programmer reading your code will be confused because capitalised names are conventionally used for only one very special kind of variable that we'll probably never talk about.

We can do a few at a time too, we just stick commas between them:
let my_first_variable, my_second_variable;
If you're following along in the console right now and you did both of these, JavaScript probably won't complain at you. It should. When you go to do the second line after the first, it should tell you "hey, you already told me about the my_first_variable label, you can't sign up with the same name twice!". Unfortunately right now it just silently ignored that attempt to redeclare my_first_variable. One of the reasons we're reorganising things is that it's going to let us turn on a feature in JavaScript that makes it tell us when we're doing things wrong instead of silently pretending like we didn't do them (we'll see how to turn this on at the beginning of next week).

Once we've got our variable declared, we can tell the post office what to store for us under that label. We do this with a normal equals sign. Let's store the number 5 under the label my_first_variable and the string "say Hello!" in my_second_variable.
my_first_variable = 5;
my_second_variable = "say Hello!";
We can change what we've got stored too. Let's add 1 to our 5:
my_first_variable = my_first_variable + 1;
That says: I want to store something under the label my_first_variable and the thing I want to store is whatever was in my_first_variable + 1. We only declare a variable once, but we can assign to it like this as many times as we want. This particular assignment - taking the value of a variable, adding something to it, and then storing that in the same spot under the same label is a pretty common thing to do, so JavaScript actually has a shortcut to save you some typing (there are a few similar shortcuts like this for the other math operators):
my_first_variable += 1;
That means the same thing as my_first_variable = my_first_variable + 1;

Before we continue, let's get one thing straight: those words are instructions, they aren't the variable. If you run your script, then delete that line, that doesn't undo what you've done. You're handing the post office worker a scrap of paper that says what the post office worker should do. Tearing up the piece of paper after you've given it to the post office worker doesn't tear up the thing you had the post office worker do. That line of text in your script is not your variable, it's just an instruction for what to do with your variable - the actual variable is invisible and lives inside your computer and the only way you can interact with it is to ask the post office worker to do something with it for you. Don't get confused and confuse a recipe for an apple pie with the actual apple pie it makes.

But we can store more fancy stuff too. Remember our function send_command? Well "send_command" isn't actually the function. The function itself is a list of things to do. "send_command" is just the label on that function that lets us ask the post office worker to go get it for us. Let's ask the post office worker to go get that function for us and to store it under a new label. To save ourself some typing, we can actually do the first assignment to a variable at the same time as we declare it (this is called initialising a variable):
let my_third_variable = send_command;
Now the function that was labelled "send_command" is also labelled "my_third_variable". Don't get confused here, it's not a copy of the function, it's the same function! We just asked them to put a second label on the boxes where the function is stored.

And to show that there's no magic here, let's use this new variable just like we used send_command, and we'll use our other variable just like we've been using a regular string:
let my_third_variable(my_second_variable, true);
This is exactly the same thing as send_command("say Hello!", true);

Remember that we only ever use let once to declare things. A common beginner mistake is confusing initialising variables and assigning to them. When we assign to them, we just use the variable name and an equals sign. We only use let when we want to declare a variable for the first time (and initialising is just a shortcut for declaring and then immediately doing the first assignment).

Make sure this all makes sense before moving on.

Some Complications

Don't worry too much about this, but I want to forewarn you about some things you might run into in other people's code.

We're using let here. let is relatively new to JavaScript and a lot of older code will have var in it instead. var does some really strange things and let is better and more intuitive in every way. There is absolutely no reason in your scripting to use var. Just don't do it. If you see a var in someone else's code, just think of it as let. If it doesn't make sense, either just accept it and move on or google the horrifying truth about how var actually works and pity the poor souls who wrote code before you.

You might also see variables declared with const. const is also new and it means the same thing as let except you're only allowed to assign to it one time. It's a constant. So you might do something like const PI = 3.14; because you know pi will never change. If you declare something with const and try to change it, Nexus will yell and you because you told it you weren't going to change it and then you tried to change it. This is yet another safety you can choose to use to protect yourself from yourself. It's a good habit to get into. Due to some old conventions, many people write the names of constants in all caps so they can remember they're constants, though you don't have to. If you get deeper into programming, you'll discover a whole world of people who think you should only ever use constants (never normal variables) called functional programmers (spoiler alert: these people are absolutely right - but you'll be writing simple enough things in Nexus that you don't need to worry about it).

Cleaning up Oocly Step 1

Let's use variables to make our Oocly functions more readable.

The ideal situation is to have each line of our program do one thing. That makes it a lot easier to understand what the hell is going on when we come back to this a year from now or send it to someone else.

Our OOC tell looks like this right now:
send_command("tell " + args[1] + " *achaean OOC: " + args[2][0].toUpperCase() + args[2].slice(1), true);
We're doing a lot here: we get the first letter of our message, we uppercase it, we slice the rest of the message, we glue those together, we get the recipient, and we glue it and some other stuff on. A capable JavaScript programmer can pretty easily tell what's going on here, but it's still a lot for one line. It would be nice to split it up a little:
let recipient = args[1]; // Now we don't have to remember that args[1] is the recipient, we can just use a name that makes sense and remember that instead
let message = args[2]; // Same for the message
let first_letter_of_message = message[0];
first_letter_of_message = first_letter_of_message.toUpperCase(); // There's no let here because we're just assigning, we already declared our variable (when we initialised it in the last line)
let rest_of_mesage = message.slice(1);
message = first_letter_of_message + rest_of_message; // These variables are strings, so the + between them concatenates them into one string
let to_send = "tell " + recipient + " *achaean OOC: " + message;
send_command(to_send, true);
Make sure you understand this.

That's a little extreme, but it shows how we can use variables to break things up. How much you want to break things up like this is up to you, but doing this lets us focus on doing one thing at a time. When we designed this script, we broke our problem down into progressively smaller pieces: first we figured out how to send a string, then we figured out how to glue our little strings together into the whole string, then we figured out how to split off the first letter of something, then we learned to uppercase it, then we learned to split the rest of the letters off, etc. Using variables like this lets us write our code (and read our code) in a way that reflects that.

Don't fall into the trap of thinking that putting it all on one line is better. This is the classic newbie mistake. You might worry that all those extra variables are a "waste", but that's like Bill Gates worrying that he's wasting pennies. It isn't worth his time to avoid wasting pennies. It isn't worth your time to avoid making "extra" variables. You might think you're gaining efficiency, but the comically tiny gain in efficiency will never amount to even a single second spent looking at your code all crammed into one line and trying to understand what it's doing.

You're also likely to see another, similar style to this where all of the variables for some function or script get declared together at the top instead of declaring them just before you actually need them. Our code would look like this:
let recipient, message, first_letter_of_message, rest_of_message, to_send;
recipient = args[1];
message = args[2];
first_letter_of_message = message[0];
first_letter_of_message = first_letter_of_message.toUpperCase();
rest_of_mesage = message.slice(1);
message = first_letter_of_message + rest_of_message;
to_send = "tell " + recipient + " *achaean OOC: " + message;
send_command(to_send, true);
I'll leave it up to you which style you think is easier to read. Sometimes people do a combination where they declare and initialise all their variables at the beginning, but always before doing anything with them. Any of these are totally valid options.

First Exercise

Do this same thing for OOC reply and OOC retell. Make them as easy to understand as you possibly can. Add some comments too - put them on any line where you think that six months from now you might not remember how it works.

Comments

  • Correction:

    Near the end of the first section of Variables, there is this line:
    let my_third_variable(my_second_variable, true);
    It should say:
    my_third_variable(my_second_variable, true);
    Sorry for the confusion.
  • Thanks so much for that explanation, I'd been wondering wtf the Nexus "functions" are for. (Worst. Name. Ever.) It took me like an hour to figure out how to use them. Good to know that there's a way I can ignore them entirely.
  • Just a note for anyone following these - I'm swamped right now and haven't had much time for Achaea, but I'm still intending to finish them and put them up on the Nexus wiki. Sorry for the long delay!
  • Thanks a lot for all the effort on these.  I program for a living but these tutorials really help one get familiarized with the environment in Nexus.
  • Tael said:
    Just a note for anyone following these - I'm swamped right now and haven't had much time for Achaea, but I'm still intending to finish them and put them up on the Nexus wiki. 
    Excellent!
Sign In or Register to comment.