A class is any construct that can:

- expose multiple function (implementations) as a single unit

- be chosen at runtime as a representation of a contract (polymorphism)

- (optionally) encapsulate some state accessible only to the functions of the class

This construct comes up naturally in a wide variety of programming tasks, mainly related to the large-scale organization of code.

Here are some examples of language constructs that are classes (perhaps by other names):

- `class` in C++, Java, C#, OCaml etc.; polymorphism achieved through interfaces/pure-virtual classes

- `class` in Python, Ruby; polymorphism achieved through dynamic dispatch

- `defmethod`+`defclass` in Common Lisp CLOS; polymorphism achieved through `defgeneric` (a rare example where polymorphism is at the single function level, instead of a bundle of functions)

- `structure`&functors in SML; polymorphism is achieved through `signature`

- `module` in OCaml; polymorphism achieved through the use of `module types`

- `struct` in Go; polymorphism achieved through use of `interface`

- `struct` in Rust; polymorphism achieved through `trait`

- `data` in Haskell; polymorhpism achieved through `class` and `instance`

- `typename` in the C++ template meta-language; polymorphism achieved through dynamic typing (SFINAE) or `concepts`

Now, some of these languages/constructs may have additional properties. Some support subtyping, some support multiple fields etc. Some explicitly implement interfaces, some implement them implicitly. But they all have the initial three properties in common, and recognizably solving the exact same problem in slightly different ways.

The implementation varies quite significantly, but I would be surprised if even one of these languages actually implements this construct using closures.

Now, to address some of the other points:

> If you're not using function pointers then you don't have runtime polymorphism, what do you think a vtable is?

I have often seen proposals to implement runtime dispatch via something like:

struct Obj {
foo: fn () -> int
bar: fn int -> int
field1: int
}

This is wildly inefficient compared to the vtable approach, which is closer to

struct ClassA { //vtable
foo: fn () -> int
bar: fn int -> int
}
struct Obj {
vtable: &ClassA
field1: int
}

> But you don't need a specific concept of an interface, it's just a record of closures.

If your language doesn't have a concept like Interface to begin with, you can't implement it out of thin air. There are few languages that don't have this. C is perhaps the only mainstream example, and indeed in C (even if you had closures) you couldn't return an object from a closure that is not a specific concrete type; or void* and expect people to cast to something else.

If your language does have interfaces, it will also have a better way to define classes corresponding to those interfaces than random closures returning interface types.

> I'm not arguing ergonomics, just implementation details, you can add all the syntax sugar you want

I've mentioned this before, but I don't know of a single language where the class mechanism is actually implemented in terms of closures. Typically there are other specialized constructs at the compiler level used specifically for this.