¿Cómo se usa scalaz.WriterT para el registro?¿Cómo se usa scalaz.WriterT para iniciar sesión en una expresión for?
Respuesta
Sobre transformadores monad
Esta es una introducción muy breve. Puede encontrar más información en haskellwiki o este great slide by @jrwest.
mónadas no componen, lo que significa que si usted tiene una mónada A[_]
y una mónada B[_]
, entonces A[B[_]]
no puede ser derivada de forma automática . Sin embargo, en la mayoría de los casos esto se puede lograr teniendo un llamado transformador de mónada para una mónada determinada.
Si tenemos transformador mónada BT
para mónada B
, entonces podemos componer una nueva mónada A[B[_]]
para cualquier mónadaA
. Así es, usando BT
, podemos poner el B
dentro de A
.
el uso del transformador Mónada en scalaz
El siguiente supone scalaz 7, ya que francamente no me usar transformadores monad con scalaz 6.
Un transformador de mónada MT
toma dos parámetros de tipo, el primero es la mónada de envoltura (externa), el segundo es el tipo de datos real en la parte inferior de la pila de mónadas. Nota: Puede tomar más parámetros de tipo, pero no están relacionados con el transformador, sino que son específicos para esa mónada determinada (como el tipo registrado de Writer
, o el tipo de error de Validation
).
Así que si tenemos un List[Option[A]]
que nos gustaría tratar como una sola mónada compuesta, entonces necesitamos OptionT[List, A]
. Si tenemos Option[List[A]]
, necesitamos ListT[Option, A]
.
¿Cómo llegar? Si tenemos el valor sin transformador, generalmente podemos envolverlo con MT.apply
para obtener el valor dentro del transformador. Para volver de la forma transformada a la normalidad, generalmente llamamos al .run
en el valor transformado.
Entonces val a: OptionT[List, Int] = OptionT[List, Int](List(some(1))
y val b: List[Option[Int]] = a.run
son los mismos datos, solo que la representación es diferente.
Tony Morris sugirió que es mejor ir a la versión transformada lo antes posible y usarla el mayor tiempo posible.
Nota: Al componer múltiples mónadas utilizando transformadores, se obtiene una pila de transformadores con tipos del orden inverso al del tipo de datos normal. Así que una normal de List[Option[Validation[E, A]]]
sería algo como type ListOptionValidation[+E, +A] = ValidationT[({type l[+a] = OptionT[List, a]})#l, E, A]
Actualización: A partir de scalaz 7.0.0-M2, es Validation
(correctamente) no es una mónada y así ValidationT
no existe. Use EitherT
en su lugar.
Usando WriterT para iniciar sesión
En función de su necesidad, puede utilizar la WriterT
sin ninguna mónada externa en particular (en este caso, en el fondo se utilizará el Id
mónada que no hace nada) , o puede poner el registro dentro de una mónada, o poner una mónada dentro del registro.
primer caso, el registro sencillo
import scalaz.{Writer}
import scalaz.std.list.listMonoid
import scalaz._
def calc1 = Writer(List("doing calc"), 11)
def calc2 = Writer(List("doing other"), 22)
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run should be_== (List("doing calc", "doing other"), 33)
Importamos la instancia listMonoid
, ya que también ofrece la instancia Semigroup[List]
. Es necesario ya que WriterT
necesita que el tipo de registro sea un semigrupo para poder combinar los valores de registro.
segundo caso, el registro dentro de una mónada
Aquí se optó por la mónada Option
por simplicidad.
import scalaz.{Writer, WriterT}
import scalaz.std.list.listMonoid
import scalaz.std.option.optionInstance
import scalaz.syntax.pointed._
def calc1 = WriterT((List("doing calc") -> 11).point[Option])
def calc2 = WriterT((List("doing other") -> 22).point[Option])
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run should be_== (Some(List("doing calc", "doing other"), 33))
Con este enfoque, ya que el registro está dentro de la Option
mónada, si alguna de las opciones de cota es None
, nos acaba de obtener un resultado None
sin ningún tipo de registros.
Nota: x.point[Option]
tiene el mismo efecto que Some(x)
, pero puede ayudar a generalizar mejor el código. No letal solo lo hizo de esa manera por ahora.
tercera opción, el registro fuera de una mónada
import scalaz.{Writer, OptionT}
import scalaz.std.list.listMonoid
import scalaz.std.option.optionInstance
import scalaz.syntax.pointed._
type Logger[+A] = WriterT[scalaz.Id.Id, List[String], A]
def calc1 = OptionT[Logger, Int](Writer(List("doing calc"), Some(11): Option[Int]))
def calc2 = OptionT[Logger, Int](Writer(List("doing other"), None: Option[Int]))
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run.run should be_== (List("doing calc", "doing other") -> None)
Aquí se utiliza OptionT
poner la mónada Option
dentro del Writer
. Uno de los cálculos es None
para mostrar que incluso en este caso, los registros se conservan.
Observaciones finales
En estos ejemplos List[String]
se utilizó como el tipo de registro. Sin embargo, usar String
casi nunca es la mejor manera, solo alguna convención forzada por los marcos de trabajo. Sería mejor definir un ADT de registro personalizado, por ejemplo, y si fuera necesario generarlo, conviértalo en cadena lo más tarde posible. De esta forma, podría serializar el ADT del registro y analizarlo fácilmente más tarde mediante programación (en lugar de analizar cadenas).
WriterT
tiene una gran cantidad de métodos útiles para trabajar con la facilidad de registro, echa un vistazo a la fuente. Por ejemplo dado un w: WriterT[...]
, es posible agregar una nueva entrada de registro mediante w :++> List("other event")
, o incluso iniciar la sesión utilizando el valor actualmente en manos usando w :++>> ((v) => List("the result is " + v))
, etc.
Hay muchos códigos explícita y bien largo (tipos), llamadas en los ejemplos. Como siempre, estos son para mayor claridad, refactorícelos en su código extrayendo tipos y operaciones comunes.
type OptionLogger[A] = WriterT[Option, NonEmptyList[String], A]
val two: OptionLogger[Int] = WriterT.put(2.some)("The number two".pure[NonEmptyList])
val hundred: OptionLogger[Int] = WriterT.put(100.some)("One hundred".pure[NonEmptyList])
val twoHundred = for {
a <- two
b <- hundred
} yield a * b
twoHundred.value must be equalTo(200.some)
val log = twoHundred.written map { _.list } getOrElse List() mkString(" ")
log must be equalTo("The number two One hundred")
Recuerdo ver esto como esencia (tuiteado por @puffnfresh) https://gist.github.com/3345722 – ron
- 1. ¿Por qué https solo se usa para iniciar sesión?
- 2. ¿Para iniciar sesión o no iniciar sesión?
- 3. Si usa Enterprise Library, ¿Log4net es mejor para iniciar sesión?
- 4. ¿Cómo se usa Python para iniciar sesión en una página web y recuperar cookies para usarlas posteriormente?
- 5. ¿Es posible iniciar sesión si se usa una clase en la JVM?
- 6. Cómo hacer una solicitud de Ajax para iniciar sesión
- 7. Stack Trace para iniciar sesión en .NET
- 8. ¿Iniciar sesión en Sinatra?
- 9. ¿Cómo se usa cURL para autenticarse en una aplicación de Rails que usa Devise?
- 10. ¿Cómo se usa FOR XML en las subconsultas?
- 11. Django iniciar sesión/cerrar sesión
- 12. Uso de mechanize para iniciar sesión en una página web
- 13. No se puede modificar Iniciar sesión
- 14. java.util.logging para iniciar sesión en la consola
- 15. Iniciar sesión con Linkedin
- 16. ¿Cómo iniciar sesión en una base de datos Oracle?
- 17. Cómo escribir para iniciar sesión en python con nginx + uwsgi
- 18. cómo iniciar sesión en la máquina ec2?
- 19. Cómo iniciar sesión excepciones en JavaScript
- 20. ¿Alguna alternativa de Python a Selenium para iniciar sesión programáticamente en sitios web que requieren JavaScript para iniciar sesión?
- 21. Cómo iniciar sesión como usuario en p4
- 22. No se puede iniciar una sesión de Bloomberg
- 23. ¿Cómo iniciar sesión en orden inverso?
- 24. Hacking Django Admin, ganchos para iniciar sesión/cerrar sesión
- 25. ¿Cómo iniciar sesión en wordpress mediante programación?
- 26. Iniciar sesión en Amazon S3
- 27. Cómo iniciar sesión en Craigslist usando C#
- 28. C#: ¿Cómo iniciar sesión en una cuota al utilizar DirectoryInfo
- 29. ¿Cómo iniciar sesión un usuario que usa Devise desde la consola de Rails?
- 30. Uso de Google-Account para iniciar sesión
Nota: Hay una próxima clase de tipo 'MonadWriter' en scalaz-seven head. Vale la pena vigilarlo. – ron
Ver https: // github.com/scalaz/scalaz/pull/128 – ron