Yanıtlar:
Kaldırma, bir matematiksel kavramdan ziyade bir tasarım modelidir (buradaki birisinin artık asansörlerin bir kategori veya başka bir şey olduğunu göstererek beni çürütmesini bekliyorum).
Genellikle bir parametre ile bazı veri türleriniz vardır. Gibi bir şey
data Foo a = Foo { ...stuff here ...}
Eğer kullanımlarının çok bulmak varsayalım Foo
(sayısal türlerini dikkate alırız Int
, Double
vb) ile, bu numaraları unwraps ekler veya çarpar ve sonra onları yedeklemek sarar yazma koduna sahip tutun. Paketten çıkarma ve sarma kodunu bir kez yazarak buna kısa devre yapabilirsiniz. Bu işleve geleneksel olarak "asansör" denir, çünkü şöyle görünür:
liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
Başka bir deyişle, iki bağımsız değişkenli bir işleve (örneğin, (+)
işleç ) alıp Foos için eşdeğer işleve dönüştüren bir işleviniz vardır.
Şimdi yazabilirsiniz
addFoo = liftFoo2 (+)
Düzenle: daha fazla bilgi
Elbette sahip olabilirsiniz liftFoo3
,liftFoo4
vb. Ancak bu genellikle gerekli değildir.
Gözlemle başlayın
liftFoo1 :: (a -> b) -> Foo a -> Foo b
Ama bu tam olarak aynı fmap
. Yani liftFoo1
yazmak yerine
instance Functor Foo where
fmap f foo = ...
Gerçekten tam bir düzenlilik istiyorsanız şunu söyleyebilirsiniz:
liftFoo1 = fmap
Foo
Bir fonktor haline getirebiliyorsanız , belki de onu pratik bir fonktor yapabilirsiniz. Aslında, yazabiliyorsanız liftFoo2
, uygulanabilir örnek şöyle görünür:
import Control.Applicative
instance Applicative Foo where
pure x = Foo $ ... -- Wrap 'x' inside a Foo.
(<*>) = liftFoo2 ($)
(<*>)
Foo için operatör türüne sahip
(<*>) :: Foo (a -> b) -> Foo a -> Foo b
Sarılmış işlevi sarılmış değere uygular. Eğer uygulayabiliyorsanız liftFoo2
, bunu bu açıdan yazabilirsiniz. Veya doğrudan uygulayabilir ve rahatsız etmeyin liftFoo2
, çünkü Control.Applicative
modül
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
ve benzer şekilde liftA
ve vardır liftA3
. Ama aslında onları çok sık kullanmıyorsunuz çünkü başka bir operatör var
(<$>) = fmap
Bu şunları yazmanıza izin verir:
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
Bu terim myFunction <$> arg1
, Foo içine alınmış yeni bir işlev döndürür. Bu, sırayla, (<*>)
vb. Kullanılarak bir sonraki bağımsız değişkene uygulanabilir . Yani şimdi her çağ için bir kaldırma fonksiyonuna sahip olmak yerine, papatya uygulama zinciriniz var.
lift id == id
ve lift (f . g) == (lift f) . (lift g)
.
id
ve .
kimlik ok ve sırasıyla bazı kategorideki bileşimini ok vardır. Haskell bahsederken Genellikle, söz konusu kategori olan oklar olan Haskell fonksiyonları (diğer bir deyişle, "Hask" dir id
ve .
Haskell işlevler için başvuran bildiğiniz ve sevdiğiniz).
instance Functor Foo
, değil instance Foo Functor
, değil mi? Kendimi düzenlerdim ama% 100 emin değilim.
Paul ve yairchu'nun her ikisi de iyi açıklamalardır.
Kaldırılan işlevin isteğe bağlı sayıda bağımsız değişkene sahip olabileceğini ve aynı türden olmaları gerekmediğini eklemek isterim. Örneğin, bir liftFoo1 de tanımlayabilirsiniz:
liftFoo1 :: (a -> b) -> Foo a -> Foo b
Genel olarak, 1 argüman alan işlevlerin kaldırılması tip sınıfında yakalanır Functor
ve kaldırma işlemi çağrılır fmap
:
fmap :: Functor f => (a -> b) -> f a -> f b
Türüyle benzerliğe dikkat edin liftFoo1
. Aslında, eğer varsa liftFoo1
, aşağıdakilerin Foo
bir örneğini yapabilirsiniz Functor
:
instance Functor Foo where
fmap = liftFoo1
Dahası, keyfi sayıda argümana kaldırmanın genelleştirilmesine uygulanabilir stil denir . Sabit sayıda argümanla işlevlerin kaldırılmasını kavrayacak kadar dalış yapmayın. Ama bunu yaptığınızda, Size bir Haskell öğrenin bu konuda iyi bir bölüm var. Typeclassopedia açıklanır başka iyi belgedir funktoru ve uygulamalı (hem de diğer tip sınıfları; kaydırma Söz konusu belgenin sağ bölüme aşağı).
Bu yardımcı olur umarım!
Bir örnekle başlayalım (daha net sunum için bir miktar beyaz boşluk eklenir):
> import Control.Applicative
> replicate 3 'a'
"aaa"
> :t replicate
replicate :: Int -> b -> [b]
> :t liftA2
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
> :t liftA2 replicate
liftA2 replicate :: (Applicative f) => f Int -> f b -> f [b]
> (liftA2 replicate) [1,2,3] ['a','b','c']
["a","b","c","aa","bb","cc","aaa","bbb","ccc"]
> ['a','b','c']
"abc"
liftA2
düz türlerin bir işlevini, listeler vb. gibi sarılı aynı türdekiApplicative
bir işleve dönüştürür IO
.
Başka bir ortak asansör lift
dan Control.Monad.Trans
. Bir monadın monadik hareketini, dönüştürülmüş bir monadın hareketine dönüştürür.
Genel olarak, "lift" bir işlevi / eylemi "kaydırılmış" türe kaldırır (böylece orijinal işlev "kaydırmalar" altında çalışmaya başlar).
Bunu ve monadları vb. Anlamanın ve neden faydalı olduklarını anlamanın en iyi yolu muhtemelen kodlamak ve kullanmaktır. Daha önce kodladığınız ve bundan fayda sağlayacağından şüphelendiğiniz bir şey varsa (yani, bu kodu kısaltacaktır, vb.), Sadece deneyin ve konsepti kolayca kavrayacaksınız.
Kaldırma, bir işlevi başka (genellikle daha genel) bir ortamda karşılık gelen bir işleve dönüştürmenizi sağlayan bir kavramdır
http://haskell.org/haskellwiki/Lifting adresine bir göz atın
Göre bu parlak öğretici bir funktor bir kap (gibidir Maybe<a>
, List<a>
ya da Tree<a>
bir başka tür bu kutu deposu elemanları a
). <a>
Öğe türü için Java jenerik gösterimini kullandım a
ve öğeleri ağaç üzerinde çilek olarak düşünüyorum Tree<a>
. Bir işlevi vardır fmap
bir eleman dönüştürme işlevini alır, a->b
ve kap functor<a>
. a->b
Konteynerin etkili bir şekilde dönüştürülmesi için her eleman için geçerlidir functor<b>
. Sadece ilk argüman sağlandığında a->b
, fmap
bekler functor<a>
. Yani, a->b
tek başına tedarik , bu eleman düzeyindeki işlevi functor<a> -> functor<b>
kaplar üzerinde çalışan işleve dönüştürür . Buna kaldırma denirfonksiyon. Konteynere functor da denildiğinden , Monad yerine Functors kaldırma için bir ön koşuldur. Monadlar kaldırmaya bir çeşit "paralel" dir. Her ikisi de Functor kavramına güvenir ve yaparlar f<a> -> f<b>
. Fark, kaldırma a->b
işleminin dönüşüm için kullanmasıdır , oysa Monad kullanıcının tanımlamasını gerektirir a -> f<b>
.
r
bir türe fonksiyonlar ( c
çeşitlilik için kullanalım ), Functor'lardır. Herhangi bir "içermez" c
. Bu örnekte, fmap size yeni bir işlev vermek için bir a -> b
işlev ve bir işlev alan işlev bileşimidir . Yine de kap yok.Ayrıca, yapabilseydim, son cümle için tekrar işaretlerdim. r -> a
r -> b
fmap
bir işlevdir ve hiçbir şey için "beklemez"; "Konteyner" bir Functor olmak , tüm kaldırma noktasıdır . Ayrıca, Monad'lar, herhangi bir şey varsa, kaldırmak için çift bir fikirdir: Bir Monad, sadece bir kez kaldırılmış gibi, olumlu sayıda kez kaldırılmış bir şeyi kullanmanıza izin verir - bu düzleştirme olarak bilinir .
To wait
, to expect
, to anticipate
eş anlamlıdır. "İşlev bekler" diyerek "işlev bekler" demek istedim.
b = 5 : a
ve f 0 = 55
f n = g n
her ikisi de, "konteynerin" sahte mutasyonunu içerir. Ayrıca listelerin tipik olarak tamamen hafızada saklanması, fonksiyonlar ise tipik olarak bir hesaplama olarak saklanır. Ancak, aramalar arasında saklanmayan not yazma / monorfik listelerin her ikisi de bu fikrin saçmalıklarını kırıyor.