2013-02-22 22 views
32

Functors'lar için fmap . fmap'u anlıyorum, ancak işlevler için başımı şimdi aylardır ağrıyor."(.). (.)" Yi nasıl anlarım?

(.) tanımını (.) . (.)'a uygulayabileceğinizi gördüm, ancak bunu nasıl yapacağımı unutmuşum.
Ben kendim hep yanlış çıkıyor çalıştığınızda:

(.) f g = \x -> f (g x) 
(.) (.) (.) = \x -> (.) ((.) x) 
\x f -> (.) ((.) x) f 
\x f y -> (((.)(f y)) x) 
\x f y g-> (((.)(f y) g) x) 
\x f y g-> ((f (g y)) x) 
\x f y g-> ((f (g y)) x):: t2 -> (t1 -> t2 -> t) -> t3 -> (t3 -> t1) -> t 

Eğer bunu yapmanın tek yolu "sadece tanımı uygulayarak", nasıl herkes (.) . (.) ile geldi?
Eksik olduğum daha derin bir anlayış veya sezgi olmalı.

+4

@lordlupine gözlüklerle bir boncuk gözlü düğme burunlu adam? –

+0

@Will Ness Haklısınız ..: P –

+2

@WillNess: Baykuş-With-Gözlük Combinator – amindfv

cevap

16

Ayrıca fmap . fmap anlayışınızı kullanabilirsiniz:

result :: (b -> c) -> ((a -> b) -> (a -> c)) 
result = (.) 

Şimdi, yani sadece bir fonksiyon bileşimi bu bağdaştırıcılarla bu kompozisyonu (bizim kelime) çıkıyor. daha sonra, iki Functor s foo ve bar varsa

fmap . fmap :: (a -> b) -> foo (bar a) -> foo (bar b) 

fmap . fmap

bir işlev alır ve iki Functor s bileşimi için bir neden fonksiyonu oluşturur.

Şimdi, her tür t için, (->) t bir Functor olduğunu ve bunun için fmapFunctor(.) olduğunu.

Yani (.) . (.), iki Functor ler (->) s ve (->) t olan durum için fmap . fmap ve böylece

(.) . (.) :: (a -> b) -> ((->) s) ((->) t a) -> ((->) s) ((->) t b) 
      = (a -> b) -> (s -> (t -> a))  -> (s -> (t -> b)) 
      = (a -> b) -> (s -> t -> a)  -> (s -> t -> b) 

iki argüman, g :: s -> t -> a bir fonksiyonu olan bir işlev f :: a -> b "oluşturan" dir

((.) . (.)) f g = \x y -> f (g x y) 

Bu görünüm aynı zamanda, modelin daha fazla argüman alma işlevine doğru uzandığını ve nasıl açıldığını açıkça belirtir,

(.) . (.) . (.) :: (a -> b) -> (s -> t -> u -> a) -> (s -> t -> u -> b) 

vb

+0

Yani 'foo (bar a) 'işlevinin bir işlevi olduğunu söyleyebilirim ?! Ahh :) – SomeName

+0

@FabianGerhardt 'foo' ve' bar' burada iki * tip * değişkenlerdir. foo (çubuk a), "s" işlevinden "t" - "a" arasında bir işlevin "* tipini" belirtir. Eğer bir fonksiyon tipini belirtmek isteniyorsa, bu sadece bir fonksiyonun * fonksiyonu olacaktır *. –

3

Yani, bu ben biraz daha artan olanaklarını geniş yaparken ne alıyorum

(.) f g = \x -> f (g x) 
(.) . g = \x -> (.) (g x) 
      = \x -> \y -> (.) (g x) y 
      = \x -> \y -> \z -> (g x) (y z) 
      = \x y z -> (g x) (y z) 
(.) . (.) = \x y z -> ((.) x) (y z) 
      = \x y z -> \k -> x (y z k) 
      = \x y z k -> x (y z k) 

hangisi, GHCi göre doğru türe sahip

