Haskell: <*> nasıl telaffuz edilir? [kapalı]


109

Uygulamalı tip sınıfında bu işlevleri nasıl telaffuz edersiniz:

(<*>) :: f (a -> b) -> f a -> f b
(*>)  :: f a -> f b -> f b
(<*)  :: f a -> f b -> f a

(Yani, operatör olmasalar, ne denilebilirdi?)

Bir yan not olarak, purematematikçi olmayanlar için daha dostça bir adla yeniden adlandırabilseydiniz , buna ne derdiniz?


6
@J Cooper ... nasıl telaffuz ettiğimizi duyuyor musunuz? :) Bir ses kaydetme ve oynatma özelliği için meta.stackoverflow.com'da bir istek göndermek isteyebilirsiniz :).
Kiril

8
"Aman Tanrım, gerçekten operatörleri bitiyordu, değil mi?" Ayrıca, pureolabilir için iyi bir isim makeApplicative.
Chuck

@Lirik, Pekala, sanırım "bu şeyi ne aradın" demek istediğimi kast ediyorum :) @Chuck, pureönerinizi bir cevap olarak gönderin ve size oy vereceğim
J Cooper

6
(<*>), Control.Monad'ın "ap" sının Control.Applicative sürümüdür, bu nedenle "ap" muhtemelen en uygun addır.
Edward KMETT

11
buna tepegöz derdim, ama bu sadece benim.
RCIX

Yanıtlar:


245

Maalesef matematiğimi gerçekten bilmiyorum, bu yüzden İşlevleri Uygulamalı tip sınıfında nasıl telaffuz edeceğimi merak ediyorum

Matematiğini bilmek ya da bilmemek burada büyük ölçüde alakasız, sanırım. Muhtemelen farkında olduğunuz gibi, Haskell soyut matematiğin çeşitli alanlarından, özellikle Kategori Teorisinden , functor ve monadlardan aldığımız birkaç bitlik terminolojiyi ödünç alır . Bu terimlerin Haskell'de kullanımı, biçimsel matematiksel tanımlardan biraz farklıdır, ancak genellikle yine de iyi tanımlayıcı terimler olacak kadar yakındırlar.

ApplicativeTip sınıfı arasındaki bir yere oturur Functorve Monadbir benzer bir matematik temeli olması beklenebilir yüzden. Control.ApplicativeModülün dokümantasyonu şu şekilde başlar:

Bu modül, bir functor ve bir monad arasındaki bir yapıyı açıklar: saf ifadeler ve sıralama sağlar, ancak bağlanma yoktur. (Teknik olarak, güçlü bir gevşek monoidal funktor.)

Hmm.

class (Functor f) => StrongLaxMonoidalFunctor f where
    . . .

MonadSanırım o kadar akılda kalıcı değil .

Tüm bunların temelde özetlediği şey, matematiksel olarak Applicativeözellikle ilginç olan herhangi bir konsepte karşılık gelmemesi , dolayısıyla Haskell'de kullanılma şeklini yakalayan hazır terimlerin ortalıkta yok olmasıdır. Öyleyse, matematiği şimdilik bir kenara koyun.


Ne diyeceğimizi (<*>)bilmek istiyorsak, temelde ne anlama geldiğini bilmek yardımcı olabilir.

Öyleyse neyin var Applicative, neyse ve neden buna böyle diyoruz?

ApplicativePratikte ne anlama geliyor, keyfi işlevleri a Functor. Maybe(Muhtemelen en basit olan, önemsiz olmayan Functor) ve Bool(aynı şekilde, önemsiz olmayan en basit veri türü) kombinasyonunu düşünün .

maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not

İşlev , üzerinde çalışmaktan çalışmaya fmapgeçmemizi sağlar . Ama ya kaldırmak istersek ?notBoolMaybe Bool(&&)

maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)

Eh, bu bizim istediğimiz değil hiç ! Aslında, oldukça faydasız. Zeki olmayı deneyebilir ve Boolarkadan bir başkasını gizlice sokabiliriz Maybe...

maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)

