Oh man, I am excited to try to answer this question as best I can. I hope I can get my thoughts properly in order.
As @Doval mentioned and the questioner pointed out (albeit rudely), you do not really have a type system. You have a system of dynamic checks using tags, which is in general much weaker, and also much less interesting.
The question of "what is a type system" can be quite philosophical, and we could fill a book with different viewpoints on the matter. However, as this is a site for programmers, I'll try to keep my answer as practical as possible (and really, types are extremely practical in programming, despite what some may think).
Overview
Let's start with a seat-of-the-pants understand of what a type system is good for, before diving into the more formal underpinnings. A type system imposes structure on our programs. They tell us how we may plug various functions and expression together. Without structure, programs are untenable and wildly complex, ready to cause harm at the slightest mistake of the programmer.
Writing programs with a type system is like driving a care in mint condition - the brakes work, the doors close safely, the engine is oiled, etc. Writing programs without a type system is like riding a motor cycle without a helmet and with wheels made out of spaghetti. You have absolutely no control over your.
To ground the discussion, let's say we have a language with literal expression num[n]
and str[s]
that represent the numeral n and the string s, respectively, and primitive functions plus
and concat
, with the intended meaning. Clearly, you don't want to be able to write something like plus "hello" "world"
or concat 2 4
. But how can we prevent this? A priori, there is no method to distinguish the numeral 2 from the string literal "world". What we would like to say is that these expressions should be used in different contexts; they have different types.
Languages and Types
Let's step back a bit: what is a programming language? In general, we can divide a programming language into two layers: the syntax and the semantics. These are also called the statics and the dynamics, respectively. It turns out that the type system is necessary to mediate the interaction among these two parts.
Syntax
A program is a tree. Don't be fooled by the lines of text you write on a computer; these are just the human-readable representations of a program. The program itself is an Abstract Syntax Tree. For example, in C we might write:
int square(int x) {
return x * x;
}
That is the concrete syntax for the program (fragment). The tree representation is:
function square
/ | \
int int x return
|
times
/ \
x x
A programming language provides a grammar defining the valid trees of that language (either concrete or abstract syntax may be used). This is usually done using something like BNF notation. I would assume you've done this for the language you've created.
Semantics
OK, we know what a program is, but it's just a static tree structure. Presumably, we want our program to actually compute something. We need semantics.
Semantics of programming languages is a rich field of study. Broadly speaking, there are two approaches: denotational semantics and operational semantics. Denotational semantics describes a program by mapping it into some underlying mathematical structure (e.g. the natural numbers, continuous functions, etc). that provides meaning to our program. Operational semantics, on the contrary, defines a program by detailing how it executes. In my opinion, operational semantics are more intuitive to programmers (including myself), so let's stick with that.
I won't go through how to define a formal operational semantics (the details are a bit involved), but basically, we want rules like the following:
num[n]
is a value
str[s]
is a value
- If
num[n1]
and num[n2]
evaluate to the integers n_1$ and $n_2$, then
plus(num[n1], num[n2])` evaluates to the integer $n_1 + n_2$.
- If
str[s1]
and str[s2]
evaluates to the strings s1 and s2, then concat(str[s1], str[s2])
evaluates to the string s1s2.
Etc. The rules are in practice a lot more formal, but you get the gist. However, we soon run into a problem. What happens when we write the following:
concat(num[5], str[hello])
Hm. This is quite a conundrum. We have not defined a rule anywhere for how to concatenate a number with a string. We could attempt to create such a rule, but we intuitively know that this operation is meaningless. We don't want this program to be valid. And thus we are led inexorably to types.
Types
A program is a tree as defined by a language's grammar. Programs are given meaning by execution rules. But some programs are unable to be executed; that is, some programs are meaningless. These programs are ill-typed. Thus, typing characterizes meaningful programs in a language. If a program is well-typed, we can execute it.
Let's give some examples. Again, as with the evaluation rules, I will present typing rules informally, but they can be made rigorous. Here are some rules:
- A token of the form
num[n]
has type nat
.
- A token of the form
str[s]
has type str
.
- If expression
e1
has type nat
and expression e2
has type nat
, then the expression plus(e1, e2)
has type nat
.
- If expression
e1
has type str
and expression e2
has type str
, then expression concat(e1, e2)
has type str
.
Thus, according to these rules, there is plus(num[5], num[2])
is has type nat
, but we cannot assign a type to plus(num[5], str["hello"])
. We say a program (or expression) is well-typed if we can assign it any type, and it is ill-typed otherwise. A type system is sound if all well-typed programs can be executed. Haskell is sound; C is not.
Conclusion
There are other views on types. Types in some sense correspondg to intuitionistic logic, and they can also be viewed as objects in category theory. Understanding these connections is fascinating, but it is not essential if one merely wants to write or even design a programming language. However, understanding types as a tool for controlling program formations is essential to programming language design, and development. I have only scratched the surface of what types can express. I hope you think they are worthwhile enough to incorporate into your language.