Monocle IV: lente Prism

En la entrada anterior, Monocle III: lente Optional , realicé una descripción de la lente Optional, así como, la la descripción de unos ejemplos de uso. En la presente entrada, Monocle IV: lente Prism, me centraré en la lente Prism.

La lente Prism tiene un uso óptica para la selección de parte de un CoProducto (Suma); por ejemplo, sealed trait o Enum. La suma corresponde con la herencia de clases y objetos.

Prism tiene dos tipos de parámetros: Prism[S, A], S representa la Suma y A una parte de la suma. La lente está definida en el paquete monocle.Prism

Para los ejemplos, definiremos la siguiente jerarquía de clases y objetos

 sealed trait Json
 case object JNull extends Json
 case class JStr(v: String) extends Json
 case class JNum(v: Double) extends Json
 case class JObj(v: Map[String, Json]) extends Json

Definición de un prisma

La definición de un prisma se puede realizar de dos formas posibles: la primera, utilizando pattern matching; y, la segunda, utilizando funciones parciales. Para los dos casos, se define el tipo de entrada y el tipo de salida. Así, tenemos los siguiente ejemplos definidos, respectivamente, con pattern matching y de forma parcial.

 // Forma 1
 val jStrForma1 = Prism[Json, String]{
   case JStr(v) => Some(v)
   case _ => None
 }(JStr)
 // Forma 2
 val jStrForma2 = Prism.partial[Json, String]{case JStr(v) => v}(JStr)

Operaciones básicas

Creación de un objeto

Dada la estructura jerárquica Json definida previamente y los prismas definidos, se puede realizar la creación de los elementos de la siguiente forma descritos en los siguientes ejemplos:

 println(s"1 Json String=${jStrForma1("hello")} ")
 println(s"2 Json String=${jStrForma2("hello")} ")

La salida por consola es la siguiente:

 1 Json String=JStr(hello) 
 2 Json String=JStr(hello

Operación Set

Dada la estructura jerárquica Json definida previamente y los prismas definidos, la operación de asignación de un valor se realiza de la siguiente forma:

 println(s"3 Set Json=${jStrForma1.set("Bar")(JStr("Hello"))}")
 println(s"5 Set 'Bar' en un tipo JNum=${jStrForma1.set("Bar")(JNum(10))}")

La salida por consola es la siguiente:

 3 Set Json=JStr(Bar)
 5 Set 'Bar' en un tipo JNum=JNum(10.0)

Operación Get

Dada la estructura jerárquica Json definida previamente y los prismas definidos, la operación de obtención de un valor se realiza de la siguiente forma:

 println(s"1 Json String (JStr) a Option=${jStrForma1.getOption(JStr("Hello"))}")
 println(s"1 Json Double (JNum) a Option=${jStrForma1.getOption(JNum(3.2))}") // JNum no está definido en jStrForma1.

 println(s"2 Json String (JStr) a Option=${jStrForma2.getOption(JStr("Hello"))}")
 println(s"2 Json Double (JNum) a Option=${jStrForma2.getOption(JNum(3.2))}") // JNum no está definido en jStr.

La salida por consola es la siguiente:

 1 Json String (JStr) a Option=Some(Hello)
 1 Json Double (JNum) a Option=None

 2 Json String (JStr) a Option=Some(Hello)
 2 Json Double (JNum) a Option=None

Operación modify

Dada la estructura jerárquica Json definida previamente y los prismas definidos, la operación de modificación de un valor se realiza de la siguiente forma:

 println(s"4 Modify Json=${jStrForma1.modify(_.reverse)(JStr("Hello"))}")
 println(s"6 Modify reverse de JNum(10)=${jStrForma1.modify(_.reverse)(JNum(10))}")
 println(s"7 ModifyOption String=${jStrForma1.modifyOption(_.reverse)(JStr("Hello"))}")
 println(s"8 ModifyOption Num=${jStrForma1.modifyOption(_.reverse)(JNum(10))}")

La salida por consola es la siguiente:

 4 Modify Json=JStr(olleH)
 6 Modify reverse de JNum(10)=JNum(10.0)
 7 ModifyOption String=Some(JStr(olleH))
 8 ModifyOption Num=None

Composición de prismas

Monocle tiene definidos prismas de tipos básicos, como por ejemplo: double, bigInt, bigDecimal,…definidos en el paquete monocle.std.xxx

Con los primas existentes y con los prismas que definimos, podemos realizar composición de los mismos con la función composePrism. En el siguiente ejemplo, defino dos primas, siendo uno de ellos, una composición de un prisma que definido;y, además, el prisma de transformación de un elemento de tipo Double y un entero.

 import monocle.std.double.doubleToInt // Prism[Double, Int] defined in Monocle
 val jNum: Prism[Json, Double] = Prism.partial[Json, Double]{case JNum(v) => v}(JNum)
 val jInt: Prism[Json, Int] = jNum composePrism doubleToInt
 println(s"9 Entero =${jInt(5)} ")
 println(s"9 Entero con Option=${jInt.getOption(JNum(5.0))}")
 println(s"9 Double con Option=${jInt.getOption(JNum(5.2))}")
 println(s"9 String con Option=${jInt.getOption(JStr("Hello"))}")

La salida por consola es la siguiente:

 9 Entero =JNum(5.0) 
 9 Entero con Option=Some(5)
 9 Double con Option=None
 9 String con Option=None

Generadores de Prismas

Los generadores de prismas son macros existentes que facilitan la creación de Prismas. Los generadores de prismas están en el paquete monocle.macros.GenPrism. Así, la definición de un prisma mediante un generador, se realiza de la siguiente forma:

 import monocle.Prism
 import monocle.macros.GenPrism
 val rawJNum: Prism[Json, JNum] = GenPrism[Json, JNum]

Para obtener un valor utilizando el prisma anterior, se realiza de la siguiente forma:

 println(s"1 GenPrism JNum(10.0)=${rawJNum.getOption(JNum(10.0))}")
 println(s"2 GenPrism JStr('Prueba')=${rawJNum.getOption(JStr("Prueba"))}")

La salida por consola es la siguiente:

 1 GenPrism JNum(10.0)=Some(JNum(10.0))
 2 GenPrism JStr('Prueba')=None

Además de los prismas, podemos utilizar otras lentes, como por ejemplo el generador de la lente Iso. En el siguiente ejemplo, se muestra un ejemplo de uso de las lentes prisma e Iso utilizando composición de lentes:

 import monocle.macros.GenIso
 val jNum: Prism[Json, Double] = GenPrism[Json, JNum] composeIso GenIso[JNum, Double]
 val jNull: Prism[Json, Unit] = GenPrism[Json, JNull.type] composeIso GenIso.unit[JNull.type]
 println(s"3 GenPrism-GenIso=${jNum.getOption(JNum(10.0))}")
 println(s"4 GenPrism-GenIso=${jNull.getOption(JNum(10.0))}")
 println(s"5 GenPrism-GenIso=${jNum.getOrModify(JNum(10.0))}")
 println(s"6 GenPrism-GenIso=${jNum.getOrModify(JNum(10.0)).getOrElse(0.0)}")

La salida por consola es la siguiente:

 3 GenPrism-GenIso=Some(10.0)
 4 GenPrism-GenIso=None
 5 GenPrism-GenIso=\/-(10.0)
 6 GenPrism-GenIso=10.0

En la siguiente entrada, Monocle V: lente Travesal, describiremos la lente Traversal de la librería Monocle así como unos ejemplos prácticos.

Para el lector interesado, las entradas que he realizado sobre el tema son las siguientes:

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s