Ваш сопоставитель создан как побочный эффект в isDefined
.Передача побочных эффектов в рутину, такую как map
, обычно является причиной катастрофы, но это даже не то, что происходит здесь.Ваш код требует, чтобы isDefined
был вызван непосредственно перед apply
с тем же аргументом.Это делает ваш код очень хрупким, и это то, что вы должны изменить.
Клиенты PartialFunction
вообще не обязаны следовать этому протоколу.Представьте себе, например,
if (f.isDefinedAt(x) && f.isDefinedAt(y)) {fx = f(x); fy = f(y)}.
А здесь код, который вызывает apply
, даже не ваш, а классы коллекции, так что вы не контролируете, что происходит.
Ваша конкретная проблема вgetLinkNew
означает, что isDefined
просто никогда не вызывается. Аргумент PartialFunction
для collectFirst
равен {case m => ...}
.isDefined
, который вызывается, является isDefined
этой функции.Поскольку m
является неопровержимым шаблоном, это всегда верно, и collectFirst всегда будет возвращать первый элемент, если он есть.То, что частичная функция возвращает другую частичную функцию (Mapping
), которая не определена на m
, не имеет значения.
Редактировать - Возможный обходной путь
Очень легким изменением было бы проверить, доступен ли matcher
, и создать его, если его нет.Лучше сохраните строку entity
, которая также использовалась для его создания, чтобы вы могли проверить, является ли она правильной.Это должно сделать побочный эффект мягким, если нет многопоточности.Но, кстати, не используйте null
, используйте Option
, поэтому компилятор не позволит вам игнорировать вероятность того, что это может быть None
.
var _currentMatcher : Option[(String, Matcher)] = None
def currentMatcher(entity: String) : Matcher = _currentMatcher match{
case Some(e,m) if e == entity => m
case _ => {
_currentMatcher = (entity, pattern.matcher(entity))
_currentmatcher._2
}
}
Редактировать снова.Глупый меня
Извините, так называемый обходной путь действительно делает класс более безопасным, но это не делает решение collectFirst
работать.Опять же, частичная функция case m =>
всегда определяется (примечание: entity
даже не появляется в вашем коде getLinkNew
, что должно беспокоить).Проблема заключается в том, что для NodeSeq потребуется PartialFunction (не для объекта, который будет известен функции, но не будет передан в качестве аргумента).Будет вызвано isDefined, затем применить.Шаблон и сопоставитель зависят от NodeSeq, поэтому их нельзя создать заранее, а только в isDefined и / или apply.В том же духе вы можете кэшировать то, что вычисляется в isDefined для повторного использования в Apply.Это определенно не красиво
def linkFor(entity: String) = new PartialFunction[NodeSeq, String] {
var _matcher : Option[String, Matcher] = None
def matcher(regexp: String) = _matcher match {
case Some(r, m) where r == regexp => m
case None => {
val pattern = Pattern.compile(regexp)
_matcher = (regexp, pattern.matcher(entity))
_matcher._2
}
}
def isDefined(mapping: NodeSeq) = {
matcher(mapping \ "match" text).matches
}
def apply(mapping: NodeSeq) = {
// call matcher(...), it is likely to reuse previous matcher, build result
}
}
Вы используете это с (config \ mapping).collectFirst(linkFor(entity))