A minimal but powerful programming language based on C, Go and Haskell
Perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away.
(Antoine de Saint-Exupéry, translated by Lewis Galantière.)
dotLang is a general-purpose programming language which is built upon a small set of rules and minimum number of exceptions and a consistent set of specifications.
Because it is:
dotLang is very similar to C but with some significant improvements:
After having worked with a lot of different languages (C#, Java, Scala, Perl, Javascript, C, C++ and Python) and getting familiar with some others (including Go, D, Swift, Erlang, Rust, Zig, Crystal, Fantom, OCaml and Haskell) it still irritates me that most of these languages sometimes seem to intend to be overly complex with a lot of rules and exceptions to keep in mind. This doesn’t mean I don’t like them or I cannot develop software using them, but it also doesn’t mean I should not be looking for a programming language which is simple, powerful and fast.
That’s why I am creating a new programming language: dotLang.
dot programming language (or dotLang for short) is an imperative, static-typed, garbage collected, functional, general-purpose language based on author’s experience and doing research on many programming languages (namely Go, Java, C#, C, C++, Scala, Rust, Objective-C, Swift, Python, Perl, Smalltalk, Ruby, Haskell, Clojure, Eiffel, Erlang, Elixir, Elm, Falcon, Julia, Zig, F# and Oberon-2). I call the paradigm of this language “Data-oriented”. This is an imperative language which is also very similar to Functional approach and it is designed to work with data. There are no objects or classes. Only data structures and functions. We have first-class and higher-order functions borrowed from the functional approach.
Two main objectives are pursued in the design and implementation of this programming language:
Achieving both of the above goals at the same time is impossible, so there will definitely be trade-offs and exceptions. The underlying rules of design of this language are Principle of least astonishment, KISS rule and DRY rule.
As a 10,000 foot view of the language, the code is written in files (called modules) organized in directories (called packages). We have bindings (immutable data which can be functions or values) and types (Blueprints to create bindings). Type system includes primitive data types, sequence, map, enum, struct and union. Concurrency and lambda expression are also provided.
Language | First-class functions | Sum types | Full Immutability | Garbage Collector | Module System | Concurrency* | Generics | built-in data types | Number of keywords |
---|---|---|---|---|---|---|---|---|---|
C | No | Partial | No | No | No | No | No | 14 | 32 |
Scala | Yes | Yes | No | Yes | Yes | Yes | Yes | 9 | ~27 |
Go | Yes | No | No | Yes | Yes | Yes | No | 19 | 25 |
Java | Yes | No | No | Yes | Yes | No | Yes | 8 | 50 |
Haskell | Yes | Yes | No | Yes | Yes | No | Yes | 63 | 28 |
dotLang | Yes | Yes | Yes | Yes | Yes | Yes | Yes | 8 | 9 |
dotLang consists of these components:
dot
: A command line tool to compile, debug and package source code.core
library: This package is used to implement some built-in, low-level features which can not be simply implemented using pure dotLang. This will be a built-in feature of the compiler/runtime.std
library: A layer above core which contains some general-purpose and common functions and data structures. This is optional to use by developers.Grammar of dotLang in a notation similar to EBNF can be found here
queue = import("/core/std/queue")
(you can also import from external sources like GitHub).int
, float
, char
, byte
, bool
, string
, type
, nothing
.my_var:int = 19
(type is optional, everything is immutable).my_array = [1, 2, 3]
(type of my_array
is [int]
, sequence of integers).my_map = ["A":1, "B":2, "C":3]
(type of my_map
is [string:int]
, hash map of string to integer)MyInt = int
(Defines a new type MyInt
with same binary representation as int
).IntType : int
(A different name for the same type).Point = struct(x: int, y:int, data: float)
(Like C struct
).location = Point{x:10, y:20, data:1.19}
.MaybeInt = int | nothing
(Can store either of two types, note that this is a named type).calculate = fn(x:int, y:int -> float) { x/y }
(Functions are all lambdas, the last expression in the body is return value).my_task := processData(x,y,z)
(Start a new micro-thread and evaluate an expression in parallel).ValueKeeper = fn(T: type -> type) { struct{data: T} }
(A function that returns a type)push = fn(x: T, stack: Stack(T), T: type -> Stack(T)) { ... }
DayOfWeek = enum [saturday, sunday, monday, tuesday, wednesday, thursday, friday]
result = validateData(a,b,c)@{makeError(InvalidArgument)}
#
Comment.
Access struct members()
Function declaration and call{}
Code block, multiple selection from module namespace, error check, struct declaration and literals[]
Sequence and hashMap|
Union data type->
Function declaration//
Nothing-check operator:
Type declaration (binding, struct field and function inputs), type alias, struct literal=
Binding declaration, named type_
Place-holder (lambda creator and assignment)::
Function call composition@
Error check?
If operator&
Type inference for struct literals:=
Parallel execution..
Access inside modulePrimitive data types: int
, float
, char
, byte
, bool
, string
, nothing
, type
Core data types: error
Operators: and
, or
, not
Data type identifiers: fn
, struct
, enum
Reserved identifiers: true
, false
, import
SomeDataType
, someFunction
, some_data_binding
, some_module_alias
.0x
prefix for hexadecimal numbers and 0b
for binary._
as digit separator in number literals.Operators are mostly similar to C language:
and, or, not, ==, !=, >=, <=
+, -, *, /, %, %%, >>, <<, **
==
will do a comparison based on contents of its operands.A // B
will evaluate to A if it is not nothing
, else it will be evaluated to B (e.g. y = x // y // z // 0
).A ? B
will evaluate boolean expression A first, if true will evaluate to B, otherwise will evaluate to nothing
. This can be mixed with //
to provide ifElse construct.home_dir = (is_root ? "/root") // (is_default_user ? "/default") // (is_unknown ? "unknown") // "/tmp"
true
or false
which are equal to 1
and 0
respectively when used as index of a sequence.#
. Anything after #
till end of the line is comment.##
as first two characters of the line and can be defined for a binding, type or function. These will be scanned with tools to automatically generate documentation. If ##
is the only thing in the line, it starts a block comment until another ##
appears in the file.
##
some docs about this function
x:int - comments about this input
-> string - comments about the output
##
process = fn(x:int -> string) { ... }
##Meta comment in one line
x = 12
##
some docs about this type
x:int - comments about this field
y: string - comments about the y
##
DataTypeX = struct {x:int, y:string}