
Я не могу решить, что a.size или size(a) правильнее при получении длины массива. Когда я пишу Leaf, у меня постоянно возникает ощущение, что, возможно, a.size неверен. Как правильно получить мета-свойства объекта?
Другие примеры
Рассмотрение только size приведет к предвзятости обсуждения, поэтому давайте перечислим несколько других общих метаподобных свойств:
a.typevs.type(a)getHashCode(a)vs.a.getHashCode()a.countvscount(a)toString(a)vsa.toString()a.clone()vsclone(a)
Для контраста давайте выберем пару сценариев, в которых явно предпочтительнее один синтаксис. Рассмотрим простую структуру:
type point : [ x : integer, y : integer ]
a.x кажется правильным способом доступа к полю x. x(a) кажется очень неправильным. Разница в том, что x и y являются фактическими свойствами point, а не мета-свойствами. Сложность определения мета-свойства является частью проблемы.
Я не могу придумать прекрасной ситуации, когда мета-свойство определенно лучше выглядит как функция, возможно, получающая адрес переменной.
a.address чувствует себя не так. Тем не менее, в C++ у нас на самом деле есть get_shared_from_this() функций-членов, а также некоторые проекты с шаблонными операторами приведения, определенными для их типов (например, сам исходный код Leaf). Граница между свойством, мета-свойством и оператором довольно размыта.
Что такое мета-свойство
Сводится ли это только к этому вопросу: что такое мета-свойство? Я склонен сказать, что только истинные свойства должны иметь синтаксис свойства pt.x, а все мета-свойства должны использовать синтаксис функции getHashCode(pt). Однако мы не можем установить это правило, если не можем определить четкое различие между свойством и мета-свойством.
Свойство — это то, что по своей сути является частью ценности. Если бы свойство было удалено, значение потеряло бы часть своего значения, оно было бы неполным. Вот почему x является свойством point: если мы удалим x, то у нас просто больше не будет point.
Мета-свойство — это то, что является внешним по отношению к значению. Интерпретация значения никак не зависит от этого метасвойства. Интерпретация point не зависит от его адреса, хэш-кода или формата строки. Это все мета-свойства.
В Leaf я уже использую термины "внутренний" и "внешний" в системе типов. Они применяются к определениям типов, но их значение практически такое же, как здесь для свойств.
Таким образом, правильность a.size или size(a) зависит от того, является ли size внутренним или внешним по отношению к значению. Для массива или любой коллекции это часть внутреннего значения. Коллекция не может существовать без размера. a.size кажется правильной формой.
Проблема ООП
Одна из причин, по которой типы свойств смешиваются, связана с тем, как работает ООП, используя классы и виртуальное наследование. Согласно моему определению, getHashCode является мета-свойством, поэтому должен иметь синтаксис getHashCode(a). Тем не менее, важно, чтобы реализация getHashCode могла быть переопределена для каждого типа: она должна быть полиморфной функцией.
Общим подходом к полиморфным функциям является virtual функция-член. Это заставляет getHashCode стать реальным свойством объекта, а не мета-свойством.
Второй подход — перегрузка функций на основе типа. Это работает только для несвязанных типов; иерархия типов по-прежнему нуждается в виртуальном подходе.
Возможно, мы просто декорируем метафункции и свойства, чтобы изменить синтаксис вызова:
[extrinsic] defn getHashCode = function() { ... }
Мы по-прежнему определяем их, как если бы они были обычными виртуальными функциями, но декорация extrinsic говорит, что они должны вызываться только как getHashCode(a), а не a.getHashCode(). Для меня это похоже на взлом, а не на четкое решение.
Я бы предпочел первоклассную поддержку внешних полиморфных функций. Где-то должна быть какая-то реализация этого, но я не могу найти. Кто-нибудь знает язык, который позволяет это?
Первоначально опубликовано на mortoray.com 14 марта 2017 г.