Welcome to the future of programming languages: OK?

Related tags

Miscellaneous OK


Try it out on the playground

OK?'s mascot: Quentyn Questionmark.

Programming Is Simple Again

OK? is a modern, dynamically typed programming language with a vision for the future. OK?'s mission is to do away with the needless complexity of today's programming languages and let you focus on what matters: writing code that makes a difference.



Very early on in its design, it was decided that OK? would not feature a ternary operator. For those unaware, the ternary operator looks like this:

let a = isprod ? 'prod' : 'dev';

Disgusting, we agree. Einstein, Tesla, and Newton all died long ago, so there's really only a handful of humans left on Earth who are capable of parsing that stupifying syntax. What does the question mark mean? What does the colon mean? In OK? we leave those questions for the philosophers and focus on what's important: writing clean code.

A language only needs one conditional control flow construct, and in OK?, that construct is the switch statement. Switch statements are more versatile and expressive than ternary operators and if statements, and after a while you'll forget those other constructs ever existed. Here's the above statement in idiomatic OK?:

let a = switch isprod {
  case true: "prod";
  case false: "dev";

The switch form, although longer, is unquestionably clearer. Understanding the value of simplicity over complexity is the first step to learning OK?

Readable Switches

Given that switches are so central to OK?, we wanted to avoid some common pitfalls around switches found in other languages. In other languages it's common to have a single switch statement take up several pages of an editor with bloated logic being shoved into each switch case, so in OK? we made it that you can only have one statement per case:

If you want to execute multiple statements per switch case, just wrap them in a function:

switch x {
  case true:
    z = z + 2
  case false:
    x + x = 1;
    y = y - 1; // <-- ERROR: switch blocks can only contain a single statement

let onfalse = fn() {
  x + x = 1;
  y = y - 1;

switch x {
  case true: z = z + 2;
  case false: onfalse();

This ensures separation of concerns: the specific per-case logic is factored away, bringing the cases themselves to the forefront.

Nulls Are Not OK

Null values, famously dubbed the billion dollar mistake. I want my money back Tony Hoare.

Implicit nullability is a thorn in the side of every self-respecting developer and that's why in OK?, null values are NOT OK, OK?

We could have omitted null values from OK?, but then you might forget how bad they are, so we've included them, represented not as nil or null but as NO!, which is the only sensible answer to the question Are nulls OK?

To drive the point home, we've also added a builtin function named ayok? which takes a value and returns whether that value is OK.

let x = NO!
puts(ayok?(x)) // prints 'false'

let y = 10
puts(ayok?(y)) // prints 'true'

Now when your colleagues ask whether nulls are OK, you'll know exactly what to say.

Error Handling

In OK?, errors are simply values, just like any other value. One value they're particularly similar to is strings, and that's because by convention, they actually are strings. For example:

let divide = fn(a, b) {
  return switch b {
    case 0: [NO!, "cannot divide by zero"];
    default: [a / b, ""];

result = divide(5, 0)
switch result[1] {
  case "": puts(result[0])
  default: puts(result[1]) // prints "cannot divide by zero"

No magic, just arrays and strings.

Readable Logical Operators

Ever come across a conditional statement that chains a heap of long boolean expressions together?

switch p.isactive() && p.credits() >= reqcreds && p.nottype("Admin") {

I think I pulled a neck muscle trying to read that obscenely long line.

In OK?, the && and || operators can only act on variables, so that it's possible for the reader to understand what's going on.

// ERROR: '&&' operator must act on variables
switch p.isactive() && p.credits() >= reqcreds && p.nottype("Admin") {

Here's how the above switch statement would be done in OK?

let isactive = p.isactive()
let enoughcr = p.credits() >= reqcreds
let notadmin = p.nottype("Admin")
switch isactive && enoughcr && notadmin {

This reflects a central tenet of OK?: be kind to the reader.

If you need to short-circuit your conditionals, you can use the lazy keyword:

let isactive = p.isactive()
let enoughcr = lazy p.credits() >= reqcredits
let notadmin = lazy p.nottype("Admin")
switch isactive && enoughcr && notadmin {

If p.isactive() returns true, then p.credits() and p.usertype() will never be called.

With this feature you get the best of both worlds: clean, readable code, without sacrificing performance.

One Comparison Operator

Ever found yourself running in circles looking for the right comparison operator? Instead of wasting time memorising the hieroglyphics of ==, !=, >, <, >=, and <=, what if we told you that you only needed one?

OK? features a single comparison operator: >=

In other languages In OK?
a >= b a >= b
a <= b b >= a)
a > b !(b >= a)
a < b !(a >= b)
a == b let x = a >= b; let y = b >= a; x && y
a != b let x = !(a >= b); let y = !(b >= a); x || y

This means that instead of juggling six different comparison operators in your head you can focus on what matters: creating great software.

In the OK? standard library we haven't found the need to extract any of these expressions into their own functions, but there's nothing stopping you from rolling your own:

let equals = fn(a, b) {
  let x = a >= b;
  let y = lazy b >= a;
  return x && y;

Dead-simple Operator Precedence

in OK?, 5 + 2 * 3 evaluates to 21, not 11, because addition and multiplication have equal operator precedence. If you want to evaluate your expression in some other order, you simply need to use parentheses: 5 + (2 * 3).

This simple left-to-right default spares you from scrounging around the internet looking for an operator precedence table, and lets you keep your eyes on the code.

Death To Classes

The authors of OK? watched as object-oriented (OO) languages boomed in popularity, only to find them soon buckling under their own weight. Central to this clinical obesity is the class.

A class takes a sensible idea: defining data along with methods that act on that data, and then drives it off a cliff by adding inheritance and subtype polymorphism. It should be no surprise that a bunch of class-obsessed aristocratic oldies in the 60s, who probably spent all their time deciding which child should inherit most of the estate, decided to add a construct named 'class' which revolved around inheritance.

Well, the revolution has finally come! There is no inheritance in OK?, in solidarity with all those who fought against the bourgeoisie in years past. Some other languages opted for structs over classes yet despite their developers honourably denouncing OO, old habits die hard with some accidentally uttering 'class' when they mean 'struct'. Some developers even say 'class' deliberately, a dogwhistle to return to the old days when the OO aristocracy still held the developer profession by the throat.

To remove any ambiguity and to ensure full commitment to the death of classes, we've decided to use our own terminology: 'notaclass' , or 'nac' for short.

notaclass person {
  field name
  field email

Let's deep dive into what makes our nacs special:

All Fields Are Private

You don't check how your friend is feeling by prying them open with a crowbar and perusing through their entrails; you just ask them. The same is true in programming. There is no way to mark a field as public in OK? because the public API of a nac should describe behaviour, not state.

let p = new person()
p.name = "Jesse" // <-- ERROR: access of private field 'name'

For extenuating circumstances, you can define a privacy acknowledgement with the pack keyword, allowing external code to access a nac's fields if they include the acknowledgement in a comment, preceded by 'I acknowledge that':

notaclass person {
  pack "I am a stupid piece of shit who should not be doing this"

  field name
  field email

let p = new person()
// I acknowledge that I am a stupid piece of shit who should not be doing this
p.name = "Jesse"  // <-- No error

This makes it easy to find privacy violations with CTRL+F and lets you communicate your tolerance level explicitly.

No Constructors

What part of notaclass don't you understand? Constructors are a class-based thing, and OK? does not have classes. The word Constructor also contains the word struct, and OK? does not have structs. If you want to define the initial state of a nac, just add it as a separate method:

notaclass person {
  field name
  field email

  public init fn(selfish, name, email) {
    selfish.name = name;
    selfish.email = email;

let p = new person()
p.init("Jesse", "[email protected]")

Notice the first argument in that method: we considered using self, this, or me, for the receiver argument, but felt like these all had connotations that would confuse people if carried over into OK?. In OK?, receivers are just regular function arguments with no special scoping and no special treatment. But they are still kind of similar to receivers in other languages so we settled on self-ish, a sensible middle-ground. It's a word that accurately describes you, if you're the kind of person who disagrees with this convention.

Evolution Over Composition

You may be familiar with the phrase 'Composition Over Inheritance'. That was cute but the world has moved on. Composition fixes the fragile base-class problem introduced by Inheritance, only to introduce its own useless base-component problem where you can't shake the feeling that you've actually handicapped yourself a bit by depending on composition for code reuse, especially when the component has no ability to interact with its parent.

It's time we take the next logical step: Evolution Over Composition. Instead of thinking in terms of is-a or a has-a relationships, think in terms of becomes-a relationships. How does this work?

To enable evolution, you simply need to define an evolve method in your nac. The evolve method is invoked after any other of the nac's methods are executed, and it determines if the preconditions have been met to evolve the nac instance into a new nac type.

notaclass brgousie {
  public whoami fn(selfish) {
    return "a good-for-nothing aristocrat who likes classes"

notaclass person {
  field name
  field email
  field likeclas


  public whoami fn(selfish) {
    return selfish.name;

  public makeold fn(selfish) {
    selfish.likeclas = true;

  evolve fn(selfish) {
    switch selfish.likeclas {
      case true:
        return new brgousie();
        return NO!;

let p = new person();
p.init("John", "")
puts(p.whoami()); // prints "John"
p.makeold(); // evolve() method is called behind the scenes
puts(p.whoami()); // prints "a good-for-nothing aristocrat who likes classes"

This simple yet powerful feature enables a vast array of possibilities, without the frustration evoked by its predecessors.

Familiarity Admits Brevity

At some point, the High Counsel Of Programming Conventions got together and decided that variable names need to stretch for miles. It's time to reverse that decision. Familiarity Admits Brevity, which is why these days I don't even say goodbye before hanging up on my wife. You should be intimately familiar with your codebase, meaning all of your variables and method names should be short and sweet. You shouldn't need to use juvenile word separators like underscores or camelCase because if you can't capture the meaning of a variable in a single word, that's a sign that you need to refactor.

For this reason, it's idiomatic OK? to limit all variable and method names to eight characters, all in lowercase, and without underscores.

Some example abbreviations:

invalid valid
characters chars
MaximumPhysicalAddress mxphsadr
accrueYesterdaysYield() ayy()
hostEnterpriseYellowBorderBackground() heybbg()

You may disagree with this idiom, and that's okay, because it's enforced by the compiler. You're welcome.

Concurrency, Iterated

We've made it this far without talking about iteration or concurrency, and that's because in OK?, the two concepts are one and the same.

No modern language can be taken seriously without first-class concurrency support, yet we've found that for all that support, developers still forget to leverage concurrency in the one place that screams for it the most: loops.

Introducing OK?'s map function

OK?'s only concurrency construct is also its only loop construct: the map function.

map behaves similar to map functions in other languages, except for one thing: the callbacks are always executed concurrently.

For example, the following code snippet returns a result in one second:

let doubled = map([1,2,3], fn(e) {
  sleep(1); // sleep one second
  e * 2
}); // [2,4,6] obtained in one second

If you just want to compute two expensive values concurrently, you can use map with a switch:

let result = map([0,1], fn(e, i) {
  switch i {
  case 0:
    return expnsiv1();
  case 1:
    return expnsiv2();
let value1 = result[0];
let value2 = result[1];

With the union of concurrency and iteration, the sky is the limit.

let every = fn(arr, check) {
  let passed = true;
  map(arr, fn(e) {
    switch check(e) { case true: passed = false; } }
  return passed;

result = every([5,2,4,1,3], fn(e) { return e >= 2 }); // false

With this speed, your program's going to finish before you've even started writing it.


Dave says:

OK? has transformed me from an angry developer to a happy developer. I used to think composition over inheritance, having my own projects composed of various different languages, never thinking about the power of evolution. Now I've evolved to only use one language: OK?

Joel says:

I used to find OK?'s opinionated syntax constraints coercive. Now I find them liberating. Thinking hard about how to fit a complex variable name into eight characters forces me to write code that future me can easily maintain

Sarah says:

I used to think there were all these features I needed to write quality software, but after a while working with OK? it just clicked: when you stick to the basics, the resulting code is clear and easy to understand.

Jack says:

If I'm on the OK? subreddit and I see you use the word 'class' when you meant to say 'nac', I'm going to start screaming. And that screaming will not stop until I've found you. By then, you'll be the one screaming.

How To Get Started

To play around with the language in your browser, you can go to the playground

To use the language locally, follow the following steps:

  1. git clone the repo.
  2. within the ok directory run go install.
  3. Run ok without any arguments to bring up the REPL, or you can run an OK? file with ok test.ok.

Happy OK'ing!


  • Thanks to https://interpreterbook.com/ for helping us create the best new language since assembly
  • Thanks to @markbergin77 for the idea about nulls not being OK
  • Thanks to @matomatical for showing that even two comparison operators was one too many.
  • fix(): misc readme

    fix(): misc readme

    I LOVE OK?! -- Adding less aggressive language. For those who access other peoples privates, you're not a stupid piece of shit, but you should seriously consider whether or not you're OK?.

    opened by jakiestfu 1
  • Fix implementation of fn every

    Fix implementation of fn every

    I think there is a typo in this example: the switch clause is wrong. Check the code on an array of integers all greater than 2 and the result would be false as well. https://github.com/jesseduffield/OK/blob/d150075b0824c6a5c4d45a599755da33b1e1ff8f/README.md?plain=1#L381-L391

    opened by lrusso96 1
  • support acknowledgement comments

    support acknowledgement comments

    Worth noting that you must acknowledge a privacy violation in a comment above the line where you access the private field/method. It won't work if you comment on the same line. The acknowledgement will apply to every subsequent line in the scope.

    opened by jesseduffield 0
  • Completely Optional

    Completely Optional

    Slightly modified Quentyn's appearance and personality.

    Introduced a few modifications and bugs, possibly even some errors which may cause additional confusion and might need to be fixed later.

    opened by standardgalactic 0
  • x = x + 1 is confusing and obviously wrong

    x = x + 1 is confusing and obviously wrong

    Assignments where a given variable appears both on the left and right of the = are confusing and wrong. Take x = x + 1, there is no value of x such the equality holds. The mathematically correct thing to do in such a case is to set x to NO!. More generally, any assignment where the same variable appears on both sides should set all involved variables to NO!.

    Incrementing x then looks like:

    // The Value Formerly Known As
    let tvfka = fn(a) {
      return a;
    x = 42;
    old_x = tvfka(x);
    x = old_x + 1;
    opened by FlorentBecker 1
  • equals example is dangerous and confusing

    equals example is dangerous and confusing

    As you know, most modern programming languages contain at least two and sometimes three equals operators. This is because value equality and identity comparison are type specific and open up a whole can of worms.

    It therefore surprised me when you suggested defining equals as:

    let equals = fn(a, b) {
      let x = a >= b;
      let y = lazy b >= a;
      return x && y;

    This seems to over-complicate the issue by either assuming generics or duck typing? Generics are scary!

    May I suggest instead defining equals for each type independently as follows:

    let equals = fn(a : int, b: int) {
      let x = a >= b;
      let y = lazy b >= a;
      return x && y;
    let equals = fn(a : string, b: string) {
      let x = a >= b;
      let y = lazy b >= a;
      return x && y;
    let equals = fn(a : char, b: char) {
      let x = a >= b;
      let y = lazy b >= a;
      return x && y;
    let equals = fn(a : Person, b: Person) {
      let x = a >= b;
      let y = lazy b >= a;
      return x && y;

    This eliminates confusing ideas like duck typing (should we call it duck taping?) and scary topics like generics. Someone died once due to buying cheep generic medicine online!

    opened by timthelion 1
  • Suggestion: require parenthesis where operator precedence is non-standard

    Suggestion: require parenthesis where operator precedence is non-standard

    I noticed that the order of precedence for operators doesn't exactly conform to the guidelines that the rest of language follows: while in most places the compiler requires the programmer to be explicit (e.g. pack comments), precedence of operators is a (arbitrary?) choice of the compiler that will still confuse people who are used to excusing Aunt Sally.

    What I would expect from Ok? is to raise a compiler error on expressions such as a + b * c, and require that the programmer either changes the order, or adds parentheses.

    opened by noam93k 0
  • STRUCT_OBJ is never used.

    STRUCT_OBJ is never used.

    The STRUCT_OBJ type, defined here is never used.

    It seems that an instantiated struct describes itself as being a HASH_OBJect here:

    func (self *StructInstance) Type() ObjectType { return HASH_OBJ }

    Nowhere in the code is STRUCT_OBJ referenced, so it might be as well to remove it - especially given the comment above!

    opened by skx 0
Jesse Duffield
Creator of lazygit, lazydocker, and horcrux.
Jesse Duffield
Aboriginal Generics: the future is here!

Aboriginal Generics: the future is here! Inspired by this gem of an idea (click the image to go to the original comment): Installation go get github.c

Pavel Vasilev 151 Oct 3, 2022
IBus Engine for GoVarnam. An easy way to type Indian languages on GNU/Linux systems.

IBus Engine For GoVarnam An easy way to type Indian languages on GNU/Linux systems. goibus - golang implementation of libibus Thanks to sarim and haun

Varnamproject 10 Feb 10, 2022
Day-1 is apart of my 6 days of Christmas challenge where i write in two new languages everyday, and make something weird out of it.

Day-1 is apart of my 6 days of Christmas challenge where i write in two new languages everyday, and make something weird out of it. today was a HTTP server written with PostGreSQL using Golang, R, and shell script read more

RE43P3R 0 Dec 21, 2021
Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

Advent of Code 2021 Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved

Kemal Ogun Isik 0 Dec 2, 2021
Zach Howell 0 Jan 4, 2022
Unit tests generator for Go programming language

GoUnit GoUnit is a commandline tool that generates tests stubs based on source function or method signature. There are plugins for Vim Emacs Atom Subl

Max Chechel 66 Jan 1, 2023
Functional programming library for Go including a lazy list implementation and some of the most usual functions.

functional A functional programming library including a lazy list implementation and some of the most usual functions. import FP "github.com/tcard/fun

Toni Cárdenas 31 May 21, 2022
FreeSWITCH Event Socket library for the Go programming language.

eventsocket FreeSWITCH Event Socket library for the Go programming language. It supports both inbound and outbound event socket connections, acting ei

Alexandre Fiori 110 Dec 11, 2022
Flow-based and dataflow programming library for Go (golang)

GoFlow - Dataflow and Flow-based programming library for Go (golang) Status of this branch (WIP) Warning: you are currently on v1 branch of GoFlow. v1

Vladimir Sibirov 1.5k Dec 30, 2022
Simple interface to libmagic for Go Programming Language

File Magic in Go Introduction Provides simple interface to libmagic for Go Programming Language. Table of Contents Contributing Versioning Author Copy

Krzysztof Wilczyński 12 Dec 22, 2021
A library for parallel programming in Go

pargo A library for parallel programming in Go Package pargo provides functions and data structures for expressing parallel algorithms. While Go is pr

null 180 Nov 28, 2022
The Gorilla Programming Language

Gorilla Programming Language Gorilla is a tiny, dynamically typed, multi-engine programming language It has flexible syntax, a compiler, as well as an

null 29 Apr 16, 2022
Elastic is an Elasticsearch client for the Go programming language.

Elastic is an Elasticsearch client for the Go programming language.

Oliver Eilhard 7.1k Jan 9, 2023
👩🏼‍💻A simple compiled programming language

The language is written in Go and the target language is C. The built-in library is written in C too

paco 28 Nov 29, 2022
Examples on different options for implementing Flow Based Programming

Flow Based Programming This repository contains fragments and ideas related to Flow Based Programming. It shows different ways of implementing differe

Egon Elbre 10 Sep 22, 2022
Lithia is an experimental functional programming language with an implicit but strong and dynamic type system.

Lithia is an experimental functional programming language with an implicit but strong and dynamic type system. Lithia is designed around a few core concepts in mind all language features contribute to.

Valentin Knabel 9 Dec 24, 2022
accessor methods generator for Go programming language

accessory accessory is an accessor generator for Go programming language. What is accessory? Accessory is a tool that generates accessor methods from

masaushi 8 Nov 15, 2022
Http web frame with Go Programming Language

Http web frame with Go Programming Language

jk 0 Oct 17, 2021
A modern programming language written in Golang.

MangoScript A modern programming language written in Golang. Here is what I want MangoScript to look like: struct Rectangle { width: number he

PlebusSupremus1234 3 Nov 12, 2021