Skip to content

Instantly share code, notes, and snippets.

@FunctionDJ
Last active November 22, 2025 15:17
Show Gist options
  • Select an option

  • Save FunctionDJ/7e279ba44123500577399fd4a4893ab6 to your computer and use it in GitHub Desktop.

Select an option

Save FunctionDJ/7e279ba44123500577399fd4a4893ab6 to your computer and use it in GitHub Desktop.
fun language

fun

Ideas for a programming language similar to TypeScript that compiles to JavaScript. Somewhat of an inversion of DreamBerd.

Strict comparison only, no truthy/falsy

// === and !== don't exist
"1" == 1 // TypeError
if (someString) {} // TypeError, must use someString != ""

Immutable variables by default, explicit mutability

inspired by Rust

const x = 1 // ❌
let x = 1 // ✅
x++ // ❌
let mut y = 1
y++ // ✅
var z = 1 // ❌

Object literals are frozen by default (includes arrays)

let myObj = { bar: 1 }
myObj.bar++ // ❌

let myObj2 = mut { bar: 1 }
myObj2.bar++ // ✅
myObj2 = {} // ❌ only properties are reassignable, since variable is not mut

No semicolons, method calling parentheses and array indexing must not have a gap

Maybe there are other cases in JS that need the no-gap rule in fun to get rid of semicolons

let x = 0; // ❌
let someValue = myArray
	[0] // interpreted as an array literal, not an array accessor
someFunction
( // will be interpreted as an expression wrapper (e.g. IIFE)
	someArgument
)

No hoisting

let x = foo() // ❌
fn foo() => "hi"

Arrow functions always have implicit return and must be single-line, named functions use fn keyword

let myFunction = () =>
	otherFunction(
		evenAnotherFunction(
			longComplicatedValue
		)
	) // ❌

fn myFunction() {
	return otherFunction(
		evenAnotherFunction(
			longComplicatedValue
		)
	)
} // ✅

Errorful function signatures

inspired by Java and Effect

fn foo() => JSON.parse(someString)
// ^ () => unknown, throws JsonParseError

Simple error mapping and error literals

fn parseUserJson(someString: string) {
	return JSON.parse(someString)
		.catch(JsonParseError, new UserJsonParseError)
					// ^ UserJsonParseError has never been created before (one-off),
					// must not collide with any other symbol
}
// ^ (someString: string) => unknown, throws UserJsonParseError

Type-safe error handling

fn foo() {
	let fileData = readFileSync(...)
	let networkResponse = fetchSomethingSync(...)
}
// ^ foo() => void, throws FSReadError, FetchNetworkError

fn bar() {
	let _, error = foo()
	catch (error, FSReadError) {
		console.log(error.details)
	}
}
// ^ bar() => void, throws FetchNetworkError

Types that are not inferred or derived are runtime schemas

fn strHasLength16orMore(str: string) => str.length >= 16
fn strHasNumbers(str: string) => str.match(/\d/) !== null

interface SignupDTO {
	password: string([strHasLength16orMore, strHasNumbers]) // list of validators
}

fn signup(requestBodyJson: unknown) {
	let { password } = SignupDTO.parse(requestBodyJson)
	//    ^ string
}
// ^ signup(requestBodyJson: unknown) => void, throws SchemaError

Platform-specific globals must be access through a single global

document.createElement() // ❌
navigation.back() // ❌
window.document.createElement() // ✅ (this will get me cancelled)

HOF shorthand

inspired by Java method references

foo.filter(x => x.bar)
foo.filter(x => x.quux())

// equivalent
foo.filter(::bar)
foo.filter(::quux())

Ternaries can't be nested

let foo = cond1 ? (cond2 ? "a" : "b") : "c" // ❌

Named function arguments must be used if signature has >=2 arguments

fn foo(width: number, height: number) => ...
foo(8, 2) // ❌
foo(height: 2, width: 8) // ✅

Modules

inspired by Python

Import variant 1: Import a named export (no side-effects allowed)

from "package" import Foo, Bar as MyRenamedBar // default exports dont exist

Import variant 2: Namespace import (no side-effects allowed)

import "foo" // exports function "bar"
foo.bar()
import "package" as MyRenamedPackage
MyRenamedPackage.Foo

Import variant 3: Side-effects

import effect "someFile.css" // discouraged, should be solved with a custom loader to create a `<link>` element for frontend stuff

Functions with side-effects are called with #

This might be impossible to implement because something as innocent as a network request to get some data changes a lot in the overall system. The idea is more targeted towards having no practical consequences for the program state, like modifying variables.

let myModule = {
  noSideEffect: () => "hi",
  fn sideEffect() {
    console#log("bye")
  }
}

myModule.noSideEffect()
myModule#sideEffect()

To do

  • remove continue?
  • remove labelled statements? (for etc)
  • replace switch with match (if keeping switch, enforce break and remove it as keyword for switch)
  • make if-else syntactic sugar for ternary and remove a ? b : c keywords?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment