One fewer keystroke per statement

Hey! It’s been only a few days since last release, but I just couldn’t stop working on Lit. So here’s a new release, and it’s packed with stuff I wanted since forever. The new features are:

One fewer keystroke per statement

Semicolons aren’t required anymore! You can now write:

println "Hello, world!" # no semicolon here

Semicolons are still allowed, just not required.

Treat primitives as instances

Now it’s possible to call methods on primitive types like numbers and strings. For example:

println 65.chr?() # "A"

let s = "abc"
println s.empty?() # false
println s.size() # 3
println s.chars() # ["a", "b", "c"]

Add function literals (anonymous functions)

You can now create functions without naming them. This is useful for passing functions as arguments to other functions. For example, you can now write:

let add = fn { |a, b| return a + b; }
println add(1, 2); # 3

Operator overloading

You can now overload operators on custom types. Here’s a list of the currently supported operators and the methods you need to implement in your type to overload them:

Operator Method to implement
unary - neg
+ add
- sub
* mul
/ div
% mod
== eq
!= neq
< lt
<= lte
> gt
>= gte
[] get
[]= set
stringify (i.e. println) to_s

Check out this example to see how to overload operators on custom types.

else if

Add an else if syntax sugar to the if statement. Now you can write:

if a == 1 {
    println "a is 1"
} else if a == 2 {
    println "a is 2"
} else {
    println "a is neither 1 nor 2"
}

This is equivalent to:

if a == 1 {
    println "a is 1"
} else {
    if a == 2 {
        println "a is 2"
    } else {
        println "a is neither 1 nor 2"
    }
}

Add Map type

A map, also known as a dictionary or hash table, is a collection of key-value pairs.

let m = Map();
m.set("a", 1);
m.set("b", 2);
println m.get("a"); # 1
println m.get("b"); # 2
println m.get("c"); # nil
println m.merge(Map("a", 2)) # Map("a" => 2, "b" => 2)

Allow fn keyword before method definitions

In the past, you could not use the fn keyword before method definitions. Now you can:

type Foo {
  # this wasn't allowed before
  fn bar { println "bar"; }

  # this was allowed before and still works. I might deprecate it in the future
  baz { println "baz"; }
}

Change default string representation for instances of custom types

Before, instances had the not very helpful “ instance" string representation. Now, they'll list their types and fields.

type User {
  init { |name, age|
    self.name = name
    self.age = age
  }
}

println User("Alice", 30) # User(name: "Alice", age: 30)

New stdlib functions

sleep, argv, exit, panic, eprint, eprintln, and measure were added.

Next up

I know most of these changes are syntax-related, but I want Lit to feel right as a scripting language. The language isn’t super powerful yet, so I’ve been writing only small scripts with it. It is also super slow, but I don’t care about that yet.

As for the next release, I don’t have a plan for the next release, but I’d love to remove (or greatly reduce) the statements in the lang. Blocks need to be expressions, as well as ifs. println/print should not be keywords, but rather functions. Maybe a syntax for array and map literals? Stay tuned!