Prelude> :t (.) . (.) 
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c 
Prelude> :t \x y z k -> x (y z k) 
\x y z k -> x (y z k) 
    :: (t1 -> t) -> (t2 -> t3 -> t1) -> t2 -> t3 -> t 
Prelude> 

Ben kökenlerini bilmiyoruz Bu birleştiricinin, , kombinator mantığında, ile çalıştığınız, böylece daha uygun lambda ifadeleri kullanarak bir şeyleri tanımlayamayacağınız şekilde, geliştirilmiş mantıksal bir mantıkla kullanılmak üzere geliştirilmiştir. , bu şeyleri anlatan bazı sezgiler olabilir, ama bunu bulamadım. Büyük ihtimalle, bunu yapmak zorunda kalmanız halinde bir miktar sezgi geliştirmiş olursunuz.

4

y'u tanıttığınızda çözümünüz farksız. Olması gereken

orijinal tip imzasını maçları
\x f y -> ((.) ((.) x) f) y  :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
\x f y z -> ((.) ((.) x) f) y z :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
\x f y z -> ((.) x (f y)) z  :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
-- Or alternately: 
\x f y z -> (x . f y) z   :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
\x f y z -> (x (f y z))   :: (c -> d) -> (a -> b -> c) -> a -> b -> d 