... ama bu hiç iyi değil. Bir kere yanlış. Başka bir şey için, bu çirkin . Denemeye devam edebilirdik, ancak birden fazla argümandan oluşan bir işlevi keyfi bir üzerinde çalışmak için kaldırmanın bir yoluFunctor olmadığı ortaya çıktı . Can sıkıcı!

Kullandığımız Öte yandan, kolayca yapabileceğini Maybe'ın Monadörneği:

maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
                  y' <- y
                  return (x' && y')

Şimdi, bu sadece basit bir işlevi çevirmek için çok zahmetli - bu yüzden Control.Monadotomatik olarak yapmak için bir işlev sağlar liftM2. Adındaki 2, tam olarak iki bağımsız değişkenin işlevleri üzerinde çalıştığı gerçeğini ifade eder; benzer işlevler 3, 4 ve 5 bağımsız değişken işlevleri için mevcuttur. Bu işlevler daha iyidir , ancak mükemmel değildir ve argüman sayısını belirtmek çirkin ve beceriksizdir.

Bu da bizi Applicative type sınıfını tanıtan makaleye getiriyor . Yazarlar esasen iki gözlem yapıyorlar:

  • Çoklu argüman fonksiyonlarını a'ya kaldırmak Functorçok doğal bir şeydir
  • Bunu yapmak, bir Monad

Normal işlev uygulaması, terimlerin basit yan yana getirilmesiyle yazılmıştır, bu nedenle "kaldırılmış uygulamayı" olabildiğince basit ve doğal hale getirmek için, bu makale , uygulama için hazır bulundurulacak, içine kaldırılacak infix operatörleriniFunctor ve bunun için gerekli olanı sağlamak için bir tür sınıfını tanıtmaktadır . .

Tüm bunlar bizi şu noktaya getiriyor: (<*>)basitçe işlev uygulamasını temsil ediyor - öyleyse neden onu boşluk "yan yana operatörü" olarak yaptığınızdan farklı bir şekilde telaffuz ediyorsunuz?

Ancak bu çok tatmin edici değilse, Control.Monadmodülün aynı şeyi monadlar için de yapan bir işlev sağladığını gözlemleyebiliriz :

ap :: (Monad m) => m (a -> b) -> m a -> m b

apElbette "başvur" un kısaltması nerede . Herhangi yana Monadolabilir Applicativeve apözellikler ikincisi de mevcut yalnızca alt kümesini ihtiyacı belki söyleyebiliriz eğer (<*>)bir operatör değildi, bu çağrılmalıdır ap.


Olaylara diğer yönden de yaklaşabiliriz. FunctorKaldırma işlemi denir fmapbunun bir genelleme olduğu için maplistelerinde operasyonu. Listelerde ne tür bir işlev nasıl çalışır (<*>)? apElbette listelerde ne işe yarıyor, ama bu kendi başına pek yararlı değil.

Aslında, listeler için belki daha doğal bir yorum var. Aşağıdaki tip imzasına baktığınızda aklınıza ne geliyor?

listApply :: [a -> b] -> [a] -> [b]

Listeleri paralel olarak sıraya dizme fikrinde çok çekici bir şey var, birinci işlevin her bir işlevi ikincinin karşılık gelen öğesine uygulanması. Maalesef eski dostumuz için Monad, bu basit işlem , listeler farklı uzunluklarda ise monad yasalarını ihlal ediyor . Ama bir para cezası verir Applicative, bu durumda genelleştirilmiş bir versiyonunu bir araya(<*>) getirmenin bir yolu haline gelir , bu yüzden belki onu adlandırmayı hayal edebiliriz ?zipWithfzipWith


Bu sıkıştırma fikri aslında bize tam bir çember getiriyor. Tekoidal işlevler hakkında daha önceki matematik konularını hatırlıyor musunuz? Adından da anlaşılacağı gibi, bunlar, her ikisi de tanıdık Haskell tipi sınıflar olan monoidlerin ve functorların yapısını birleştirmenin bir yoludur:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a

