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 Traversal
s 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 Prism
s peuvent être appliquées dans la pratique, le look de Control.Exception.Lens
, ce qui fournit une collection d' Prism
s en Haskell extensible Exception
de la hiérarchie. Cela vous permet d'effectuer d'exécution des essais de type sur SomeException
s 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, Lens
es et Prism
s coder les deux principaux outils de conception de la programmation orientée objet, la composition et le sous-typage. Lens
es sont de première classe de la version de Java .
et =
opérateurs, et Prism
s sont de première classe de la version de Java instanceof
et implicite de l'upcasting.
Un fructueux façon de penser l' Lens
es, 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 Lens
es et Prism
s sont très modulable. Vous pouvez composer Prism
s pour obtenir plus grand, Prism
s ("a
est s
, ce qui est un p
") à l'aide d' (.)
; la composition d'un Prism
avec un Lens
vous donne un Traversal
.