: (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d

(Size :t expression her adımı kontrol edebilirsiniz GHCi, genişleme yapmak çok kolaydır)

Düzenleme:

:

derin intution şudursadece biz

\f g x -> f (g x) 

için kolaylaştırabilirsiniz Hangi

\f g -> \x -> f (g x) 

olarak tanımlanan iki argüman bunu tedarik öldüğünde, curried var ve hala çözmek için başka argüman gerekiyor. 2 argümanla (.)'u her kullandığınızda, bir argüman için "ihtiyaç" yaratırsınız.

(\f0 g0 x0 -> f0 (g0 x0)) (\f1 g1 x1 -> f1 (g1 x1)) (\f2 g2 x2 -> f2 (g2 x2)) 

Biz f0 ve g0 beta-azaltabilir (ama biz bir x0 yok:

(.) . (.)

elbette sadece (.) (.) (.), o yüzden genişletmek edelim!):

\x0 -> (\f1 g1 x1 -> f1 (g1 x1)) ((\f2 g2 x2 -> f2 (g2 x2)) x0) 

Yedek f1 için 2 ifadesi ...

\x0 -> \g1 x1 -> ((\f2 g2 x2 -> f2 (g2 x2)) x0) (g1 x1) 

Şimdi "ters takla"! ( f2 üzerinde beta azaltma):
Bu ilginç bir adımdır - x0, f2 için kullanılır - Bu, veri olabilecek x'un bir işlev olduğu anlamına gelir. Ekstra argüman için "ihtiyaç" -
Bu (.) . (.) sağlar şeydir.
\x0 -> \g1 x1 -> (\g2 x2 -> x0 (g2 x2)) (g1 x1) 

Bu

... edelim ( g2 üzerine) bir son kez beta azaltmak, normal görünmeye başlıyor:
\x0 -> \g1 x1 -> (\x2 -> x0 ((g1 x1) x2)) 

Yani biz sadece

\x0 g1 x1 x2 -> x0 ((g1 x1) x2) 

kalacaksın argümanların hala güzel olduğu yerler.

+1

gerçekten, bu çok daha basit [birleştirici denklemlerle] (https://gist.github.com/WillNess/5016202). Gerçekten mi. :) değil mi? (Bunu Davie'den aldım, "Haskell Kullanarak İşlevsel Programlama Sistemlerine Giriş"). –

+0

@WillNess Kesinlikle daha basit, ama etrafta uçan tüm argümanları daha kolay anlayabiliyorum. Bu gerçekten iyi bir kitap gibi gözüküyor! Daha önce hiç duymamıştım. – amindfv

+0

biraz modası geçmiş, ancak * "erken günler" * çekiciliğine sahip. Yine de AFAIR, Monad'lara sahip değil. –

3

yerine lamda ifadelerin, combinators-style, denklemleri yazmaya en kolay: a b c = (\x -> ... body ...)a b c x = ... body ... eşdeğerdir, ve tersi, x{a,b,c} arasında görünmez şartıyla. Yani,

-- _B = (.) 

_B f g x = f (g x) 
_B _B _B f g x y = _B (_B f) g x y 
       = (_B f) (g x) y 
       = _B f (g x) y 
       = f ((g x) y) 
       = f (g x y) 

Sen f (g x y) verilen bu, bunu into a combinatory form (bütün parantez ve değişken tekrarlar kurtulmak) dönüştürmek istediğiniz keşfederler. Sonra umarım bu türetme geriye izleme, combinators' tanımlarına uyan desenleri geçerlidir. Bu olsa da çok daha az mekanik/otomatik. (.) . (.) ile geliyor

35

o anlamak oldukça zor olduğunu ne yapar arkasında, önsezilerim aslında oldukça basittir.

(.) çok uzakta olduğunda "boru" tarzı hesaplamaları içine ifadesini yeniden yazma (kabukta | düşünün) alır. Ancak, yalnızca bir tane alan bir fonksiyon ile birden argüman alan bir fonksiyon oluşturmak için denemek kez kullanımı zor hale gelir. Bir örnek olarak, en concatMap bir tanımını yapalım:

concatMap f = concat . map f 

Ancak f kurtulmanın hayır "hoş" bir yolu vardır:

xs kurtulmak
concatMap :: (a -> [b]) -> [a] -> [b] 
concatMap f xs = concat (map f xs) 

sadece standart bir işlemdir. Bu map iki argüman alır ve nihai sonuca concat başvurmak istediğinizi gerçeğiyle kaynaklanır.

Sen elbette bazı pointfree hileler uygulamak ve sadece (.) ile alabilirsiniz:

concatMap f = (.) concat (map f) 
concatMap f = (.) concat . map $ f 
concatMap = (.) concat . map 
concatMap = (concat .) . map 

Ama ne yazık ki, bu kodun okunabilirliği çoğunlukla gitti.Birincisinin nihai sonuç ikinci işlevi geçerlidir: Bunun yerine, tam ihtiyacımız olan şey gelmez yeni bir bağdaştırıcının, tanıtmak.

-- .: is fairly standard name for this combinator 
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d 
(f .: g) x y = f (g x y) 

concatMap = concat .: map 

Güzel, bu motivasyon için. Gelinen işlere gidelim. Buradaki ilginç kısım geliyor. Şimdi, buradaki ilginç kısım geliyor. Bu, genellikle takılıp kaldığınızda yardımcı olan nokta hileleridir: .'u önek formuna yeniden yazıp oradan devam etmeye çalışın.

 = \f g x -> (.) f (g x) 
    = \f g x -> (.) f . g $ x 
    = \f g  -> (.) f . g 
    = \f g  -> (.) ((.) f) g 
    = \f  -> (.) ((.) f) 
    = \f  -> (.) . (.) $ f 
    =    (.) . (.) 

Algılamaya gelince, okumanız gereken bu very nice article vardır. Ben (.) ilgili kısmı tefsir edeceğiz:

en bizim combinator ne yapması gerektiğini tekrar düşünelim: Bu sonucung ait (I nihai sonucu kullanıyorum sonuca f uygulamalıdır Tam olarak uygulandığında gerçekten tam olarak ne alırsanız yapın - başka bir işlev türü ile tür değişkenleri birleştiren modulo - g işlevi, sonucu, yalnızca için yalnızca g x uygulamasıdır.

fsonucunug sonucuna nasıl uygulayalım? Eh, g'u bir kez uyguladıktan sonra, sonucu alacağız ve buna f uygulayacağız. Tanıdık geliyor: (.)'un yaptığı şey bu.

(.:) = result . result -- the result of result 
+0

Bu cevabı gerçekten çok beğendim ve bana yardımcı oldu. Yapabilseydim bunu da işaretlerdim. – SomeName

İlgili konular