Skip to content

A prototypal object oriented language that borrows heavily from functional programming concepts and languages

Notifications You must be signed in to change notification settings

jasonsbarr/liszt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Liszt Programming Language

Programming language that seeks to incorporate the best aspects of both functional and object oriented programming

This repo may end up being a prototype as I'm experimenting with bidirectional type checking.

Written with TypeScript v4.8.4.

Basic Concepts

Liszt is a gradually typed language with bidirectional type checking, which means type annotations can be omitted and many types will still be inferred from the expressions. The one place where type annotations are really useful is function parameters and sometimes the function return type, but if you omit these your program will still work (albeit without static type checking).

Liszt is object-oriented with prototypes, but also has first-class functions and other functional programming features.

The syntax is inspired by Ruby and Python. The type system is inspired by TypeScript.

Liszt compiles to JavaScript, so it can be used both for browser-based applications and on the server via Node.js.

Credits

The type checker implementation owes a great deal to Jake Donham's Reconstructing TypeScript series.

Syntax

Comments start with a semicolon and continue to the end of the line.

There are literals for Integer, Float, String, Boolean, Symbol, Object, Tuple, Vector, and Nil types.

17
3.14
"hello, world"
true
:thisIsASymbol
{ type: "Person", name: "Jason", age: 42 }
(1, false, "hello", { value: 42 })
vec[1, 2, 3]
nil

Note that both Integer and Float are subtypes of Number, so they can be mixed together in most operations (need to fix this).

Declare variables with var and constants with const. Note that if you reassign a variable the new value must be of the same type as the one it was declared with.

var changeMe = "I can be changed!"
const cantChangeMe = "Try to change me and it will throw an error"

changeMe = 42 ;=> Type error!

Define functions with the def keyword and end the body with end. Note that we strongly recommend using type annotations with the function parameters, though a return type annotation is not necessary. If you don't annotate the function parameters, they will be typed as Any which basically turns off the type checker.

def inc(x: integer)
    x + 1
end

If you want to annotate the return type you can do that too; sometimes it's a big help to the type checker (especially with recursive functions).

def fib(n: integer): integer
    if n == 0
        1
    else
        n * fib(n - 1)
end

You can also just assign a lambda to a variable (though a lambda must use an explicit block if you want its body to include more than a single expression). Note the function type annotation - it's not necessary, but it does help the type checker out! When there's no function type annotation, function parameters that are not individually annotated get typed as Any.

const inc: (x: integer) => integer = (x) => x + 1

You can use type variables in your function definitions for parametric polymorphism. Type variables start with a single quote character.

def id(x: 'a)
    x
end

Iterate over vectors with for statements.

var sum = 0

for n in vec[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  sum = n + 1
end

sum == 55  ;=> true

You can also create your own types as aliases for built-in types, tuple types, and object types.

; Object type
type Point = { x: number, y: number }

; Intersection type
type Point3D = Point & { z: number }

; Union type
type Coordinate =
    { type: "Cartesian", x: number, y: number }
  | { type: "Polar", angle: number, magnitude: number }

; Function type
type Adder = (x: number) => number

; Curried function type
type AdderMaker = (x: number) => (y: number) => number

; Tuple type
type Coord = (number, number)

You can also use type variables to create generic types, with no concrete type annotations needed.

type Box = { value: 'a }
var boxedNum: Box = { value: 5 }

Note that types are structural, not nominal, so an unannotated object with all the same property types as a defined type will check properly for that type.

Releases

No releases published

Packages

No packages published