haskell - significado - prismas y piramides ejercicios
¿Qué son los prismas? (3)
Acabo de escribir una entrada de blog, que podría ayudar a desarrollar una intuición acerca de los prismas: los prismas son constructores (los lentes son campos). http://oleg.fi/gists/posts/2018-06-19-prisms-are-constructors.html
Los prismas se pueden introducir como una coincidencia de patrones de primera clase , pero esa es una vista de un solo lado. Yo diría que son constructores generalizados , aunque tal vez se usen más a menudo para la comparación de patrones que para la construcción real.
La propiedad importante de los constructores (y los prismas legales ), es su inyectividad. Aunque las leyes de prisma usuales no establecen eso directamente, se pueden deducir las propiedades de inyectividad.
Para citar lens
documentación de lens
biblioteca, las leyes de prismas son:
Primero, si review
un valor con un Prism
y luego preview
, lo recuperaré:
preview l (review l b) ≡ Just b
En segundo lugar, si puede extraer un valor a utilizando un Prism
l
de un valor s
, entonces el valor s
se describe completamente mediante l
y a
:
preview l s ≡ Just a ⇒ review l a ≡ s
De hecho, solo la primera ley es suficiente para probar la inyectividad de la construcción a través de Prism
:
review l x ≡ review l y ⇒ x ≡ y
La prueba es sencilla:
review l x ≡ review l y
-- x ≡ y -> f x ≡ f y
preview l (review l x) ≡ preview l (review l y)
-- rewrite both sides with the first law
Just x ≡ Just y
-- injectivity of Just
x ≡ y
Podemos usar la propiedad de inyectividad como una herramienta adicional en la caja de herramientas de razonamiento ecuacional. O podemos usarlo como una propiedad fácil de verificar para decidir si algo es un Prism
legal. El cheque es fácil ya que solo el lado de review
de Prism
. Muchos constructores inteligentes , que, por ejemplo, normalizan los datos de entrada, no son prismas legales.
Un ejemplo usando case-insensitive
:
-- Bad!
_CI :: FoldCase s => Prism'' (CI s) s
_CI = prism'' ci (Just . foldedCase)
λ> review _CI "FOO" == review _CI "foo"
True
λ> "FOO" == "foo"
False
La primera ley también es violada:
λ> preview _CI (review _CI "FOO")
Just "foo"
Estoy tratando de lograr una comprensión más profunda de lens
biblioteca de lens
, así que juego con los tipos que ofrece. Ya he tenido algo de experiencia con lentes, y sé lo poderosas y convenientes que son. Así que pasé a Prismas, y estoy un poco perdido. Parece que los prismas permiten dos cosas:
- Determinar si una entidad pertenece a una rama particular de un tipo de suma, y si lo hace, capturar los datos subyacentes en una tupla o un singleton.
- Destrucción y reconstrucción de una entidad, posiblemente modificándola en proceso.
El primer punto parece útil, pero generalmente uno no necesita todos los datos de una entidad, y ^?
con lentes lisos permite obtener Nothing
si el campo en cuestión no pertenece a la rama que representa la entidad, al igual que lo hace con los prismas.
El segundo punto ... no sé, ¿podría tener usos?
Entonces la pregunta es: ¿qué puedo hacer con un Prisma que no puedo con otras ópticas?
Edición : ¡gracias a todos por sus excelentes respuestas y enlaces para leer más! Ojalá pudiera aceptarlas todas.
Además de las otras excelentes respuestas, creo que los Iso
proporcionan un buen punto de vista para considerar este asunto.
Habiendo algo de
i :: Iso'' sa
significa que si tiene un valor des
también tiene (virtualmente)a
valor de a, y viceversa.Iso''
le ofrece dos funciones de conversión,view i :: s -> a
review i :: a -> s
que están garantizadas para tener éxito y sin pérdidas.Hay algunos
l :: Lens'' sa
significa que si tienes unas
también tienes unaa
, pero no al revés .view l :: s -> a
puede soltar información en el camino, ya que no se requiere que la conversión sea sin pérdidas, y por lo tanto no puede ir al otro lado si todo lo que tiene es unaa
(consulteset l :: a -> s -> s
, que también requiere unas
además del valor a para proporcionar la información faltante).- Hay algunos
p :: Prism'' sa
significa que si tiene un valors
también podría tener una
, pero no hay garantías. Lapreview p :: s -> Maybe a
conversiónpreview p :: s -> Maybe a
no está garantizado para tener éxito. Aún así, tienes la otra dirección,review p :: a -> s
.
En otras palabras, un Iso
es invertible y siempre tiene éxito. Si abandonas el requisito de invertibilidad, obtienes una Lens
; Si abandonas la garantía de éxito, obtienes un Prism
. Si eliminas ambos, obtienes un recorrido transversal afín (que no está en la lente como un tipo separado), y si das un paso más y te rindes al tener un objetivo, terminas con un Traversal
. Eso se refleja en uno de los diamantes de la jerarquía de subtipo de lente :
Traversal
/ /
/ /
/ /
Lens Prism
/ /
/ /
/ /
Iso
Las lentes caracterizan la relación has-a ; Los prismas caracterizan la relación es-una .
Una Lens sa
dice " s
tiene una a
"; tiene métodos para obtener exactamente uno a
de un s
sobrescribir exactamente uno a
en un s
. A Prism sa
dice " a
es a s
"; tiene métodos para reenviar una a
a una y para (intentar) bajar una s
a a
.
Poner esa intuición en el código le da la formulación de lentes "get-set" (o "costate comonad coalgebra"),
data Lens s a = Lens {
get :: s -> a,
set :: a -> s -> s
}
y una representación de prismas "upcast-downcast",
data Prism s a = Prism {
up :: a -> s,
down :: s -> Maybe a
}
up
inyecta una a
en s
(sin agregar ninguna información), y down
comprueba si la s
es una a
.
En la lens
, up
se escribe review
y down
es preview
. No hay un constructor de Prism
; Usted utiliza el constructor inteligente prism''
.
¿Qué puedes hacer con un Prism
? Inyectar y proyectar tipos de suma!
_Left :: Prism (Either a b) a
_Left = Prism {
up = Left,
down = either Just (const Nothing)
}
_Right :: Prism (Either a b) b
_Right = Prism {
up = Right,
down = either (const Nothing) Just
}
Los lentes no son compatibles con esto, no se puede escribir un Lens (Either ab) a
porque no se puede implementar get :: Either ab -> a
. Como cuestión práctica, puede escribir un Traversal (Either ab) a
, pero eso no le permite crear una Either ab
de una a
- solo le permitirá sobrescribir una a
que ya está allí.
Aparte: creo que este punto sutil acerca de
Traversal
s es la fuente de su confusión acerca de los campos de registro parciales.
^?
con lentes simples permite obtenerNothing
si el campo en cuestión no pertenece a la rama que representa la entidadUtilizando
^?
con unaLens
real nunca devolveráNothing
, porque unaLens sa
identifica exactamente unaa
dentro de unas
. Cuando se enfrenta con un campo de registro parcial,
data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }
makeLenses
generará unTraversal
, no unLens
.
wobble :: Traversal'' Wibble Int wubble :: Traversal'' Wibble Bool
Para ver un ejemplo de cómo se puede aplicar Prism
s en la práctica, consulte Control.Exception.Lens
, que proporciona una colección de Prism
s en la jerarquía de Exception
extensible de Haskell. Esto le permite realizar pruebas de tipo de tiempo de ejecución en SomeException
e inyectar excepciones específicas en SomeException
.
_ArithException :: Prism'' SomeException ArithException
_AsyncException :: Prism'' SomeException AsyncException
-- etc.
(Estas son versiones ligeramente simplificadas de los tipos reales. En realidad, estos prismas son métodos de clase sobrecargados).
Para resumir, Lens
es y Prism
s juntos codifican las dos herramientas de diseño de la programación orientada a objetos, la composición y el subtipo. Lens
son una versión de primera clase de Java .
y =
operadores, y Prism
s es una versión de primera clase de la instanceof
de Java y la conversión implícita implícita.
Una manera fructífera de pensar acerca de las Lens
es que le brindan una manera de dividir una s
compuesta en un valor focalizado y un contexto c
. Pseudocódigo
type Lens s a = exists c. s <-> (a, c)
En este marco, un Prism
te da una manera de ver una s
como si fuera un contexto o un contexto c
.
type Prism s a = exists c. s <-> Either a c
(Lo dejo a usted para convencerse de que estos son isomorfos a las representaciones simples que demostré anteriormente. ¡Intente implementar get
/ set
/ up
/ down
para estos tipos!)
En este sentido, un Prism
es un Co- Lens
. Either
es el dual categórico de (,)
; Prism
es el dual categórico de la Lens
.
También puede observar esta dualidad en la formulación de "óptica de profunctor" : Strong
y Choice
son duales.
type Lens s t a b = forall p. Strong p => p a b -> p s t
type Prism s t a b = forall p. Choice p => p a b -> p s t
Esta es más o menos la representación que usa la lens
, ya que estas Lens
y Prism
son muy composibles. Puede componer Prism
s para hacerlos más grandes (" a
es una s
, que es una p
") usando (.)
; componer un Prism
con una Lens
te da un Traversal
.