Haskell'de “kaldırma” nedir?


Yanıtlar:


179

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, Doublevb) 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 liftFoo1yazmak yerine

instance Functor Foo where
   fmap f foo = ...

Gerçekten tam bir düzenlilik istiyorsanız şunu söyleyebilirsiniz:

liftFoo1 = fmap

FooBir 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.Applicativemodül

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

ve benzer şekilde liftAve 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.


26
Bu asansörleri standart yasalara saygı gerektiğini muhtemelen değer hatırlatan olduğunu lift id == idve lift (f . g) == (lift f) . (lift g).
Carlos Scheidegger

13
Asansörler gerçekten de "kategori falan" dır. Carlos sadece Funktör yasaları listelenmiş nerede gelmiştir idve .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 idve .Haskell işlevler için başvuran bildiğiniz ve sevdiğiniz).
Dan Burton

3
Bu okumalı instance Functor Foo, değil instance Foo Functor, değil mi? Kendimi düzenlerdim ama% 100 emin değilim.
amalloy

2
Uygulayıcı Olmadan Kaldırma = Functor. Demek istediğim 2 seçeneğiniz var: Functor veya Uygulamalı Functor. İlk asansör tek parametre, ikinci çoklu parametre işlevlerini yerine getirir. Hemen hemen bu kadar. Sağ? Roket bilimi değil :) sadece kulağa benziyor. Harika cevap için teşekkürler btw!
jhegedus

2
@atc: Bu kısmi bir uygulamadır. Bkz. Wiki.haskell.org/Partial_application
Paul Johnson

41

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 Functorve 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 Foobir ö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!


25

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"

liftA2dü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 liftdan 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.


13

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


40
Evet, ama bu sayfa başlıyor "Genellikle (kovaryant) bir işlevle başlıyoruz ...". Tam olarak acemi dostu değil.
Paul Johnson

3
Ancak "functor" bağlıdır, bu yüzden acemi bir Functor'un ne olduğunu görmek için tıklayabilir. Kuşkusuz, bağlı sayfa o kadar iyi değil. Bir hesap almam ve bunu düzeltmem gerekiyor.
Mart'ta

10
Diğer fonksiyonel programlama sitelerinde gördüğüm bir sorun; her kavram, yeni başlayanlar tam daire çizene kadar (ve virajın etrafına) gelene kadar diğer (bilinmeyen) kavramlar açısından açıklanmaktadır. Özyinelemeyi sevmekle ilgili bir şey olmalı.
DNA

2
Bu bağlantıya oy verin. Lift bir dünya ile başka bir dünya arasında bağlantı kurar.
eccstartup

3
Bunun gibi cevaplar sadece konuyu zaten anladığında iyidir.
doubleOrt

-2

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 ave öğeleri ağaç üzerinde çilek olarak düşünüyorum Tree<a>. Bir işlevi vardır fmapbir eleman dönüştürme işlevini alır, a->bve kap functor<a>. a->bKonteynerin 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, fmapbekler functor<a>. Yani, a->btek 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->bişleminin dönüşüm için kullanmasıdır , oysa Monad kullanıcının tanımlamasını gerektirir a -> f<b>.


5
Sana bir iz bıraktım çünkü "functor bir kaptır" trol aromalı alev yemi. Örnek: birinden rbir 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 -> biş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 -> ar -> b
BMeph

1
Ayrıca, fmapbir 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 .
BMeph

1
@BMeph To wait, to expect, to anticipateeş anlamlıdır. "İşlev bekler" diyerek "işlev bekler" demek istedim.
Val

@BAnaf, bir işlevi, işleçlerin kapsayıcı olduğu fikrine karşı bir örnek olarak düşünmek yerine, işlevin aklı başına işleç örneğini, işlevlerin kapsayıcı olmadığı fikrine karşı bir örnek olarak düşünmeniz gerektiğini söyleyebilirim. İşlev, bir etki alanından bir etki alanına eşleme olup, etki alanı tüm parametrelerin çapraz ürünüdür, etki alanı işlevin çıktı türüdür. Aynı şekilde bir liste, Naturals'tan listenin iç türüne (domain -> codomain) bir eşlemedir. Fonksiyonu hatırlarsanız veya listeyi tutmazsanız daha da benzer hale gelirler.
noktalı virgül

@B: Listelerin bir kapsayıcı gibi düşünülmesinin tek nedenlerinden biri, birçok dilde mutasyona uğratılabilmeleri, geleneksel olarak işlevlerin yapamamasıdır. Ancak Haskell'de bu , ikisi de mutasyona uğratılamadığından ve her ikisi de kopya-mutasyona uğratılabileceğinden adil bir ifade değildir: b = 5 : ave f 0 = 55 f n = g nher 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.
noktalı virgül
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.