Использование валидации с | @| в Scalaсе

Фон

У меня есть Map[String,String] значений конфигурации. Я хочу извлечь ряд ключей и предоставить осмысленные сообщения об ошибках, если они отсутствуют. Например:

val a = Map("url"->"http://example.com", "user"->"bob", "password"->"12345")

Скажем, я хочу преобразовать это в класс case:

case class HttpConnectionParams(url:String, user:String, password: String)

Теперь я могу просто использовать цикл for для извлечения значений:

for(url <- a.get("url"); 
 user <- a.get("user"); 
 password <- a.get("password")) yield { 
 HttpConnectionParams(url,user,password) 
}

Получить Option[HttpConnectionParams]. Это красиво и чисто, за исключением случаев, когда я получаю None, тогда я не знаю, чего не хватает. Я хотел бы предоставить эту информацию.

Проверка с помощью Scalaz

Введите scalaz. Я использую версию 7.1.3.

Из того, что мне удалось собрать (хорошая ссылка здесь) Я могу использовать дизъюнкции:

for(url <- a.get("url") \/> "Url must be supplied"; 
 user <- a.get("user") \/> "Username must be supplied"; 
 password <- a.get("password") \/> "Password must be supplied") yield { 
 HttpConnectionParams(url,user,password) 
}

Это хорошо, потому что теперь я получаю сообщение об ошибке, но это ориентированный на железную дорогу, поскольку он останавливается при первом сбое. Что делать, если я хочу получить все ошибки? Позвольте использовать валидацию и аппликативный строитель (aka "| @|" ):

val result = a.get("url").toSuccess("Url must be supplied") |@|
 a.get("username").toSuccess("Username must be supplied") |@|
 a.get("password").toSuccess("Password must be supplied")
result.tupled match {
 case Success((url,user,password)) => HttpConnectionParams(url,user,password)
 case Failure(m) => println("There was a failure"+m)
}

Вопросы

Это делает то, что я ожидаю, но у меня есть некоторые вопросы об использовании:

  • Есть ли простая в использовании альтернатива скалязу для этого варианта использования? Я бы предпочел не открывать ящик пандоры и вводить скалаз, если мне это не нужно.
  • Одна из причин, почему я не хочу использовать скалаз, - это действительно очень сложно понять, что делать, если вы не знаете, как я, знаете всю структуру. Например, каков список имплицитов, которые необходимы для того, чтобы заставить вышеуказанный код работать? import scalaz._ как-то не работал у меня. [1] Как я могу понять это из документов API?
  • Есть ли более краткий способ выразить пример использования валидации? Я споткнулся, пока не пришел к чему-то, что сработало, и я понятия не имею, есть ли другие, лучшие способы сделать то же самое в сказасе.

[1] После большого беспокойства я пришел к этому набору импорта для аппликативного варианта использования. Надеюсь, это поможет кому-то:

import scalaz.std.string._
import scalaz.syntax.std.option._
import scalaz.syntax.apply._
import scalaz.Success
import scalaz.Failure
1 ответ

Вы можете сделать это немного лучше, указав вспомогательный метод и пропустив шаг .tupled, используя .apply:

import scalaz._, Scalaz._
def lookup[K, V](m: Map[K, V], k: K, message: String): ValidationNel[String, V] =
 m.get(k).toSuccess(NonEmptyList(message))
val validated: ValidationNel[String, HttpConnectionParams] = (
 lookup(a, "url", "Url must be supplied") |@|
 lookup(a, "username", "Username must be supplied") |@|
 lookup(a, "password", "Password must be supplied")
)(HttpConnectionParams.apply)

Кроме того, не стесняйтесь использовать import scalaz._, Scalaz._. Мы все это делаем, и это очень хорошо в подавляющем большинстве случаев. Вы всегда можете вернуться и уточнить свой импорт позже. Я также все еще поддерживаю этот ответ, который я написал много лет назад, - вы не должны чувствовать, что вам нужно иметь полное понимание Scalaза (или кошек), чтобы иметь возможность эффективно использовать куски.

licensed under cc by-sa 3.0 with attribution.