0

I'm working on a programming language design ans I'm struggling with how I should type the concept of Class.

Let me show you some code to illustrate my problem :

class MyClass
{

}

function doSomething(item : MyClass)
{
    return ...
}

a = new MyClass
doSomething(a)

As you can see, this function takes one parameter whose type is MyClass.

Now, I also need the classes to work as first class citizens so that classes themselves could be taken as function parameters or returned.

Keeping the same logic, we can introduce a Class class, like so :

class MyClass
{

}

function doSomethingOnClass(class : Class)
{
    return ...
}

doSomethingOnClass(MyClass)

Here, the class parameter should be a class, not an instance.

What bothers me is the semantic of the Class class. Since Class is the type of all classes, it is a metaclass. We also need to assume that the type of the Class class is Class itself.

But, we some rigor and formalism, is it ok to treat classes and metaclasses the same way since they represent different levels of conceptualisation ?

The same questions could be extended to any other language element that could be treated as first class citizens (class properties ...)

  • 2
    Smalltalk does something like that - see this (it's a bit of a rabbit hole, though). – Filip Milovanović Jul 2 at 12:54
  • 1
    @FilipMilovanović I see the opposite: Smalltalk's hierarchy is so complicated because it separates classes from metaclasses, and also because of its lookup rules . With a simple unified hierarchy, which also allows properties/methods to be defined on individual objects, MyClass is an instance of Class, Class is an instance of Class, and that's that. – user253751 Jul 2 at 13:57
  • @user253751 Absolutely, I aim to make it the most simple as possible, and I think smalltalk complicates everything way too much. In fact, I'd prefer making the language not reflective to clearly separates what belongs to the language and what belongs to the code but in this case I don't see how it would be possible with the language to write a generic sort function with a such signature : sort ( data : Set<MyClass> , attr : Attribute ) with Attribute being the name of the class attribute to sort by. (Typically why I want the classes and attributes to be first-class citizens). – ibi0tux Jul 2 at 14:09
  • 1
    @ibi0tux typically, I think languages following this approach will be fully dynamic. sort calls getattr(instance, attr) to get the attribute, which fails at runtime if you passed it an attribute from a different class. Alternatively, you can do it with complex generics by representing the attribute as a function MyClass -> Sortable or have it do the comparison itself like Java's Comparator – user253751 Jul 2 at 14:47
  • I like the Python way using getattr but in a strong-typing language there's still a need to define the return type of the getattr function, and also a generic class. I would write something like getattr(instance : Generic, attr : String) : Attribute. Sounds not really neat. A such function raises another question which is : is it a good thing to write an attribute name in a string (attr) ? String values belong to data while attribute names belong to the code. In Python it's okay since attributes are string indexes of the class' __dict__ but this doesn't apply for most languages. – ibi0tux Jul 2 at 15:06
1

It's Objects all the way down

Side Note: There are varying opinions on the definition of strong-typed language, and how that may or may not interact with the definition of static-typed language.

You say

Now, I also need the classes to work as first class citizens so that classes themselves could be taken as function parameters or returned.

This is a good thought, and you should run with it. What if you were able to treat all things uniformly as Objects? Let's see where that goes.

Everything is an object. In many systems (Python, Java, etc.), there is a root class called Object. Any class inherits from this one. What if we made this as normal an object as we can? It would be an instance of some class, let's call it Type. Type is itself an object as well, and since it counts as a class it must inherit from Object. This is a circular dependency, which you'll have to resolve.

Since Type here is a class whose instances are other classes, that makes it a metaclass. It's also just an object, so you can put it into variables and pass it in and out of functions.


You also ask

[I]s it ok to treat classes and metaclasses the same way since they represent different levels of conceptualisation ?

This is fine. In the runtime view that everything is just an object, your "different levels of conceptualisation" is just an illusion. Your type checker may have a different opinion on that, which is perfectly fine.

For that matter, things like a class' attribute or method can easily be objects themselves. An Attribute could just be an object with get and set methods (that explicitly or implicitly take the instance on which to operate), while a method could be a function that (again explicitly or implicitly) takes a class instance as an argument.


In the comments, you add

I don't see how it would be possible with the language to write a generic sort function with a such signature : sort ( data : Set<MyClass> , attr : Attribute ) with Attribute being the name of the class attribute to sort by.

You should be able to get away with this by stealing the concept of Generics from other languages, and maybe inventing some syntax to define dependencies in argument lists. For example, you could do something like this

sort ( data : Set<T> , attr : T::(String, Integer) )

which would mean the first argument must be a Set containing some type T, and the second argument must be an Attribute object compatible with (but not necessarily directly on) type T, which takes a String and an Integer.

| improve this answer | |
  • Thank you ! I forgot to mention that I'd like to avoid inheritance as much as possible in the language. I consider that the type of any class is Class, but it's only a type, there's no inheritance. I also aim to have the least keywords as possible in the language, so I'd like to avoid introducing builtin class names. That's why I really like your idea of introducing alternative syntaxes to write generics (what I did already for functions actually : map ( list : List<X> , (X) : Y ) : List<Y> ). I just need to find the most convenient syntax. – ibi0tux Jul 8 at 8:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.