Оставляя в стороне WB (который на самом деле не нужен для ответа на ваш вопрос) - проблема, кажется, имеет простой ответ, основанный только на том, как выражения оцениваются во время заданий.Вот пример:
In[1505]:=
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[1509]:= g/:cccQ[g[x0_]]:=True
During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted
Чтобы это сработало, я специально сделал определение для notGoodQ
, чтобы он всегда возвращал True
.Теперь, почему g[x0_]
оценивался во время задания через TagSetDelayed
?Ответ таков: хотя TagSetDelayed
(а также SetDelayed
) в назначении h/:f[h[elem1,...,elemn]]:=...
не применяет никаких правил, которые может иметь f
, оно будет оценивать h[elem1,...,elem2]
, а также f
.Вот пример:
In[1513]:=
ClearAll[h,f];
h[___]:=Print["Evaluated"];
In[1515]:= h/:f[h[1,2]]:=3
During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed
Тот факт, что TagSetDelayed
равен HoldAll
, не означает, что он не оценивает свои аргументы - он только означает, что аргументы приходят к нему без оценки, и независимо от того, являются ли ониони будут оцениваться в зависимости от семантики TagSetDelayed
(которую я кратко описал выше).То же самое относится и к SetDelayed
, поэтому обычно используемое утверждение, что оно «не оценивает свои аргументы», не является буквально правильным.Более правильное утверждение состоит в том, что он получает аргументы, которые не были оценены, и оценивает их особым образом - не оценивает rhs, тогда как для lhs оценивает голову и элементы, но не применяет правила для головы.Чтобы избежать этого, вы можете обернуть вещи в HoldPattern
, например:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
.Вот несколько примеров использования:
In[1527]:= cccQ[g[1]]
Out[1527]= True
In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted
Обратите внимание, однако, что необходимость HoldPattern
внутри левой стороны при определении часто является признаком того, что выражение внутри вашей головы может также вычисляться во время вызова функции, который может сломать ваш код.Вот пример того, что я имею в виду:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Этот код пытается отловить такие случаи, как h[f[something]]
, но он, очевидно, завершится ошибкой, поскольку f[something]
выполнит оценку до того, как оценка достигнет h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Для меня необходимость в HoldPattern
на lhs является знаком того, что мне нужно пересмотреть мой дизайн.
РЕДАКТИРОВАТЬ
Что касается отладки во время загрузки в WB, то, что вы можете сделать (IIRC, не можете проверить прямо сейчас), это использовать старые добрые операторы print, вывод которых будет отображаться в консоли WB.Лично я редко испытываю потребность в отладчике для этой цели (отладка пакета при загрузке)
РЕДАКТИРОВАТЬ 2
В ответ на правку в вопросе:
Относительно порядка определений: да, вы можете сделать это, и это решит эту конкретную проблему.Но, как правило, это не надежно, и я не считаю это хорошим общим методом.Трудно дать определенный совет для рассматриваемого случая, поскольку он немного выходит за рамки контекста, но мне кажется, что использование UpValues
здесь неоправданно.Если это делается для обработки ошибок, есть других способов сделать это без использования UpValues
.
Обычно UpValues
чаще всего используются для безопасной перегрузки некоторой функции, без добавления какого-либо правила к перегружаемой функции.Один из советов - избегать ассоциировать UpValues
с головами, которые также имеют DownValues
и могут оценивать - тем самым вы начнете играть в игру с оценщиком и в конечном итоге проиграете.Самым безопасным является присоединение UpValues
к инертным символам (головкам, контейнерам), которые часто представляют «тип» объектов, для которых вы хотите перегрузить данную функцию.
Что касается моего комментария о наличии HoldPattern
указывает на плохой дизайн., конечно, являются законным использованием для HoldPattern
, например, это (несколько искусственное):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Здесь это оправдано, потому что во многих случаях Plus
остается неоцененным и полезно в его неоцененной форме - поскольку можно сделать вывод, что он представляет собой сумму.Нам нужно HoldPattern
здесь, потому что Plus
определено для одного аргумента, и потому что шаблон оказывается единственным аргументом (даже если он описывает в общем случае несколько аргументов) во время определения.Итак, мы используем HoldPattern
здесь, чтобы предотвратить обработку шаблона как нормального аргумента, но это в основном отличается от предполагаемых вариантов использования для Plus
.Всякий раз, когда это так (мы уверены, что определение будет работать правильно для предполагаемых случаев использования), HoldPattern
хорошо.Обратите внимание, что этот пример также хрупок:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Причина, по которой все еще в основном нормально, заключается в том, что обычно мы не используем Plus
для одного аргумента.Существует вторая группа случаев, где структура обычно предоставляемых аргументов такая же, как структура шаблонов, используемых для определения.В этом случае оценка шаблона во время назначения указывает, что такая же оценка будет происходить с фактическими аргументами во время вызовов функции.Ваше использование попадает в эту категорию.Мой комментарий по поводу ошибки проектирования был для таких случаев - вы можете помешать оценке шаблона, но вам придется также помешать оценке аргументов, чтобы эта работа работала.А сопоставление с шаблоном по отношению к не полностью оцененному выражению хрупко.Кроме того, функция никогда не должна принимать некоторые дополнительные условия (помимо того, что она может проверять тип) для аргументов.