Translate

viernes, 6 de mayo de 2022

Funtores en Cats parte 9

Hay situaciones en las que la eliminación de izquierda a derecha no es la opción correcta. Un ejemplo es el tipo O en Scalactic, que es un equivalente convencionalmente sesgado a la izquierda de O bien:

type PossibleResult = ActualResult Or Error


Otro ejemplo es el funtor contravariante para Function1.
Mientras que el funtor Function1 implementa la composición de función de izquierda a derecha, el funtor Contravariant implementa la composición de derecha a izquierda con estilo de composición. En otras palabras, las siguientes expresiones son todas equivalentes:

val func3a: Int => Double = a => func2(func1(a))
val func3b: Int => Double = func2.compose(func1)

// Hypothetical example. This won't actually compile:
val func3c: Int => Double = func2.contramap(func1)

Sin embargo, si intentamos esto de verdad, nuestro código no se compilará:

import cats.syntax.contravariant._ // for contramap
val func3c = func2.contramap(func1)
// error: value contramap is not a member of Double => Double
// val func3c = func2.contramap(func1)
//
 ^^^^^^^^^^^^^^^

El problema aquí es que la contravariante para la Function1 corrige el tipo de retorno y deja que el tipo de parámetro varíe, lo que requiere que el compilador elimine los parámetros de tipo de derecha a izquierda, como se muestra a continuación

type F[A] = A => Double

El compilador falla simplemente debido a su sesgo de izquierda a derecha. Podemos probar esto creando un alias de tipo que cambia los parámetros en la Función1:

type <=[B, A] = A => B
type F[A] = Double <= A

Si volvemos a escribir func2 como una instancia de <=, restablecemos el orden de eliminación requerido y podemos llamar a contramap como deseemos:

val func2b: Double <= Double = func2
val func3c = func2b.contramap(func1)
// func3c: Int => Double = scala.Function1$$Lambda$7919/0
x00000008424d3040@50061a2d

La diferencia entre func2 y func2b es puramente sintáctica: ambos se refieren al mismo valor y, por lo demás, los alias de tipo son completamente compatibles. Increíblemente, sin embargo, esta simple reformulación es suficiente para darle al compilador la pista que necesita para resolver el problema.
Es raro que tengamos que hacer este tipo de eliminación de derecha a izquierda. La mayoría de los constructores de tipos de parámetros múltiples están diseñados para tener un sesgo hacia la derecha, lo que requiere la eliminación de izquierda a derecha que es compatible con el compilador listo para usar. Sin embargo, es útil conocer esta peculiaridad del orden de eliminación en caso de que alguna vez te encuentres con un escenario extraño como el anterior.