una significado que prismas prisma piramides piramide partes nombres geometria ejercicios ejemplos con altura haskell lens

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:

  1. 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.
  2. 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 de s 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 una s también tienes una a , 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 una a (consulte set l :: a -> s -> s , que también requiere una s además del valor a para proporcionar la información faltante).

  • Hay algunos p :: Prism'' sa significa que si tiene un valor s también podría tener un a , pero no hay garantías. La preview p :: s -> Maybe a conversión preview 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 obtener Nothing si el campo en cuestión no pertenece a la rama que representa la entidad

Utilizando ^? con una Lens real nunca devolverá Nothing , porque una Lens sa identifica exactamente una a dentro de una s . Cuando se enfrenta con un campo de registro parcial,

data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }

makeLenses generará un Traversal , no un Lens .

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 .