Psi Programming Language (Classic)
2018-05-05
A programming language focused on safe, but dynamic programming.
Table of Contents
Magic at your fingertips!
Psi was a programming language experiment I did in 2018, where I was kinda unhappy with C++ and C# and wanted something that had a similar power level, but less abstraction work necessary.
Design
The language was designed to be easily typable, super orthogonal in all features and programmer-oriented. The goals were:
- static typing
- flexible, but strict type system
- closure types
- standard library
- type inference
- well defined implicit conversions
/***************************************************
* Example using some features of the Psi language. *
***************************************************/
import std;
import std.io;
const main = fn()
{
var I : int;
// Using an 'out' parameter
set(I, 42);
while(I > limit())
{
I = I - 1;
}
// Create a closure
var print = fn()
{
print("I = ");
print(I);
print("\n");
};
// Call the local print variable
print();
};
const set = fn(out dst : int, src : int)
{
dst = src;
};
const limit = fn() -> int
{
return 38;
};
Functions were just values, and option types looked pretty similar to what rust has now. Psi also featured reference semantics like in Java/.NET, but also raw pointer semantics, as well as enum literals. Another thing that was meant to be was a comptime
system like Zig has.
Examples
Typed enums:
type weight_unit = enum<real>(gram = 0.001, kilogram = 1.0, ton = 1000.0);
var amountOfFlour = 250.0 * :gram; // will result in 0.25
Complex numbers:
module complex
{
type imag = record(real : real, imag : imag);
const I = imag(real: 0, imag: 1);
}
Extension methods:
const print = fn(this I : int, j : int)
{
io.write(I, ",", j, "\n");
}
const print = fn(this p : Point)
{
io.write("(", p.x, ",", p.y, ")\n");
}
var pt = Point(x = 10, y = 20);
pt.print(); // Prints "(10,20)\n"
print(pt); // Prints exactly the same
5.print(6); // Prints "5,6\n"
Reference semantics:
var foo : ref<Point> = new Point(x = 10);
var bar : ref<Point> = new Point(x = 20);
foo = bar; # copy reference
foo = new Point(x = 40); # assign new reference
foo := Point(x = 30); # assign new value
foo := bar'value; # copy value
foo := bar; # copy value with use of coercion
var baz : Point = bar; # copy value with coercion
print(foo'value.x); # access value member
print(foo.x); # use dot-member propagation
Lambda expressions:
const apply = fn(this list : array<real>, f : fn(x:real) -> real) -> array<real>
{
var res : array<real> = array<real>(list.length);
for(I in list'range)
res[I] = f(list[I]);
return res;
}
var foo = [1.0, 2.0, 3.0].apply(\x => 2 * x);
Option type:
type ios = option<string,int>;
var val : ios = "Hello, World!";
switch(val.type)
{
case string:
print("s = %1".arg(string(val)));
break;
case int:
print("I = %1".arg(int(val)));
break;
others:
error "Not supported!";
}
Abandonment
When I discovered Zig, I figured that most of the projects goals aligned perfectly with my own goals, but the approach was somewhat different.
I started using Zig more and more and at one point, I wasn’t really interested in finishing Psi anymore.
As the project never really took of, I adopted the name of the language for the programming language I want to have on the Ashet Home Computer. It will be in a Zig-like fashion, so syntactically very similar, but semantically even simpler.
But I honestly like the design I made and maybe at one point in the future, I’ll adapt it again under a new name, with better designed semantics and less weird properties like polymorphic variables.