Share your Programming Language Jam submissions here!

← Back to all posts
The Heck Programming Language
Mashpoe (13)

The Heck Programming Language

I think the best way to demonstrate what's cool about heck is to just show some working code:

func factorial(num) {

    if (num == 1) {
        return 1
    }

    return num * factorial(num - 1)
}

print("5! is:")
print(factorial(5)) // prints 120

I assume you're probably wondering what's so special about that code. It just looks like a standard scripting language; a python clone!

It's not!

Heck is an attempt to use type inference as well as other extremely smart compile-time checks to make simple code fast. Heck is a statically-typed, compiled programming language, with a one-liner hello world:

print("hello world")

The main design philosophy behind Heck is that if a human can understand your code, a computer should be able to as well. After all, the point of a programming language is to allow humans to communicate with computers more easily.

Many language features make code harder to read for humans, and others make it more tough on computers.

For example, many people believe dynamic typing is easier for humans to understand and harder for computers, because computers cannot determine data types when reading code for a dynamically-typed language, while humans can.

We believe this to be completely false. A program is only a good program if you can look through the code and quickly understand it. Most of the time, Python code is very easy to understand, and any programmer with a good amount of experience should be able to tell you the types of every variable, even though the data types aren't explicitly defined.

The only reason a computer can't tell what the types will be
is because of a few rare edge cases, such as reassigning variable types:

a = 6

if randomCondition():
    a = "hello"

# now you don't know what type "a" is, and neither does the computer :(

Heck simply removes these weird edge cases and reads through your code in two passes in order to fully understand everything that's going on:

// during the first pass,
// nothing about the function "add" is not known
print(add(5, 6))

func add(a, b) {
    return a + b
}

Many languages avoid interpreting code in two passes at all cost, but they only ever miss out because of this.

Looking through code in this manner allows other kinds of compile-time checks as well:

let A: int

func printA() {
    print(A)
}

// ERROR: use of uninitialized variable "A"
printA()

A = 6

// OK
printA()

In the future, Heck will have reference types, and this functionality will be used to automatically do lifetime checks, ensuring that your code is completely safe.

Heck also compiles extremely fast. In fact, since the challenge asked for "wild and exciting" ideas, our team thought this would be a perfect opportunity to target one of the most underappreciated modern web technologies: WebAssembly. The Heck compiler was written from scratch in C in order to be extremely lightweight, and this allows it to be packaged into a JavaScript library.

heck.js

Heck.js is an experimental JS library that allows programmers to quickly run Heck code on the web. Heck.js has a very simple JavaScript API that allows it to quickly make calls in the browser:

example.html

<html>
    <head>
        <script src="heck.js"></script>
        <script>
        heck.addImports({
            getRandomNumber: function() {
                return Math.random();
            }
        });
        </script>
    </head>
    <body>
        <script type="heck">
        // import a function from JavaScript
        import func getRandomNumber() -> float
    
        // prints a random number to the console
        print(getRandomNumber())
        </script>
    </body>
</html>

Heck supports most common programming constructs. You can try heck in the main repl. There is a folder titled "examples" that you can look through in order to get an idea of the language.

You can also check out the docs and heck.js.

Our team name is @HeckLanguage and the members are @Deadly_Ore and @Mashpoe!

(Edit: I fixed up the submission, before the repl wasn't included properly)

Commentshotnewtop
seaspooder (2)

This is hecking cool!

MocaCDeveloper (511)

How did you make it compiled?

Like, what did you do/what did you use to make it compiled?
I really want to make a compiled language, maybe you can help give advice on how to to it?

Mashpoe (13)

@MocaCDeveloper The most common way to make a compiled language is through the use of LLVM, which is a tool that allows you to make cross-platform compilers. In order to use it, your compiler must convert source code into LLVM IR (Intermediate Representation). IR is basically just a generic assembly language that LLVM can use to generate machine code (e.g. executables) for most CPU architectures.

This version of my language doesn't use LLVM, but it does have a similar approach. I used WebAssembly, which is similar to LLVM IR because it's a generic assembly language which can be assembled for a variety of different architectures by the browser. WebAssembly and LLVM IR both have their own respective text and binary formats. The LLVM IR binary format can be used to reduce the overhead of assembling the IR, but the WebAssembly binary format proved to be too difficult to work with directly for this jam.

The Heck compiler works by reading source code, generating a syntax tree, and using the syntax tree to produce WebAssembly code, which is written to a file in text format. Then another library is used library to convert the WebAssembly code from text format to binary format. After that is all done, the WebAssembly binary can be run directly in the browser, or through Node.js.

I wouldn't recommend using WebAssembly directly unless you want your language to be able to compile and run source code directly in the browser like Heck. LLVM is more cross-platform, and LLVM IR can also be converted into WebAssembly.

If you want to see how the syntax tree is converted into IR, you can look in the folder src/compiler in my project. The code in that folder generates WebAssembly code directly from the syntax tree.

Mashpoe (13)

In order to be fair to everyone else in the jam, I want to point out that the language was started before the jam.

I don't want that to overshadow all of the hard work that our team put into the language. At the start of the jam, it was very far from anything that could actually run, it had no real functionality, and it could barely parse an incomplete syntax tree.

Heck has come so far and I'm very happy with the result.

I also forgot to add the language motto to the submission:

Heck - it's hecking fast!

AkshantLanjewar (0)

Its a solid language. It reminds me of swift with elements from rust without retarded pointer logic and compiler safety.

I'm assuming you are using LLVM, so you should initialize all your variables with a null type so each function or operation can do their own nullchecks so functions that can work with a null type won't throw an error.

Also, try to keep the documentation and imports similar because you imported getRandomNumber() -> float, but earlier there were no return types.

Otherwise really nice project

Mashpoe (13)

@AkshantLanjewar Hello! Thank you for checking out my language! I am actually not using LLVM, but I plan on making a port in the future to make things more cross platform. Right now it directly outputs WebAssembly, which is important because Heck is supposed to be able to quickly compile in the browser (see Heck.js) and LLVM has too much overhead for that.

I don't think a null type would work for Heck since it's statically typed. As shown in the Python code snippet, changing variable types causes confusion for humans and computers, so if null had it's own type, initializing variables to null would mean that they are permanently null in a statically-typed language like Heck. Heck works more like Rust, where the compiler can tell if a variable has been initialized. There are still some edge cases where Heck can't detect uninitialized variables, but those will be fixed very soon.

The compiler does do something similar to a null check for each operation. In the second pass it keeps track of which global and local variables are initialized, so you get the same result as runtime null checks, but with zero overhead. Rust is one of Heck's biggest inspirations, and it doesn't initialize variables to null because of the overhead. The only difference is that Heck keeps track of this for each function call so things like mutable globals aren't unsafe (thread safety will be handled differently than Rust so this is ok).

Like I said, there are still some issues with this. For example, there are no initialization checks for array elements.

This issue is currently bypassed by taking advantage of the way WebAssembly handles memory addresses and the way arrays and strings are stored in memory. Each array or string is stored alongside a 32-bit integer that indicates it's size, which is much better than a null terminator IMO. I decided to put a 32-bit integer with the value "0" at the memory address "0," which acts as a "dummy" empty array or string, and then I initialized all array elements to zero by default.

If you try to access an uninitialized element of an array, there will be no issues because uninitialized strings will point to the address "0" which has a fake empty string. Access to child arrays has the same result, and integers and floats will just have the value "0."

This is not a long term solution because on most architectures null is an invalid memory address, but it enforces safety for the time being.

Also, the reason Heck has what appears to be an inconsistency with the imports is because it can't do type inference if it can't read the function body. Imported functions are implemented outside of the language and the compiler can't see what they return, so, as mentioned in the docs, the return type must either be explicitly declared or it is assumed to be void.

I hope that clears some things up. I should have specified that Heck is supposed to have the same speed as C or Rust, and that everything has to be determined at compile time. Heck is still far from perfect, but I hope it eventually becomes a language with one of the most intelligent compilers :)

AkshantLanjewar (0)

@Mashpoe yeah I agree, but with a c based memory allocation model all variables are pre-initialized in binary with an array of 0. for example in your print part of the code, you print A and it throws an error as undefined. In a normal memory allocation model, variables are created as an array of 0 in memory, ex a 32 int is 32 bytes of 0 in memory.

So when you call a function that references A, it is defined as 32 bytes of 0, and the value is just 0 due to binary mathematics.

I don't know the specifics of Heck's design but I think you may be allocating memory based on the value assigned and reassigned based on changes.

checking for null can simply just be checking if the variable = 0 in binary which takes picoseconds, same with the allocation of 32 bytes of memory

Mashpoe (13)

@AkshantLanjewar Memory may be set to zero by default on some platforms but this is definitely not the usual case. Uninitialized pointers in C will not always be set to NULL and numbers will not always be set to zero. This can happen but it is only guaranteed for static memory (e.g. global variables) and it is never safe to assume statically allocated (values allocated on the stack) or dynamically allocated (e.g. with new or malloc()) memory is set to zero. See the Wikipedia section on memory management in C.

Higher level languages like Java may initialize references to null but this is simply not the case for languages like C, C++, or Rust. Rust is unique because like Heck, it keeps track of which values have been initialized before their use at compile time, but most lower-level compiled languages do not automatically set values to null because of its inherent overhead.

Anyway, thanks for looking at my language and providing criticism. I really appreciate when people care enough about my work to look at it and give feedback :)

Mashpoe (13)

All of the features shown in the description as well as the docs (such as arrays and while loops) should work without any issues!