A modern, type safe, secure language compiled to PHP

Inspired by Rust, Zig and Kotlin, THP has a modern syntax, semantics, type system and stdlib.

union Animal {
╰╴No statement matched
Dog(String), Cat(String, Int), } val my_pet = try Pets::get_first() match my_pet case Dog(name) { print("{name} has 1 live ") } case Cat(name, lives) { print("{name} has {lives}") }

Generics & Types

Type safety is enforced at compile time. Everything has a specific type. There is no `mixed`.

You can use generics where neccesary.

Array[Int] numbers = [0, 1, 2, 3, 4, 5]
╰╴No statement matched
numbers.push("7") // Compile error: expected an Int, found a String // Runtime type checking val object = try JSON::decode(data) val name = try object.get[String]("name") // Any is available, but it's not usable without an explicit cast fun mock() -> Any { ... } // Compile error: Cannot use `Any` without an explicit cast val result = mock() // Ok val result = mock() as String
thp

Tagged unions

Make invalid state irrepresentable.
Model data in a type-safe way.
Ensure all cases are handled.

union DirEntry {
╰╴No statement matched
File(String), Dir(String), } val root = DirEntry::Dir("/") val test_file = DirEntry::File("test.txt")
thp

Pattern matching

Match on values, tuples, enums, unions, types etc.
Guards available!

match test_file
╰╴No statement matched
case DirEntry::File(filename) if !filename.starts_with(".") { print(filename) } case _ { print("A valid file was not found") }
thp

Null safety

Nulls are explicit and require handling. Types can be nullable, and they must be checked before usage.
The stdlib makes extensive use of them.

String? username = Post::get("username")
╰╴No statement matched
if username? { // username is a `String` here print("Hello, {username}") }
thp

Errors as values

Exceptions are values and don't disrupt control flow.

Errors cannot be ignored, and we have syntax sugar to ease them.

val user_id = POST::get("id")
╰╴No statement matched
val user = try User::find(user_id) catch DBException e { log_error(e.message) return page(.{}, 404) }
thp