|
Re: What is the type of 42?
|
Posted: Oct 26, 2006 12:38 PM
|
|
This is the age old breaking the recursion problem. You must decide what you want to use as a primitive type. Something that can't be broken down anymore. SmallTalk uses built-in objects for their numbers. C/C++,Pascal,Lisp uses primitive types (or literals).
There are two forces at work here. In C++ for example, there are two type systems. One for functions and one for classes. What you're doing is trying to combine both execution types and data types together. It's logical that you would encounter this problem (if I understand everything correctly).
In C++, class A and class B are two different types even though their composition may be the same. Yet they are both classes (in Java this would be Class).
But functions in C++ aren't like that. Two functions have the same type if their prototypes match. The name of the prototype, if any, doesn't come into the equation. It is composition based while classes are name based.
So my question to you is do you use duck typing as in C++ functions? Or do you use static typing as in C++ classes?
There's a solution to both scenarios, even when merged. From past comments, I will assume you are using duck typing. That a function that has type (int)->(int) is the same type as another function (int)->(int) regardless if you name these types differently. Their values, the functions in question, are different, but their types are the same.
If you only had ONE kind of primitive type 'int' (without float's for example), none of this is a problem. But we must first resolve what is the definition of this primitive type. You seem to have built your system on the concept that "everything is a function". If 42 is a function, then is an int the function itself or the "thing" it returns? If you allow both definitions, I have a feeling you will regret it. I could not come to terms with this in my own system and had to ditch the idea completely.
I'll tell you what I did and you can decide for yourself. In my system, I have somewhat of a "everything is a component" system. My system has no concept of functions (good riddance), but components can take input and produces output values (they all "execute" in parallel though). My problem became about how to define a value. Naturally, we think that a component should return or produce this value. As I said, this did not work for me. I threw that idea out.
I decided that the component *itself* was the value. It takes no input and produces no output, yet the internals contains a value, namely 42 as per your example. This internal 42 has no type, no value and only exists for the compiler's sake. In your case, the function called 42 would take no input and produce no output, yet would still be a function and 'contain' 42 in some manner.
define 42 {} 42: ()->() int: ()->()
So 42 is both a function and an int which simply resolve to ()->(). So you can say that 42 is a different instance than 5, yet both are of the same type ()->(). We can also rest assured that no normal function will accept nothing and return nothing as that would be pointless if it has no side-effects. This means we can easily tell the difference between primitive types and normal functions. I consider this an important feature. ()->(int) isn't obvious at all.
But what if we have multiple primitive types? If you don't support duck typing and you have a system where types remember their names, it's automatic. This is the class A is different than class B scenario.
But if you have duck typing like functions in C++ where two different functions having the same prototype are of the same type, then you must qualify ()->(). One example could be this.
int: ()->()#int float: ()->()#float
They are both empty functions, but have different qualifiers which makes them different. You're trying to use the return value as the qualifier with ()->(int) and end up with recursion. I personally don't see that as a viable solution.
These qualifiers can be ditched at compile time if no longer needed at runtime so that only the raw values and code remains. The cool thing about qualifiers is that you can insert new primitive types at any time and while old compilers won't be able to generate code for those types, they will still be able to check if the program is "correct" typewise.
The qualifier can also be used for normal functions if you so choose. If you want a function to only operate on function x as input, then you have a way to specify that by defining the type of function x with a qualifier. You'll have to worry about collisions, but that's another discussion. So duck typing would have no qualifiers, whereas static typing would.
My system is much more elaborate, but I hope I'm in the ballpark of what you're talking about. Sorry for being so verbose.
|
|