tl; dr: ist es möglich, eine der lens
Familie von Abstraktionen zu verwenden, zu wickeln/auspacken jeder beliebigen newtype
(also eine Instanz für solche Abstraktionen bereitstellt)?Wie die vorformulierten der Verpackung zu beseitigen und Abwickeln mit Linsen
Ich werde meine Frage durch ein einfaches Beispiel motivieren, basierend auf einer wahren Geschichte. Angenommen, ich definieren die folgende newtype
:
newtype FreeMonoid a = FreeMonoid { asMap :: Map a Int }
die verwendet wird, hinsichtlich der Form darzustellen:
a0 <> a1 <> ... <> an-1
Wir können frei Monoide als Listen darstellen:
instance Ord a => IsList (FreeMonoid a) where
type Item (FreeMonoid a) = a
fromList xs = FreeMonoid $ Map.fromListWith (+) $ zip xs (repeat 1)
toList (FreeMonoid p) = do
(x, n) <- Map.toList p
genericReplicate n x
Zwei Beispiele Frei-Monoiden sind Sequenzen von Summe und Sequenzen von Produkten:
type FreeSum a = FreeMonoid (Sum a)
type FreeProduct a = FreeMonoid (Product a)
Dabei sind Sum
und Product
in Data.Monoid
definiert. Jetzt konnten wir fromList
und toList
Operationen für FreeSum
definieren und FreeProduct
wie folgt:
fromListSum :: Ord a => [a] -> FreeSum a
fromListSum = fromList . (Sum <$>)
fromListProduct :: Ord a => [a] -> FreeProduct a
fromListProduct = fromList . (Product <$>)
Das hat aber eine ganze Reihe von vorformulierten. Es wäre schöner, wenn wir einfach sagen könnte:
fromListW :: (Ord a, Wrapper f) => [a] -> FreeMonoid (f a)
fromListW = fromList . (wrap <$>)
wo wrap
ist eine Operation der (hypotetical) Wrapper
Klasse waren:
wrap :: a -> f a
unwrap :: f a -> a
Und ich möchte in der Lage sein zu schreiben eine Funktion:
toListW :: (Ord a, Wrapper f) => FreeMonoid (f a) -> [a]
toListW = (unwrap <$>) . toList
Lenses scheinen solche eine Abstraktion in Control.Lens.Wrapped
, um (für die Sum
Product
und in diesem Beispiel sind Insta nces der typeclasses dort!). Meine Versuche, die Abstraktionen in diesem Modul zu verstehen und zu verwenden, sind jedoch fehlgeschlagen. Zum Beispiel:
fromListW :: (Ord a, Wrapped (f a)) => [a] -> FreeMonoid (f a)
fromListW = fromList . (Wrapped <$>)
wird nicht funktionieren, da das Argument nicht eine Liste von Unwrapped (f a)
ist.
Also meine Frage ist:
- Do Linsen eine Abstraktion ähnlich wie diese
Wrapper
Klasse zur Verfügung stellen? - Wenn nicht, kann dieses Problem mit der Verwendung von Linsen gelöst werden?
Danke für die tolle Antwort. Es ist immer noch unklar, wie und warum die Lösung funktioniert, aber ich hoffe, dass das Verständnis kommt, nachdem ich tiefer in Linsen eingetaucht bin. In der Zwischenzeit ist [hier] (https://github.com/capitanbatata/sandbox/blob/master/pw-lenses/src/Coercions.lhs) ein funktionierender Code. –