G#

G# is our flagship language. It takes center stage in the upcoming A Major framework centered around fluidly developing robotics routines. In his senior year in university, Danny was searching for a project worthy of all the skills he’d gained during his time as a Computer Science student. He’d always had an interest in designing his own language, but what he anticipated to be a toy language to be a proof-of-concept rapidly expanded to be the foundation for a full engineering suite.

Inspiration for this framework struck one day from his hobby of studying music theory. The A Major scale has exactly three sharps: C#, F#, and G#. C# and F# are already popular .NET languages. From this realization, it was a simple matter to fill the gap and create a union. It was an interesting concept, but how useful could it actually be? The framework takes further inspiration from the music scale in that in the scale, G# is known as the leading tone. To mirror that, the framework would emulate that same quality in the new language. How would G# stand out from its brethren?

Programming languages don’t typically exist for no reason. True, many developers work on compilers as a hobby and purely for fun, but the languages that really establish a footing in the development landscape do so because they are able to contribute something that was previously lacking. They’re able to prove they do their job efficiently and with enough thoughtful design as to attract developers to its use. With that in mind, it was yet again another simple leap to ask why C# and F# were both hosted in the .NET ecosystem? What were they providing? To keep the answers brief, each focused on a different paradigm. C# excels at object oriented solutions while F# is essentially a re-imagining of OCAML and therefore focuses on functional solutions.

It was clear G# had to bring something new to the table, something that made it worthy of this leading quality. Prior to all of this coming about, he languished over what his senior project should focus on. His other passion besides language design is robotics, so a project showing off a couple gadgets that could communicate and interact in some way would’ve been equally stimulating and enjoyable. Now that this new language needed a unique contribution, the choice was easy and the languishing had ended. There was no need to choose between the two. G# would focus on its own paradigm. It would work in a rules based environment that would make it very well suited for robotics routines. This brought on the new challenges of now communicating with external hardware, but the mind was racing with ideas.

As expected, it was a lot to take on for a semester’s worth of capstone work. Regardless, the final presentation was rather decent. It was proven the syntax was feasible and it could produce the right assembly code for the target platform. It would be general enough to be used in a console application but still make it known its intent was to eventually produce a binary for an Arduino board. Since then, plenty of refinements have been made to the syntax and the implementation of the compiler including one major overhaul. A Major, the name of the scale, still houses the trio in the form of an IDE. A modular-component kit will be created for easy construction of various designs. The IDE will automatically scan and create an object model of the schematic for fluid consumption in the code. This has been a summary to get an understanding of G#/A Major’s goal. A more thorough guide of how this all came to be can be read in our blog posts. Now, for a proper introduction:

Design

G#’s guiding principle is it should handle as much as possible so you, the maker, may focus entirely on the logic of your routines. It’s also designed to be picked up easily by beginning programmers. How that was done is tougher to appreciate if you have no prior programming experience or research, but this is the gist of it:

  • Certain terms were renamed for faster understanding and retention. For example, the string data type (a data type specifies what a variable can hold; and for that matter, a variable can be thought of as a box that contains a specific type of item) was renamed to text. It’s more obvious what a text variable is intended to store than string.
  • Similarly, a number type is introduced. In many (if not most) languages, there is a distinction between whole integer values and those with fractional decimal quantities after them (often named double and/or float). Storage of numeric values are handled by the compiler so you can focus on any mathematics required.
  • Library imports are mostly automatic. In .NET, for example, it’s often required to import the System namespace to access essential things like user input and output. The compiler handles a lot of these behind the scenes. The import keyword is really only needed when referencing other containers you create.
  • The compiler infers as much as possible. While some types were renamed for increased comprehension (like string above), they often aren’t explicitly required either. Function return types, for example, are inferred, but can be specified if you so choose. It’s actually wise to specify them anyway for self-documenting code and initial planning, but the flexibility is there for more experienced programmers and for beginners who forget or are unsure.
  • There is very little boilerplate. It’s easy to keep your code modular by way of the container specification and that’s all that needs to be considered when isolating and importing code. To get started, the only required from the compiler’s perspective are having a Main container and a Main function.

Syntax

Some parts of the syntax are original and some are brought over from C# and F#. Those parts are listed here for developers of those languages to get a head start. However, more detail is given below for beginners.

C# Developers…

  • may continue to use the same if, while, do while, and case constructs.
  • may consider a container a simplified version of a namespace.
  • may still declare inferred variables, but must use infer instead of var.
  • may still declare dynamic variables.

F# Developers…

  • may continue to use the same for construct.
  • will recognise the |>/<| operators, but they strictly used for casting.
  • may equate a container to a module.
  • should know variables are not inherently immutable unlike F#.

General notes:

  • The language draws technical inspiration from, besides the obvious, Occam-π, Go, and Forth.
  • Line comments start with //.
  • Block comments start with {* and end with *}.
  • The language places greater emphasis on the concept of a block. These may be stored and passed around but should not be considered lambdas. Blocks can’t accept arguments and don’t modify any external state.
  • The valid types for use in a declaration are number, text, boolean, and object.
  • By convention, not requirement, only containers and functions start with a capital letter. All but two keywords are lowercase.
  • The classic arithmetic operators +, -, *, and / work as expected. / always performs floating point division but it’s easy to target the specific form of result you want. The modulus operator is %.
  • The logical operators are <, <=, >, >=, ==, =/=, and, and or.
  • A test like 5 <= x <= 15 is allowed to avoid writing 5 <= x and x <= 15, though that of course still works.
  • The go keyword prefixes a block of function calls that should execute simultaneously.
  • The C# and F# keywords prefix blocks that hold C# and F# code. At any point, you may invoke executable C# and F# code and it will recognise the current environment the block lives in. This was done so G# can focus entirely on optimising working with the hardware and the other two can do what they do best, being OOP and Functional Programming if you find yourself needing/wanting it (though G# does have everything needed to be considered a general purpose language).

Examples:

container Main

Main () =
{
	output("Hello, world!"); // ; is needed. Code is consumed until one is found.
}
{*
    Example of what a routine may look like for a hand-like construct to play the piano
*}

container Main

Main () =
{
	Joint wrist = Joints.Origin; // if automatically scanned by A Major
	Joint indexFinger = wrist.B0, middleFinger = wrist.C0, ringFinger = wrist.D0;

	routine PlayFirstFourNotesOfTheFairyTailAnimeTheme =
	{
		// Hardware-specific instructions are still being defined and may change from what is seen here.
		{*
			Robotics components will likely need to guarantee their starting position.
			For example, if making a hand to play a melody on piano, you may want to start at and make movements relative to middle C.
			The adjust keyword acts like a function to perform the initialisation.
		*}
		adjust [[elbow, 90]]; // Simplified example; "pass" an array of tuples of joints with their starting positions

		middleFinger.down(5); // D5
		middleFinger.pause(100);
		go
		{
			middleFinger.up(5);
			ringFinger.down(5); // E5
		}
		go
		{
			ringFinger.up(5);
			middleFinger.down(5); // D5
		}
		go
		{
			middleFinger.up(5);
			indexFinger.down(5); // C5
		}
		indexFinger.up(5);
	}

    PlayFirstFourNotesOfTheFairyTailAnimeTheme; // invoke
}

More examples and details coming soon. 2020|05|06

%d bloggers like this: