Scala упрощает вложенные монады

У меня есть код, написанный в Lift. По сути, это вложенный Box (монада, аналогичная Option). Я хотел бы немного упростить его, если это возможно. Желательно добавить параметр типа, чтобы его можно было легко изменить на строку или двойное значение, если это необходимо. Вот код

tryo(r.param("boolean parameter").map(_.toBoolean)).map(_.openOr(false)).openOr(false)

«tryo» — это вспомогательная функция для перехвата и переноса результатов в Box, если возникает исключение, а r — это объект Req. Функция "param" возвращает Box[String] (который исходит из параметра запроса). Я хотел бы, чтобы он работал для строк Int и т. Д. И, если возможно, избавиться от вложенных карт / openOr (getOrElse, как вы думаете, в типах Option).

Преобразователи монад?


person Lukasz    schedule 20.08.2012    source источник


Ответы (3)


flatMap вот это дерьмо!

r.param("boolean parameter").flatMap(tryo(_.toBoolean)).openOr(false)

Или используйте для понимания:

val result = for {
  param <- r.param("boolean parameter")
  bool <- tryo(param.toBoolean)
} yield bool
result openOr false

Но это не решает вашей способности получать разные типы. Для этого я бы предложил что-то вроде:

def asOrDefault[T](input: Box[Any])(default: => T): T = input.flatMap(tryo(_.asInstanceOf[T])).openOr(default)

asOrDefault(r.param("any param"))(0)

Это не проверено... Обратите внимание, что scala.util.control.Exception.allCatch.opt() возвращает Option точно так же, как tryo возвращает Box.

person pr1001    schedule 20.08.2012
comment
Поскольку понимание более сложно, чем мой код -1 для этого, но мне нравятся другие подходы. Спасибо ! - person Lukasz; 20.08.2012
comment
Лукаш, возможно, хотя я бы сказал, что для понимания понятнее, чем твой пример. ;-) - person pr1001; 20.08.2012

Если вы хотите абстрагировать тип, вам нужно абстрагировать как значение по умолчанию, так и преобразование из строки:

case class Converter[T]( default: T, fromString: String => T )

Затем определите неявные экземпляры для ваших типов:

implicit val intConverter = Converter[Int]( 0, _.toInt )
implicit val boolConverter = Converter[Boolean]( false, _.toBoolean )

Наконец, используйте ответ pr1001, используя неявно предоставленное значение конвертера:

def asOrDefault[T](input: Box[String])(implicit conv: Converter[T]): T = input.flatMap(
  s => tryo( conv.fromString(s))).openOr(conv.default)

Компилятор выберет для вас подходящий экземпляр преобразователя:

asOrDefault[Int]( input.param("some int param") )
asOrDefault[Boolean]( input.param("some boolean param") )
person paradigmatic    schedule 20.08.2012
comment
Это лучший ответ, чем мой. - person pr1001; 20.08.2012
comment
Я надеялся на преобразование монады: P - person Lukasz; 21.08.2012

Моя немного подправленная версия построена на основе @pr1001 и @paradigmatic.

case class Converter[T]( fromString: String => T )

implicit val boolConverter = Converter(_.toBoolean) 

implicit val intConverter = Converter(_.toInt)

def asOrDefault[T](input: Box[String], default: T)(implicit conv: Converter[T]): T =       
  input.flatMap( s => tryo(conv.fromString(s))).openOr(default)

И использование в моем случае:

def prettyPrint(implicit r: Req) = asOrDefault(r.param("prettyPrint"), false)

def maxResults(implicit r: Req): Int = asOrDefault(r.param("maxResults"), 20)
person Lukasz    schedule 21.08.2012