Bunları bir kutuya koyup biraz sallasanız nasıl görünürdü? Dan Functorbiz fikrini tutacağız onun tipi parametresinin yapısı bağımsız ve gelen Monoidbiz fonksiyonlarının genel formunu devam edeceğiz:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ?
    mfAppend :: f ? -> f ? -> f ?

Gerçekten "boş" yaratmanın bir yolu olduğunu varsaymak istemiyoruz Functorve keyfi bir türden bir değer oluşturamayız, bu yüzden mfEmptyas tipini düzelteceğiz f ().

Ayrıca mfAppendtutarlı bir tür parametresine ihtiyaç duymaya zorlamak istemiyoruz , bu yüzden şimdi elimizde:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f ?

Sonuç türü ne için mfAppend? Hakkında hiçbir şey bilmediğimiz iki rastgele tipimiz var, bu yüzden fazla seçeneğimiz yok. En mantıklı şey, ikisini birden saklamaktır:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f (a, b)

Bu noktada mfAppend, artık açıkça ziplistelerin genelleştirilmiş bir versiyonu ve Applicativekolayca yeniden yapılandırabiliriz :

mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)

Bu aynı zamanda bize bunun purea'nın özdeşlik unsuruyla ilgili olduğunu gösterir Monoid, bu nedenle diğer iyi isimler bir birim değeri, boş bir işlem veya benzeri herhangi bir şey olabilir.


Bu uzun sürdü, özetlemek gerekirse:

  • (<*>) sadece değiştirilmiş bir işlev uygulamasıdır, bu nedenle onu "ap" veya "uygula" olarak okuyabilir veya normal işlev uygulaması gibi tamamen kaldırabilirsiniz.
  • (<*>)ayrıca zipWithlisteleri kabaca genelleştirir , böylece onu "zip functors with" olarak okuyabilirsiniz fmap, "bir functor ile eşleme" şeklinde okumaya benzer .

İlki, Applicativetip sınıfının amacına daha yakın - adından da anlaşılacağı gibi - bu yüzden tavsiye ederim.

Aslında, kaldırılan tüm uygulama operatörlerinin özgürce kullanılmasını ve telaffuz edilmemesini teşvik ediyorum :

  • (<$>), tek bağımsız değişkenli bir işlevi bir Functor
  • (<*>), çoklu argüman işlevini bir Applicative
  • (=<<), Monadmevcut bir hesaplamaya giren bir işlevi bağlayan

Her üçü de, özünde, sadece normal işlev uygulaması, biraz baharatlanmış.


6
@Colin Cochrane: "Uzun soluklu" kelimesini yanlış yazmadığınızdan emin misiniz? :) Ama hey, alacağım! Bunu her zaman hissediyorum Applicativeve teşvik ettiği işlevsel deyimsel tarz yeterince sevgiyi almıyor, bu yüzden nasıl telaffuz ettiğimi (yapmadığımı) açıklamanın bir yolu olarak erdemlerini biraz yüceltme şansına karşı koyamadım (<*>).
CA McCann


6
Keşke Haskell Applicative'ler için sözdizimi şekeri olsaydı ! Gibi bir şey [| f a b c d |](orijinal makalenin önerdiği gibi ). O zaman <*>birleştiriciye ihtiyacımız olmazdı ve böyle bir ifadeye "işlevsel bağlamda işlev uygulaması" örneği olarak başvurursunuz
Tom Crockett

1
@FredOverflow: Hayır, demek istedim Monad. Veya Functorveya Monoidüçten az sıfat içeren köklü bir terimi olan başka herhangi bir şey. "Uygulayıcı", makul ölçüde tanımlayıcı olsa da, ilham vermeyen, daha çok ihtiyaç duyulan bir şeye tokatlanmış bir addır.
CA McCann

