40 votes

Qu'est-ce que Prisms?

Je suis en train de parvenir à une meilleure compréhension de l' lens bibliothèque, j'ai donc jouer avec les types qu'il offre. J'ai déjà eu une certaine expérience avec les lentilles, et de savoir comment puissant et pratique, ils le sont. Alors j'ai déménagé sur des Prismes, et je suis un peu perdu. Il semble que les prismes permettent deux choses:

  1. Pour déterminer si une entité appartient à une branche particulière d'un type somme, et si elle le fait, la capture de données sous-jacentes dans un tuple ou un singleton.
  2. Déstructuration et la reconstruction d'une entité, éventuellement de modifier dans le processus.

Le premier point me semble utile, mais généralement on n'a pas besoin de toutes les données à partir d'une entité, et ^? à la plaine des lentilles permet d'atteindre Nothing si le terrain en question n'appartient pas à la direction de l'entité représente, comme il le fait avec des prismes.

Le deuxième point... je ne sais pas, peut avoir des usages?

Donc la question est: que puis-je faire avec un Prisme que je ne peux pas avec d'autres optiques?

Edit: merci tout le monde pour l'excellence de réponses et les liens pour d'autres lectures! Je souhaite que je pourrais les accepter toutes.

37voto

Benjamin Hodgson Points 3510

Les lentilles de caractériser l' a-une relation; Prismes de caractériser l' est-une relation.

Un Lens s a dit "s a a"; il a des méthodes pour obtenir exactement un a d'un s et de remplacer exactement un a en s. Un Prism s a dit "a est s"; il a des méthodes à la sortie d'un a d'un s et de (tenter de) abattu un s d'un a.

Mettre que de l'intuition dans le code vous donne le familier "get-set" (ou "costate comonad coalgebra") la formulation d'objectifs,

data Lens s a = Lens {
    get :: s -> a,
    set :: a -> s -> s
}

et une "sortie-abattu" représentation de prismes,

data Prism s a = Prism {
    up :: a -> s,
    down :: s -> Maybe a
}

up injecte un a en s (sans ajout d'information), et down tests si l' s est a.

En lens, up est orthographié review et down est preview. Il n'y a pas d' Prism constructeur; vous utilisez l' prism' smart constructeur.


Que pouvez-vous faire avec un Prism? Injecter de projet et la somme des types!

_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
}

Les lentilles ne prennent pas en charge ce - que vous ne pouvez pas écrire un Lens (Either a b) a parce que vous ne pouvez pas mettre en oeuvre get :: Either a b -> a. En pratique, vous pouvez écrire un Traversal (Either a b) a, mais cela ne vous permettra pas de créer un Either a b d'un a - ça ne vous permettent de remplacer un a ce qui est déjà là.

Aparté: je pense que ce point subtil sur Traversals est la source de votre confusion sur l'partielle champs de l'enregistrement.

^? à la plaine des lentilles permet d'atteindre Nothing si le terrain en question n'appartient pas à la direction de l'entité représente

À l'aide de ^? avec un réel Lens ne reviendra jamais en Nothing, en raison d'un Lens s a identifie exactement un a à l'intérieur d'un s. Lorsqu'ils sont confrontés à un partiel de terrain,

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

makeLenses va générer un Traversal, pas un Lens.

wobble :: Traversal' Wibble Int
wubble :: Traversal' Wibble Bool

Pour un exemple de cette façon Prisms peuvent être appliquées dans la pratique, le look de Control.Exception.Lens, ce qui fournit une collection d' Prisms en Haskell extensible Exception de la hiérarchie. Cela vous permet d'effectuer d'exécution des essais de type sur SomeExceptions et injecter des exceptions spécifiques en SomeException.

_ArithException :: Prism' SomeException ArithException
_AsyncException :: Prism' SomeException AsyncException
-- etc.

(Ils sont un peu des versions simplifiées des types réels. En réalité, ces prismes sont surchargés, les méthodes de la classe.)

La pensée à un niveau supérieur, certains programmes peuvent être considérés comme étant "en Prism". L'encodage et le décodage des données en est un exemple: vous pouvez toujours convertir des données structurées à un String, mais pas tous l' String peut être analysée en arrière:

showRead :: (Show a, Read a) => Prism String a
showRead = Prism {
    up = show,
    down = listToMaybe . fmap fst . reads
}

Pour résumer, Lenses et Prisms coder les deux principaux outils de conception de la programmation orientée objet, la composition et le sous-typage. Lenses sont de première classe de la version de Java . et = opérateurs, et Prisms sont de première classe de la version de Java instanceof et implicite de l'upcasting.


Un fructueux façon de penser l' Lenses, c'est qu'ils vous donner un moyen de diviser un composite s en un concentré valeur a et le contexte sous - c. Pseudo-code:

type Lens s a = exists c. s <-> (a, c)

Dans ce cadre, Prism vous donne une manière de regarder un s soit une a ou le contexte sous - c.

type Prism s a = exists c. s <-> Either a c

(Je vais le laisser à vous convaincre vous-même que ce sont des isomorphe à la simple représentation que j'ai démontré ci-dessus. Essayez d'adopter get/set/up/down pour ces types!)

En ce sens, Prism est une co-Lens. Either est catégorique des double - (,); Prism est catégorique des double - Lens.

Vous pouvez également observer cette dualité dans le "profunctor optique" formulation - Strong et Choice sont des doubles.

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

C'est plus ou moins la représentation qui lens utilise, car ces Lenses et Prisms sont très modulable. Vous pouvez composer Prisms pour obtenir plus grand, Prisms ("a est s, ce qui est un p") à l'aide d' (.); la composition d'un Prism avec un Lens vous donne un Traversal.

17voto

Oleg Grenrus Points 1193

Je viens d'écrire un billet de blog, ce qui peut aider à construire une certaine intuition à propos de Prismes: Prismes sont les constructeurs (les Lentilles sont des champs). http://oleg.fi/gists/posts/2018-06-19-prisms-are-constructors.html


Les prismes peuvent être introduits à titre de première classe correspondant à un modèle, mais c'est un d'un point de vue partial. Je dirais qu'ils sont généralisées des constructeurs, mais peut-être plus souvent utilisé pour la correspondance de modèle que pour la construction réelle.

La propriété la plus importante des constructeurs (et légitime prismes), est leur injectivity. Bien que l'habitude prisme lois ne sont pas de l'état directement, injectivity propriété peut être déduite.

Pour citer lens-documentation de la bibliothèque, les prismes lois sont:

Tout d'abord, si j' review valeur avec un Prism puis preview, je vais le récupérer:

preview l (review l b) ≡ Just b

Deuxièmement, si vous pouvez extraire une valeur d'un l'aide d'un Prism l valeur s, alors la valeur s est complètement décrit par l et a:

preview l s ≡ Just a ⇒ review l a ≡ s

En fait, la première loi à elle seule, suffit à prouver la injectivity de construction via Prism:

review l x ≡ review l y ⇒ x ≡ y

La preuve en est simple:

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

Nous pouvons utiliser injectivity propriété comme un outil supplémentaire dans le général paramétré par le raisonnement boîte à outils. Ou nous pouvons l'utiliser comme un simple bien de vérifier pour décider si quelque chose est licite Prism. La vérification est facile, car nous ne l' review côté Prism. De nombreux smart constructeurs, qui, par exemple, normaliser les données d'entrée, n'est-ce pas légitime de prismes.

Un exemple d'utilisation de 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 première loi est également violé:

λ> preview _CI (review _CI "FOO")
Just "foo"

10voto

duplode Points 3803

En plus des autres excellentes réponses, je me sens Isos offrir un joli point de vue pour l'examen de cette question.

  • La présence de quelques i :: Iso' s a signifie que si vous avez un s de la valeur vous aussi (presque) ont un a de la valeur, et vice versa. L' Iso' vous donne deux fonctions de conversion, view i :: s -> a et review i :: a -> s qui sont à la fois la garantie de réussir et sans perte.

  • La présence de quelques l :: Lens' s a signifie que si vous avez un s , vous avez également un a, mais pas vice-versa. view l :: s -> a peut déposer des informations sur le chemin, comme la conversion n'est pas nécessaire d'être sans perte, et donc vous ne pouvez pas aller dans l'autre sens si vous ne disposez que d'un a (cf. set l :: a -> s -> s, ce qui requiert également un s en plus de l' a de la valeur afin de fournir les informations manquantes).

  • La présence de quelques p :: Prism' s a signifie que si vous avez un s de la valeur, vous pourriez aussi avoir un a, mais il n'y a pas de garanties. La conversion preview p :: s -> Maybe a n'est pas garantie pour réussir. Encore, vous avez l'autre sens, review p :: a -> s.

En d'autres termes, une Iso est inversible et réussit toujours. Si vous supprimez l'inversibilité exigence, vous obtenez un Lens; si vous déposez la garantie de réussite, vous obtenez un Prism. Si vous goutte à la fois, vous obtenez une affine la traversée (qui n'est pas objectif comme un type distinct), et si vous allez un peu plus loin et donner jusqu'à avoir tout au plus une cible, vous vous retrouvez avec un Traversal. Qui se reflète dans l'un des diamants de la lentille sous-type de hiérarchie:

 Traversal
    / \
   /   \
  /     \
Lens   Prism
  \     /
   \   /
    \ /
    Iso

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X