Я создаю модуль, который имеет довольно сильно вложенные хэши.Хеш должен быть полурегулярно модифицирован модулем, который, к сожалению, исключает использование Map
.
Как правило, ветвь вложенного хэша будет возвращена пользователям модуля [1], исамое простое, что нужно сделать, это просто вернуть этот вложенный хэш, например:
return %data{$branch}{$subbranch}
# ↪︎ %(subsubbranch1 => ... , subsubbranch2 => ... )
Однако природа контейнеров, таких как массивы или хэши, заключается в том, что, хотя вы можете сделать их доступными только для чтения, ключ / значения все еще могутбыть изменены.Тем не менее, пользователи модуля не должны изменять эти значения по ряду причин.Принуждение к Map
не поможет, потому что, если любое из значений также является контейнерами, они тоже будут модифицируемыми.
Моей первой мыслью было создание подкласса Hash
(или иным образом создание пользовательского Associative
), но автовификация по умолчанию все еще идет в Hash.Это, однако, может быть легко решено путем переопределения как AT-KEY
, так и ASSIGN-KEY
, так что AT-KEY
возвращает экземпляр подкласса, если ключ еще не существует:
class ProtectedHash is Hash {
has %!hash = ();
method EXISTS-KEY ($key) { %!hash{$key}:exists }
method ASSIGN-KEY ($key, \value) { %!hash{$key} = value }
method AT-KEY ($key) {
%!hash{$key} := ProtectedHash.new unless %!hash{$key}:exists;
%!hash{$key};
}
}
Что яя хотел бы сделать это, если ASSIGN-KEY
(или часть автовивификации AT-KEY
) вызывается из вне моего модуля.Я думал об использовании что-то вроде $? MODULE, но это будет установлено во время компиляции и всегда будет правдой.Похоже, я могу немного отключиться от Backtrace и проверить имя файла, который вызвал, но насколько последовательным я могу предположить трассировку вызова для этих двух функций?
Например, для ASSIGN-KEY
у меня есть:
method ASSIGN-KEY ($key, \value) {
my @trace = Backtrace.new.list[3..*];
# The first three can be ignored:
# 0: code at ...Backtrace.pm6
# 1: method new at ...Backtrace.pm6
# 2: method AT-KEY at ...ThisFile.pm6
if/unless ??? {
%!hash{$key} = value
}
}
AT-KEY
обычно вызывается суб postcircumfix<{ }>
(в этом случае @trace[0]
можно игнорировать,и trace[1]
будет представлять интерес), но также может быть, хотя и редко, вызываться напрямую, и в этом случае trace[0]
- это место, где я хотел бы проверить имя файла.
Существуют ли другиеобщие способы, которыми можно назвать AT-KEY
или ASSIGN-KEY
?Или следует проверить, что эти два шага составляют 99,9% обращений к этим методам?[2]
[1] Есть только несколько веток subx4, которыми пользователь может манипулировать, и поэтому я считаю, что лучше предоставить им обязательно более медленный метод .Hash
, когдаим это действительно нужно, чем предполагать, что им всегда нужен манипулируемый контейнер.Иногда их можно вызвать достаточно (в частности, с помощью шаблона get-branch($foo){$subbranch}{$subsubbranch}
), что дополнительные издержки при создании глубокого клона хэша становятся достаточно последовательными.
[2] Я не слишком обеспокоен предотвращением ЛЮБОГО доступа (хотяМне, конечно, любопытно, возможно ли это исключительно через подклассы), потому что я уверен, что довольно трудолюбивый кодер всегда может что-то выяснить, но я бы хотел воспользоваться наиболее распространенными из них, чтобы сказать: «Не могу коснутьсяэтот!"(прослушайте музыку 90-х годов ...) и предоставьте сообщение об ошибке Awesome .