1
@pelotom: [ stackoverflow.com/questions/12014524/… 'e bakın , nazik insanlar bana neredeyse bu notasyonu elde etmenin iki yolunu gösterdi.
AndrewC

21

CA McCann'ın teknik cevabını geliştirmek gibi bir amacım olmadığından, daha yumuşak olanı ele alacağım:

Yeniden adlandırmak olsaydı purebenim gibi podunks daha samimi bir şeye, sizce ne olur?

Alternatif olarak, özellikle Monad" return" adı verilen sürüme karşı sürekli endişe ve ihanet dolu ağlamaların sonu olmadığından, onun işlevini zorunlu programcıların en zorunluluğunu tatmin edecek şekilde öneren başka bir ad öneriyorum. , ve ... en işlevsel iyi, umarım herkes için aynı şikayet edebilirsiniz: inject.

Bir değer alın. İçine "enjekte" o Functor, Applicative, Monadveya ne-si-sen. " inject" Seçeneğine oy verdim ve bu mesajı onayladım.


4
Genelde "birim" veya "kaldırma" gibi bir şeye meylederim, ancak bunların Haskell'de zaten çok fazla anlamı var. injectmükemmel bir isim ve muhtemelen benimkinden daha iyi, ancak küçük bir yan not olarak, "enjekte" - sanırım - Smalltalk ve Ruby'de bir tür sol kat yöntemi için kullanılıyor. Yine de bu isim seçimini asla anlayamadım ...
CA McCann

3
Bu çok eski bir iş parçacığı, ama bence injectRuby & Smalltalk listedeki her eleman arasına bir operatör "enjekte ediyor" gibi olduğu için kullanıldı. En azından ben hep böyle düşünmüşümdür.
Jonathan Sterling

1
İçin tekrar o eski yan iplik pick up: Sen operatörleri enjekte, zaten orada kurucular (ortadan kaldırarak) yenileyeceğiz değiliz. (Diğer taraftan bakıldığında, eski verileri yeni bir türe enjekte ediyorsunuz .) Listeler için, eleme sadece foldr. (Değiştirirsiniz (:)ve []burada (:)2 []bağımsız değişkeni alır ve sabittir, dolayısıyla foldr (+) 0 (1:2:3:[])1+2+3+0.) BoolSadece if- then- else(iki sabit, birini seçin) ve Maybeadı olduğu için maybe… Haskell'in bunun için tek bir adı / işlevi yoktur, çünkü hepsinin farklı türleri vardır (genel olarak elim sadece özyineleme / tümevarımdır)
kimse

@CAMcCann Smalltalk, bu ismi, talihsiz gençlerin toplandığı, seçildiği, bazen reddedildiği ve başka türlü enjekte edildiği Vietnam savaşı taslağıyla ilgili bir Arlo Guthrie şarkısından aldı .
Tom Anderson

7

Kısaca:

  • <*>uygulayabilirsiniz diyebilirsiniz . Yani Maybe f <*> Maybe aolarak telaffuz edilebilir uygulamak Maybe füzerindeMaybe a .

  • Sen adlandırmak olabilir pureiçin ofbirçok JavaScript kütüphaneleri yapmak gibi. JS size bir oluşturabilir Maybeile Maybe.of(a).

Ayrıca, Haskell'in wiki'sinde dil operatörlerinin telaffuzuna ilişkin bir sayfa var burada


3
(<*>) -- Tie Fighter
(*>)  -- Right Tie
(<*)  -- Left Tie
pure  -- also called "return"

Kaynak: First Principles'tan Haskell Programlama , Chris Allen ve Julie Moronuki


Anladığım kadarıyla bu isimler tam olarak anlaşılmadı.
dfeuer

@dfeuer, bu kitabı birincil öğrenme materyali olarak kullanan yeni nesil Haskellers'ı bekliyor.
dmvianna

1
Olabilir. Yine de isimler, anlamlarla hiçbir bağlantıları olmadığı için berbat.
dfeuer

1
@dfeuer, hiçbir yerde iyi bir çözüm göremiyorum. "ap" / "uygulamak", "kravat dövüşçüsü" kadar belirsizdir. Her şey işlev uygulamasıdır. Ancak maviden çıkan bir isim kullanımla anlam kazanabilir. "Elma" buna iyi bir örnek. Bu arada, Monad'ın iadesi tamamen Uygulamalı. Burada icat yok.
dmvianna
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.