The meta-field dilemma: `a.size` or `size(a)`
I can’t decide whether a.size
or size(a)
is more correct when getting the length of an array. While writing Leaf I have a recurrent feeling that perhaps a.size
is wrong. What is the correct way to get meta-properties from an object?
Other examples
Considering just size
will bias the discussion, so let’s list a few other common meta-like properties:
a.type
vs.type(a)
getHashCode(a)
vs.a.getHashCode()
a.count
vscount(a)
toString(a)
vsa.toString()
a.clone()
vsclone(a)
To contrast let’s pick a couple scenarios where one syntax clearly seems preferred. Consider a simple structure:
type point : [ x : integer, y : integer ]
a.x
feels like the right way to access the x
field. x(a)
seems very wrong. The difference is that x
and y
are actual properties of point
, they aren’t meta-properties. The difficulty in defining a meta-property is part of the problem.
I can’t think of a great situation where a meta-property definitely looks better as a function, perhaps taking the address of a variable.
a.address
feels wrong. Yet, in C++ we actually have get_shared_from_this()
member functions, as well as some projects having templated casting operators defined on their types (like the Leaf source code itself). The line between a property, meta-property, and operator is quite blurry.
What is a meta-property
Does it come down to just that question: what is a meta-property? I’m inclined to say that only true properties should have a property syntax pt.x
and all meta-properties should use a function syntax getHashCode(pt)
. We can’t make this rule though unless we can define a clear distinction between property and meta-property.
A property is something that is intrinsically part of a value. If the property were removed the value would lose part of its meaning, it’d be incomplete. This is why x
is a property of point
: if we remove x
then we simply don’t have a point
anymore.
A meta-property is something that is extrinsic to the value. The interpretation of the value does not depend on this meta-property in any way. The interpretation of a point
does not depend on it’s address, it’s hash code, or how it’s formatted as a string. Those are all meta-properties.
In Leaf I’m already using the terms instrinsic and extrinsic in the type system. They apply to type definitions, but the meaning is essentially the same as here for properties.
Whether a.size
or size(a)
is correct thus depends on whether size
is intrinsic or extrinsic to the value. For an array, or any collection, this is part of the instrinsic value. The collection can’t exist without a size. a.size
appears to be the correct form.
The problem of OOP
One reason why the property types get mixed is due to how OOP works, using classes and virtual inheritance. According to my definition, getHashCode
is a meta-property, thus should have the syntax getHashCode(a)
. Yet it’s important that the implementation of getHashCode
can be overridden per type: it needs to be a polymorphic function.
The common approach to polymorphic functions is a virtual
member function. This forces getHashCode
to become a real property of the object, rather than a meta-property.
A second approach is overloading functions based on the type. This only works for unrelated types; a type hierarchy still needs the virtual approach.
It’s possible that we simply decorate meta-functions and properties to change the calling syntax:
[extrinsic] defn getHashCode = function() { ... }
We still define these as though they are normal virtual functions, but the extrinsic
decorate says they must be called only as getHashCode(a)
and not a.getHashCode()
. To me this feels like kind of a hack, not a clear solution.
I’d prefer some first-class support of out-of-line polymorphic functions. There must be some implementation of this somewhere, but I can’t find it. Does anybody know of a language that allows this?
Originally published at mortoray.com on March 14, 2017.