Nie rozumiem, co to jest „podnoszenie”. Czy powinienem najpierw zrozumieć monady, zanim zrozumiem, czym jest „winda”? (Ja też zupełnie nie znam monad :) A może ktoś może mi to wyjaśnić prostymi słowami?
haskell
functional-programming
GabiMe
źródło
źródło
Odpowiedzi:
Podnoszenie jest bardziej wzorcem projektowym niż koncepcją matematyczną (chociaż spodziewam się, że ktoś tutaj teraz obali mnie, pokazując, jak windy są kategorią lub czymś).
Zwykle masz pewien typ danych z parametrem. Coś jak
Załóżmy, że można zauważyć, że wiele zastosowań
Foo
wziąć typów numerycznych (Int
,Double
etc) i zachować konieczności wprowadź kod, który rozpakowuje te numery, dodaje lub pomnaża je, a następnie owija je z powrotem do góry. Możesz to zewrzeć, pisząc raz kod rozpakowywania i zawijania. Ta funkcja jest tradycyjnie nazywana „windą”, ponieważ wygląda tak:Innymi słowy, masz funkcję, która przyjmuje funkcję dwuargumentową (taką jak
(+)
operator) i zamienia ją w równoważną funkcję dla Foos.Więc teraz możesz pisać
Edycja: więcej informacji
Oczywiście można mieć
liftFoo3
,liftFoo4
i tak dalej. Jednak często nie jest to konieczne.Zacznij od obserwacji
Ale to jest dokładnie to samo, co
fmap
. Więc zamiastliftFoo1
pisaćJeśli naprawdę chcesz całkowitej regularności, możesz powiedzieć
Jeśli możesz zrobić
Foo
z niego funktor, być może możesz uczynić go funktorem aplikacyjnym. W rzeczywistości, jeśli możesz pisać,liftFoo2
to instancja aplikacyjna wygląda następująco:(<*>)
Operator Foo ma typStosuje opakowaną funkcję do opakowanej wartości. Więc jeśli możesz wdrożyć,
liftFoo2
możesz to napisać w kategoriach tego. Lub możesz to zaimplementować bezpośrednio i nie zawracać sobie głowyliftFoo2
, ponieważControl.Applicative
moduł zawierai podobnie są
liftA
iliftA3
. Ale w rzeczywistości nie używasz ich zbyt często, ponieważ jest inny operatorDzięki temu możesz napisać:
Termin
myFunction <$> arg1
zwraca nową funkcję opakowaną w Foo. To z kolei można zastosować do następnego argumentu za pomocą(<*>)
i tak dalej. Więc teraz zamiast mieć funkcję podnoszenia dla każdego rodzaju, masz po prostu łańcuch aplikacji.źródło
lift id == id
ilift (f . g) == (lift f) . (lift g)
.id
i.
są odpowiednio strzałką identyfikacyjną i kompozycją strzałek w jakiejś kategorii. Zwykle mówiąc o Haskell, kategorią, o której mowa, jest „Haskell”, którego strzałki są funkcjami Haskella (innymi słowyid
i.
odnoszą się do funkcji Haskella, które znasz i lubisz).instance Functor Foo
, nieinstance Foo Functor
, prawda? Sam bym edytował, ale nie jestem w 100% pewien.Paul i yairchu to dobre wyjaśnienia.
Chciałbym dodać, że podnoszona funkcja może mieć dowolną liczbę argumentów i nie muszą one być tego samego typu. Na przykład możesz również zdefiniować liftFoo1:
Ogólnie rzecz biorąc, podnoszenie funkcji, które przyjmują 1 argument, jest przechwytywane w klasie typu
Functor
, a operacja podnoszenia jest nazywanafmap
:Zwróć uwagę na podobieństwo z
liftFoo1
typem. W rzeczywistości, jeśli takliftFoo1
, możesz utworzyćFoo
przykładFunctor
:Ponadto uogólnienie podnoszenia na dowolną liczbę argumentów nazywane jest stylem aplikacyjnym . Nie przejmuj się tym, dopóki nie zrozumiesz podnoszenia funkcji za pomocą ustalonej liczby argumentów. Ale kiedy to zrobisz, Learn you a Haskell zawiera dobry rozdział na ten temat. Typeclassopedia to kolejny dobry dokument, który opisuje funktora i aplikacyjnych (jak również inne zajęcia typu; przewijania w dół do prawego rozdział w tym dokumencie).
Mam nadzieję że to pomoże!
źródło
Zacznijmy od przykładu (dla lepszej prezentacji dodano trochę spacji):
liftA2
przekształca funkcję zwykłych typów na funkcję tego samego typu, która jest zawinięta w ankietęApplicative
, taką jak listyIO
itp.Inna wspólna winda jest
lift
zControl.Monad.Trans
. Przekształca monadyczne działanie jednej monady w działanie przekształconej monady.Ogólnie rzecz biorąc, „lift” podnosi funkcję / akcję do typu „opakowanego” (tak więc oryginalna funkcja zaczyna działać „pod opakowaniem”).
Najlepszym sposobem na zrozumienie tego, monad itp. Oraz zrozumienia, dlaczego są one przydatne, jest prawdopodobnie kodowanie i używanie ich. Jeśli jest coś, co zakodowałeś wcześniej i podejrzewasz, że może to skorzystać (tj. Skróci to kod itp.), Po prostu wypróbuj to, a łatwo zrozumiesz koncepcję.
źródło
Podnoszenie to koncepcja, która pozwala przekształcić funkcję w odpowiadającą jej funkcję w innym (zwykle bardziej ogólnym) ustawieniu
zajrzyj na http://haskell.org/haskellwiki/Lifting
źródło
Według tego błyszczącą tutorialu , funktorem jest jakiś pojemnik (jak
Maybe<a>
,List<a>
alboTree<a>
że elementy sklepie można z jakiegoś innego typu,a
). Użyłem notacji generycznej Java<a>
dla typu elementua
i myślę o elementach jak o jagodach na drzewieTree<a>
. Istnieje funkcjafmap
, która przyjmuje funkcję konwersji elementówa->b
i kontenerfunctor<a>
. Odnosi sięa->b
do każdego elementu pojemnika, skutecznie go przekształcającfunctor<b>
. Gdy podano tylko pierwszy argumenta->b
,fmap
czeka nafunctor<a>
. Oznacza to, żea->b
samo dostarczanie zamienia tę funkcję na poziomie elementu w funkcję,functor<a> -> functor<b>
która działa na kontenerach. Nazywa się to podnoszeniemfunkcji. Ponieważ kontener nazywany jest także funktorem, Warunkiem wstępnym podnoszenia są raczej Funktory niż Monady. Monady są swego rodzaju „równolegle” do podnoszenia. Obie opierają się na pojęciu Functor i tak robiąf<a> -> f<b>
. Różnica polega na tym, że lifting wykorzystuje sięa->b
do konwersji, podczas gdy Monad wymaga od użytkownika zdefiniowaniaa -> f<b>
.źródło
r
do typu (c
użyjmy dla odmiany) to Functory. Nie „zawierają” żadnychc
. W tym przypadku fmap jest kompozycją funkcji, przyjmująca -> b
funkcję i jedynkęr -> a
, aby dać ci nowąr -> b
funkcję. Nadal żadnych pojemników, więc gdybym mógł, zanotowałbym to ponownie na ostatnie zdanie.fmap
jest to funkcja, a nie „czekać” na cokolwiek; „Kontener” będący funktorem to cały punkt podnoszenia. Ponadto, monady są, jeśli w ogóle, podwójnym pomysłem na podnoszenie: Monada pozwala ci użyć czegoś, co zostało podniesione kilka dodatnich razy, tak jakby zostało podniesione tylko raz - jest to lepiej znane jako spłaszczenie .To wait
,to expect
,to anticipate
są synonimami. Mówiąc „funkcja czeka” miałem na myśli „funkcja przewiduje”.b = 5 : a
if 0 = 55
f n = g n
oba obejmują pseudo-mutację „pojemnika”. Również fakt, że listy są zwykle przechowywane w całości w pamięci, podczas gdy funkcje są zwykle przechowywane jako obliczenia. Ale listy zapamiętywane / monorficzne, które nie są przechowywane między wywołaniami, obalają ten pomysł.