Lens sağladığımı bildiğim en az 4 kütüphane var.
Bir lens kavramı, izomorfik bir şey sağlamasıdır.
data Lens a b = Lens (a -> b) (b -> a -> a)
iki işlev sağlayan: bir alıcı ve bir ayarlayıcı
get (Lens g _) = g
put (Lens _ s) = s
üç yasaya tabidir:
İlk olarak, bir şey koyarsanız, geri alabilirsiniz
get l (put l b a) = b
İkincisi, alıp sonra ayar cevabı değiştirmez
put l (get l a) a = a
Ve üçüncüsü, iki kez koymak bir kez koymakla aynıdır, daha doğrusu ikinci koymak kazanır.
put l b1 (put l b2 a) = put l b1 a
Tip sisteminin bu yasaları sizin için kontrol etmek için yeterli olmadığını unutmayın, bu nedenle hangi lens uygulamasını kullanırsanız kullanın, kendiniz sağlamanız gerekir.
Bu kütüphanelerin birçoğu üstte bir sürü ekstra birleştirici sağlar ve genellikle basit kayıt türleri alanları için lensleri otomatik olarak oluşturmak için bir çeşit şablon haskell makinesi sağlar.
Bunu göz önünde bulundurarak, farklı uygulamalara dönebiliriz:
Uygulamalar
fclabels
fclabels belki de objektif kütüphaneleri hakkında en kolay anlaşılan şeydir , çünkü a :-> b
doğrudan yukarıdaki türe çevrilebilir. Lens oluşturmanıza izin verdiği için yararlı olan bir Kategori örneği (:->)
sağlar. Ayrıca kanunsuz birPoint
burada kullanılan bir lens kavramını genelleştiren tür ve izomorfizmlerle uğraşmak için bazı sıhhi tesisat sağlar.
Benimsenmesinin bir engeli fclabels
, ana paketin şablon-haskell tesisatını içermesidir, bu nedenle paket Haskell 98 değildir ve (oldukça tartışmalı olmayan) TypeOperators
uzantıyı gerektirir.
Veri erişimci
[Düzenle: data-accessor
artık bu temsili kullanmıyor, ancak buna benzer bir forma taşındı data-lens
. Yine de bu yorumu saklıyorum.]
Veri erişimci göre biraz daha popüler fclabels
çünkü kısmen, bir Haskell 98 Bununla birlikte, iç temsil kendi seçimi bana ağzımda biraz kusmak.
T
Bir lensi temsil etmek için kullandığı tip dahili olarak
newtype T r a = Cons { decons :: a -> r -> (a, r) }
Sonuç olarak, get
bir merceğin değeri için, 'a' argümanı için tanımlanmamış bir değer göndermelisiniz! Bu bana inanılmaz derecede çirkin ve özel bir uygulama olarak dikkat çekiyor.
Bununla birlikte, Henning sizin için erişimcileri otomatik olarak ayrı bir ' veri erişimci şablonu ' paketinde oluşturmak için şablon haskell tesisatını dahil etti .
Halihazırda kullanan, Haskell 98 olan ve çok önemli olan oldukça büyük bir paket setinin avantajına sahiptir. Category
çok örneği , bu nedenle sosisin nasıl yapıldığına dikkat etmezseniz, bu paket aslında oldukça makul bir seçimdir. .
lensler
Daha sonra, lensleri doğrudan bu tür monad homomorfizmleri olarak tanımlayarak, bir merceğin iki durum monad arasında bir durum monad homomorfizması sağlayabildiğini gözlemleyen lens paketi vardır .
Gerçekten lensleri için bir tür sağlamak için rahatsız olsaydı, bir rütbe-2 türü olurdu:
newtype Lens s t = Lens (forall a. State t a -> State s a)
Sonuç olarak, bu yaklaşımı sevmiyorum, çünkü Haskell 98'den gereksiz bir şekilde sizi çekiyor (eğer objektiflerinizi soyut olarak sağlamak için bir tür istiyorsanız) ve sizi Category
lens örneğinden mahrum eder , bu da size izin verir onları oluşturmak .
. Uygulama ayrıca çok parametreli tip sınıfları gerektirir.
Burada belirtilen diğer tüm lens kütüphaneleri bir birleştirici sağlar veya aynı durum odaklama efektini sağlamak için kullanılabilir, bu nedenle lensinizi doğrudan bu şekilde kodlayarak hiçbir şey kazanılmaz.
Ayrıca, başlangıçta belirtilen yan koşulların bu formda gerçekten hoş bir ifadesi yoktur. 'Fclabels'de olduğu gibi, bu, doğrudan ana pakette bir kayıt türü için lensleri otomatik olarak oluşturmak için şablon haskell yöntemi sağlar.
Category
Örneğin, barok kodlama eksikliği ve ana paketteki şablon haskell gereksinimi nedeniyle, bu benim en sevdiğim uygulama.
veri lensli
[Düzenle: 1.8.0'dan itibaren bunlar comonad-transformers paketinden veri lensine taşındı]
Benim data-lens
paketi açısından lensler sağlar Mağaza comonad.
newtype Lens a b = Lens (a -> Store b a)
nerede
data Store b a = Store (b -> a) b
Genişletilmiş bu eşdeğer
newtype Lens a b = Lens (a -> (b, b -> a))
Bunu, öğeyi almanın sonucundan oluşan bir çifti döndürmek için alıcıdan ve ayarlayıcıdan ortak argümanı ve yeni bir değer koymak için bir ayarlayıcıyı çarpan olarak değerlendirebilirsiniz. Bu, 'ayarlayıcı'nın burada değeri elde etmek için kullanılan bazı çalışmaları geri dönüştürebilir fclabels
, özellikle tanımlayıcılar zincirlendiğinde tanımdan daha verimli bir 'değiştirme' işlemi sağlar .
Bu temsil için hoş bir teorik gerekçe de vardır, çünkü bu cevabın başlangıcında belirtilen 3 yasayı karşılayan 'Lens' değerlerinin alt kümesi, sarılmış fonksiyonun mağaza komonad için bir 'comonad kömürü' olduğu lenslerdir. . Bu, bir lens için 3 tüylü kanunu l
2 güzel puansız eşdeğerine dönüştürür:
extract . l = id
duplicate . l = fmap l . l
Bu yaklaşım ilk olarak belirtildiği ve Russell O'Connor tarif edilmiş Functor
olan Lens
şekilde Applicative
etmektir Biplate
: Tanıtımı multiplate edildi ve bir ön baskı dayalı blogged hakkında Jeremy Gibbons tarafından.
Ayrıca, kesinlikle lenslerle çalışmak için bir dizi birleştirici ve kaplar için bazı stok lensleri içerir Data.Map
.
Yani data-lens
a Category
( lenses
paketin aksine ) biçimindeki lensler Haskell 98 ( fclabels
/ aksine / lenses
), aklı başında (arka ucundan farklı olarak data-accessor
) ve biraz daha verimli bir uygulama sağlıyor, data-lens-fd
dışarıya çıkmak isteyenler için MonadState ile çalışma işlevselliği sağlıyor Haskell 98, ve şablon-haskell makineleri artık mevcut data-lens-template
.
28.06.2012 Güncellemesi: Diğer Lens Uygulama Stratejileri
İzomorfizma Lensleri
Dikkate değer diğer iki lens kodlaması vardır. Birincisi, bir lensi bir yapıyı alanın değerine kırmanın bir yolu ve 'diğer her şey' olarak görmenin güzel bir teorik yolunu verir.
İzomorfizm için bir tür verilir
data Iso a b = Iso { hither :: a -> b, yon :: b -> a }
geçerli üyeler tatmin edecek hither . yon = id
veyon . hither = id
Aşağıdakileri içeren bir lensi temsil edebiliriz:
data Lens a b = forall c. Lens (Iso a (b,c))
Bunlar öncelikle lenslerin anlamını düşünmenin bir yolu olarak kullanışlıdır ve bunları diğer lensleri açıklamak için bir muhakeme aracı olarak kullanabiliriz.
van Laarhoven Lensler
Objektifleri kullanarak örnek oluşturarak (.)
ve örnek id
olmadan bile oluşturulacak şekilde modelleyebilirizCategory
type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a
bizim lensler için türü olarak.
Daha sonra bir lens tanımlamak kadar kolaydır:
_2 f (a,b) = (,) a <$> f b
ve fonksiyon kompozisyonunun lens kompozisyonu olduğunu kendiniz de doğrulayabilirsiniz.
Yakın zamanda , bu imzayı genelleştirerek, alan türlerini değiştirebilecek lens ailelerini elde etmek için van Laarhoven lenslerini nasıl daha genelleştirebileceğiniz hakkında yazdım.
type LensFamily a b c d = forall f. Functor f => (c -> f d) -> a -> f b
Bunun, lensler hakkında konuşmanın en iyi yolunun 2. sıra polimorfizmini kullanmak olduğu talihsiz bir sonucu var, ancak lensleri tanımlarken bu imzayı doğrudan kullanmanıza gerek yok.
Lens
İçin yukarıda tanımlanan I _2
aslında bir olduğunu LensFamily
.
_2 :: Functor f => (a -> f b) -> (c,a) -> f (c, b)
Lensleri, lens ailelerini ve alıcılar, ayarlayıcılar, kıvrımlar ve çapraz geçişler gibi diğer genellemeleri içeren bir kütüphane yazdım. Paket olarak hackage'de mevcuttur lens
.
Yine, bu yaklaşımın en büyük avantajı, kütüphane Functor f => (b -> f b) -> a -> f a
sahiplerinin, 'a' ve 'b' türlerine sadece tür işlevler sağlayarak herhangi bir objektif kütüphanesi bağımlılığı yaratmadan kütüphanelerinizde bu tarzda objektifler oluşturabilmeleridir . Bu, benimseme maliyetini büyük ölçüde azaltır.
Yeni lensleri tanımlamak için paketi kullanmanız gerekmediğinden, Haskell 98 kütüphanesini koruma konusundaki önceki endişelerimden çok fazla baskı gerekiyor.
lens
paket en zengin işlevsellik ve belgelere sahiptir, bu yüzden karmaşıklığını ve bağımlılıklarını önemsemiyorsanız, bu yoludur.