FreePalestine.

Justcode

no cruft: no build systems, no null, no exception handling, no ORMs, no OOP, no inheritence hierarchies, no async/await, no compilation, no dev environments, no dependency hell, no packaging, no git, no github,

no devops: no yaml, no config files, no docker, no containers, no kubernetes, no ci/cd pipelines, no terraform, no orchestrating,

no infrastructure: no sql, no nosql, no connection poolers, no sharding, no indexes, no servers, no serverless, no networking, no load balancers, no 200 cloud services, no kafka, no memcached, no unix, no OSes

Darklang puts everything in one box, so you can build CLIs and cloud apps with no bullshit, justcode.

Also it's a really enjoyable language to use!

In development - here’s how things will look shortly

$ curl https://darklang.com/download | bash

Darklang installed in ~/.darklang/bin/darklang

Add to PATH via .bashrc [y, n, ?]: y
✔Added to .bashrc.

Next you can:

# Try the tutorial

darklang tutorial

# Run some code from the package manager

darklang @paul.fizzbuzz 3

# Generate some code

darklang prompt "Find ts scripts with more than 600 lines which use the commonjs format"

# See available command line options

darklang help
$ darklang @paul.fizzbuzz 3
1
2
Fizz
$ darklang deploy @paul.fizzbuzz /fizzbuzz
Deployed to https://furry-squirrel-3562.darklang.io/fizzbuzz in 0.135s
$ curl -sSO https://furry-squirrel-3562.darklang.io/fizzbuzz/3
1
2
Fizz
$ darklang prompt "Find ts scripts with more than 600 lines which use commonjs format"

We need your AI credentials, which will be stored locally in ~/darklang/secrets/

[1] Login to use Darklang AI service
[2] Enter GitHub copilot credentials
[3] Enter OpenAI (GPT 3.5/4) credentials
[4] Use local model

Enter [1-4]: 1

Login or register [L, r]? L
Username: paul
Password: **************

✔Logged in.

Saved script in ./find-large-ts-cjs.dark in 43.8s

find-large-ts-cjs.dark

let findLargeTypescriptCommonJSFiles (path : String) =
  Directory.traverse (fun path ->
    if not (List.oneOf (File.extension path) [".ts", ".mjs", ".cjs"]) then
      print $"Skipping - wrong file type: {path}"
    else
      let contents = File.readString path
      let lines = String.splitNewlines contents
      if (List.length lines) <= 600 then
        print $"Skipping - too short: {path}"
      else
        let isCommonjs =
          lines |> List.any (fun line ->
            line |> Regex.matches "/const .* = require(.*)/"
          )
        if isCommonjs then
          print $"Found one: {path}"
  )

findLargeTypescriptCommonJSFiles "./"

Simple types using Records and Enums

Dynamic languages are great, allowing great flexibility. But now that they've matured and projects have gotten larger, static typing has been layered on top of very dynamic languages, and the intersection is not pleasant. Functional static languages like Darklang have simple Record and Enum types that can model nearly everything with much less complexity.

  • Abstract Data Types made of Record and Enums can model nearly anything, like in Rust, Elm, OCaml and F#
// Record
type Url = {
  scheme : HttpScheme
  domain : String
  port : UInt16
  path : String
  query : Option<String>
}

// Enum (aka Variant, Sum Type, or Abstract Data Types)
type UrlError =
  | InvalidScheme(String)
  | EmptyDomain
  | InvalidPort(Int64)
  | Unparseable(msg:String, context:String)

// Aliases are just shorthands
type UrlParseResult = Result<Url, UrlError>

We believe Object Oriented programming is a terrible way to model programs, and inheriting is a terrible misfeature. Languages like OCaml, Elm, and F# have shown that nearly all problems can be modeled using ADTs.

Option and Result types instead of null and exceptions

I think it's widely accepted that null is a mistake, and that an Option type (aka Optional, Maybe, etc) makes it significantly easier to program.

  • We further believe that Exceptions — which can in most languages be thrown at any point — makes it very frustrating to know that a function actually works. There are no exceptions in Darklang, and we use Result types to manage error cases

  • ? and ! operators (similar to Rust, TypeScript, and Clojure) provide ways to ergonomically handle errors without being too annoying (see also Gradual Static Typing)

  • We will admit we have some RuntimeErrors which cannot be caught, but we are working to remove them almost entirely from the language (see Gradual Static Typing for what we're keeping)

Garbage Collected

It can be fun to satisfy the borrow checker, or manage allocations individually, but it can also be fun to just get working programs immediately. We believe that run-time garbage collection is one of the greatest programming language features, and we're all in.

Unicode First

Languages created last millennium typically use Strings made of bytes, or worse, UTF16 characters, or even worse, Unicode Codepoints! Like Swift, we believe that Characters should represent screen readable characters, like 👨‍👩‍👦‍👦. All characters in Darklang represent Extended Grapheme Clusters: one character that you see on screen. Naturally, working on Unicode Codepoints and normal bytes is also well supported.

Instantly run any package from the CLI: darklang @username.functionName arg1 arg2

Darklang has a new model of sharing programs. Any function in the package manager can be called directly from the command line (so long as we can figure out how to coerce the command line arguments correctly)

$ darklang @paul.fizzbuzz 10
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz

Next-gen package management

Darklang has a rather unique package manager, where functions and types are individually versioned and immutable, taking a lot of the hassle out of package management.

  • Only download the specific package items you use
  • Only upgrade the specific package items you use
  • Automated dependency upgrades, as we track deprecation status, and know what functions are pure and safe to update.
  • Different packages can rely on different versions of other packages
  • Use multiple versions of the same package item at once: allows testing new versions without having to change an entire package version, lowering risk.
  • The package manager functions as a source repository.

Gradual Static Typing

Gradual Static Typing allows running incomplete programs so you don't need to ensure everything type checks when you're getting one path working

  • While prototyping, just run code until you hit a type error
  • After prototyping, run the full type checker to gain confidence your whole program works
  • Full type-checking hints in VSCode or in LSP editors
  • ! and ? operators allow easy error handling while you prototype
  • Automatic refactoring converts ! into proper error handling

Fully asynchronous runtime

Darklang has a fully asynchronous runtime, so making an HTTP call or reading a file doesn't block the runtime.

No async / await

Adding async and await keywords to every language was a mistake. It exposes the complexity of concurrency and multi-threading to languages which were originally designed for simplicity.

Concurrent and parallel execution via data-dependencies

When you make an async request, it first waits for any arguments that are async, and starts when they're done. If another function call needs to use the result, it will wait for it before starting.

Since darklang values are immutable, there won't be any race conditions from this.