Skip to content

Type Declarations

Michael Wang edited this page Jul 11, 2022 · 9 revisions

Primitive Types

These types form the foundation of cish's type system.

Usage Type Allowed Values Size(Bytes)
bool Boolean True/False 4
char Character ASCII/UTF-8 1
int Integer -2^63 to 2^63 8
float Float Any IEEE Double 8

Super Types

These types are allowed to have type arguments.

Usage Type Allowed Sub-Types
array<T> Array 1
proc<ReturnT, arg1T, arg2T, ...> Procedure 1...100

Note: All supertypes have a type argument limit of 100 types. That means that procedures have can have a maximum of 99 arguments, and a minimum of 1 type argument to specify it's return type.

User-Defined/"Typedefed" types.

These types fall under one of the following categories - some of which are super types that can handle type arguments. They can all be referenced by typing the user-defined type's relevant identifier in the place of an expected type. Note that cish doesn't support a typedef the same way C does.

Type Is Super Type
Structs/Records Yes
Generic Type Arguments No

Special Types

These types have their special functionality, and don't exactly conform to the same patterns normal primitive/super types support. See the section below the table for more information regarding the specifics of each special type.

Usage Type
auto Automatic Type
nothing Equivalent of C void
any Equivalent of C#/Java object
See the bottom of this page for more information/links regarding Cish generics and records/structs.

The Automatic Type

The automatic type is only allowed in the type specification of a variable declaration or the type specification of a procedure literals return type. Any usage outside the specified use-cases should invoke a TYPE_NOT_ALLOWED error. Using the auto keyword will tell the Cish compiler to assume the type of the value at the first return statement, or the set value depending on the context of it's usage.

Here are a few examples that demonstrate valid use-cases of automatic typing:

  1. Usage in Variable Declaration.
auto i = 69;

In the above example, using the auto keyword would tell Cish to assume the type of 69 for the variable i. Ultimately i's type assumes the type of int.

  1. Usage in Procedure Literals.
global proc<int, int, int> add = proc (int a, int b) return auto {
  return a + b;
}

Note that in the above example, the auto specification of the return type in the procedure we assign to the variable add assumes the type of a + b, which is an int. Note: auto cannot be used as a type-argument/subtype to any super type.

The Nothing Type

The nothing type is only allowed in the return type specification of a procedure literal, and in the first type argument (which corresponds to a procedures return type specification) of the super type, proc. The nothing type, in essence, is the equivalent of void in C and many of its descendants. When undergoing type checking with another type, at least on the top-level (expression/value evaluations vs. a match type, not the type checking of subtypes/type arguments), nothing types always invoke an error.

Here's a comprehensive example of the nothing type's usage:

global array<proc<nothing, int>> handlers = [
  proc(int i) {
    //opcode handlers for 0
  },
  proc(int i) {
    //opcode handlers for 1
  },
  proc(int i) {
    //opcode handlers for 3
  }
];

or better yet

auto myMap = new map<array<char>, proc<nothing, array<char>>> {
  hasher = dj2b;
};
mapEmplace<array<char>, proc<nothing, array<char>>>(myMap, "hello", proc(array<char> s) println(s););
mapEmplace<array<char>, proc<nothing, array<char>>>(myMap, "bye", proc(array<char> s) println("by?"););

and simply use it like:

auto handleRes = mapFind<array<char>, proc<nothing, array<char>>>(myMap, "hello");
if(handleRes is success<any>)
  dynamic_cast<success<proc<nothing, array<char>>>>(handleRes).result("hello");

Note: You must include the following libraries for the above map example to work: "stdlib/data/map.csh", "stdlib/data/std.csh", stdlib/data/io.csh".

The Any Type

The any type is allowed anywhere a type is required. Typecheking against an any type will always succeed, however using the any type as a non-any type will always fail. For example:

any uselessVar = 100;

however:

int myVar = uselessVar;

will cause a unexpected type error.

When the Any Type should be used

  • Checking whether an status is an error or a success, regardless of the result type is an excellent use case or any:
auto res = myFallibleFunc();
if(res is error<any>)
  println(dynamic_cast<error<any>>(res).msg)

In the example above, all the elements of handlers are procedures that return nothing.

Type Arguments

cish Type Arguments are complicated to the degree in which a paragraph won't do it justice. Instead, refer to the dedicated wiki page.