I'm going to stop you right there, because plenty of languages (especially functional ones) have ways of declaring custom complex types without using classes.
I'm not too experienced with this though, so this is pretty much the extent of my knowledge on this topic.
struct PositiveInteger(u32);
Then give it a constructor (which in rust is just a regular static function) that checks the non-zero variant. You can define method on this type, and make it implement traits (which are kind of like interfaces).The best bit: there is zero runtime cost to this, the memory-representation of this type is identical to that of the underlying u32.
Rust-style enums which can contain data, and also have method implemented on them are even better. Doesn't mean classes (structs in Rust) aren't useful, but once you use a language that allows you to define other kinds of custom types, they seem very restrictive when they're the only available tool.
Having the functions that operate on the struct attached directly to the struct declaration, vs having some functions that the first parameter is the struct on which the function operates, doesn't seem like a particularly meaningful distinction to me. OK, you like C-style programming in favor of C++-style programming, congrats. It's still a class either way.
Enums are are the better example of non-class types. For example, you can have:
enum StringOrInt {
String(String),
Int(u32),
}
And you can go ahead and implement methods on that type. Classes have "AND-state", not "OR-state". But a Type in general can have either kind of state.With design by contract you can put in whatever fancy constraints you want on function parameters and return values, and those will be enforced.
As far as objects go, they're much more useful for me as just a means of passing state. Rather than using a bunch of global variables or having to pass in a ton of function arguments, I can just use an object which contains all the state I need.
Of course, having lots of state can usher in its own set of problems, and there's something to be said for trying to make your code as stateless as possible. But sometimes you need state, and maybe even a lot of it.
I'm not an Ada expert, but it has excellent support for range-restricted integer types.[0]
Ada's 'discriminated types' are also fun. They let you create members which only exist when they're applicable. [1]
> Do they also allow you to define your own operations on those types
Looks like Ada supports operator overloading, yes. [2]
[0] https://www.adaic.org/resources/add_content/standards/05rm/h...
[1] https://www.adaic.org/resources/add_content/standards/05rat/...
[2] https://www.adaic.org/resources/add_content/standards/05aarm...
interface DuckTypedObject {
quack: true
bark?: false
eyes: 'beady'
}
This will require that any object used as a DuckTypedObject must have the `quack` and `eyes` properties and may optionally have a `bark` of `false`, but doesn't otherwise prescribe what the object actually has to be.However it is worth keeping the following in mind. The compiler will check that in your code that these properties are present and assigned correctly. However at runtime nothing is guaranteed especially when dealing with the DOM.
One of the things that I don't like about TypeScript (I have written a fair bit of it) is that you believe you have type safety when it is really type hinting.
function someFunction(obj?:DuckType) {
//Snip other logic
someOtherFunction(obj);
}
function someOtherFunction(obj:DuckType) {
}
The type checker catch that. So you still have to do: function someFunction(obj?:DuckType) {
obj = obj || { /* set some object properties */
//Snip other logic
someOtherFunction(obj);
}
function someOtherFunction(obj:DuckType) {
}
I have found plenty of examples where people haven't set a default value because the compiler hasn't flagged anything wrong with the code and you get an uncaught reference error.[1] https://news.ycombinator.com/item?id=21476261 [2] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
I dislike this example. The numeric systems of every programming language I've ever used has been (more or less) terrible, precisely because there are extremely common and simple arithmetic types, just like this, which it's terrible at representing. Half of the "modern" languages I've seen just provide the thinnest possible wrapper around the hardware registers ("Int32"!).
(What if I need to accept an integer in the range 5 < x < 10 instead? Am I supposed to define a new class for every range?)
Instead of saying we need a system of user-definable classes so every user can fix the numerics on their own, for each program they write, I'd say we should fix our numeric systems to support these common cases, and then re-evaluate whether we really need classes.
Are there non-numeric analogues to this type of value restriction? Maybe. It doesn't seem like a very common one, but it is an interesting one. Perhaps what we really want is "define type B = type A + restriction { invariant X }". I can't think of any examples offhand in my own work, but that could be because I haven't had that language feature so I wasn't looking for places to apply it.
One being the idea of types as describing shape of data. In the best of cases perhaps some semantics tied to the data (how the bits are to be interpreted)
Than there is the the other view, the Curry-Howard one. Where types describes proofs and invariants of the program it self, and how interesting properties of program compositions can be ensured.
It seems much time is wasted when people holding one perspective debates with people holding the other.
Perhaps we should have separate words for these concepts.
type MyType is range min .. max
There's already a built-in for positive integers, which is defined as subtype Positive is Integer range 1 .. Integer'Last;
Note the subtype there. Ada recognizes that a positive integer is a type of integer, but not the other way around. And it enforces that in the type checking: You can pass any Positive into a function that accepts Integer, but you can't just pass an Integer into a function that accepts Positive. This happens even though they're not classes and this isn't OOP. Ada does have object-oriented constructs, but they are a later addition to the language. I have never used Ada professionally, but my understanding (based on book learning) is that it tends to be used conservatively.It's similar in OCaml. Despite the O standing for "object-oriented", creating classes isn't necessarily considered idiomatic. The other tools in the chest tend to be conceptually simpler, and therefore to be preferred when they will get the job done.
"define your own operations" is a requirement I'm having a hard time making sense of. To me, that is just another way of saying, "define functions", which is a feature of every language I've used except for one really ancient dialect of BASIC.
Every so often, I wonder if I should spend some time with Pascal.
Watch out, you might make topmind mad! https://wiki.c2.com/?TopMind