Hoogle
Hoogle este un motor de căutare pentru funcții, tipuri, clase etc. de Haskell. Puterea sa constă în abilitatea de a căuta funcții atât după nume cât și după tip.
Căutare după nume
Considerați că tocmai ați auzit de funcția Haskell scanl
, dar nu știți ce face. Căutând numele pe Hoogle (https://www.haskell.org/hoogle/?hoogle=scanl) obțineți o listă de rezultate similare; observați că hoogle face matching parțial și vă găsește și funcții numite scanl1
.
Anatomia unui rezultat
Fiecare rezultat are următoarele componente:
- pe prima linie este numele complet al funcției, urmat de tipul funcției
- click pe numele sau pe tipul funcției vă va duce la descrierea completă a acesteia din cadrul pachetului în care e definită
- următoarea linie (cu verde) conține o listă separată cu virgulă de forma “pachet modul-din-pachet” în care se găsește funcția (nu e necesar să înțelegeți bine noțiunile de “pachet” și de “modul”; cel mai des vă veți uita după funcții care conțin
base Prelude
) - următoarea linie conține o descriere a comportamentului funcției; butonul
+
de la începutul rândului expandează acest câmp, dacă descrierea este lungă
Citind descrierea funcției scanl
, observăm că aceasta operează ca foldl
, dar întoarce o listă cu toate rezultatele parțiale; astfel încât primul element al listei este acumulatorul, iar ultimul rezultatul final:
Prelude> foldl (+) 0 [14, 28, 2] 44 Prelude> scanl (+) 0 [14, 28, 2] [0,14,42,44] Prelude>
apoi 14, rezultatul operației (0 + 14)
apoi 42, rezultatul operației (14 + 28)
apoi 44, rezultatul operației (42 + 2)
Căutare după tip
Când scrieți cod (indiferent de limbaj), veți ajunge des în situația în care aveți nevoie de un anumit comportament; vreți să găsiți o funcție despre care știți ce ați vrea să facă, însă nu știți cum s-ar putea numi. Ar fi util să existe o implementare pentru această funcție în biblioteca standard; astfel vă scutiți de timpul de dezvoltare a funcției și aveți garanția unei implementări robuste, testată de mulți. Hoogle vă ajută să o găsiți, dacă există.
Exemplu
Aveți de rezolvat următoarea problemă: se dă o listă de numere întregi; trebuie să o rearanjați astfel încât toate numerele pare să se afle la începutul listei, urmate de toate numerele impare.
[1, 2, 3, 4] -> [2, 4, 1, 3] [44, 2, 13, 8, 77] -> [44, 2, 8, 13, 77] [11, 1, 3, 9, 2] -> [2, 11, 1, 3, 9]
Pentru acest task, cel mai util ar fi să existe o funcție care face exact asta; din păcate aceasta nu se găsește în biblioteca standard Haskell. Gândidu-ne la ce ne-ar fi de folos, putem ajunge la ideea că ne dorim o funcție care primește un predicat și o listă de întregi și ne întoarce un tuplu de două liste: elementele pentru care predicatul ține și restul. Funcția ar avea semnătura:
(Int -> Bool) -> [Int] -> ([Int], [Int])
Căutând acest tip pe hoogle, găsim 3 rezultate!
https://www.haskell.org/hoogle/?hoogle=%28Int+-%3E+Bool%29+-%3E+%5BInt%5D+-%3E+%28%5BInt%5D%2C+%5BInt%5D%29
Observăm că rezultatele nu au exact tipul dat de noi. Tipul la care ne-am gândit a fost mai restrictiv decât necesar, biblioteca standard oferindu-ne o soluție pentru liste de orice tip generic a
; hoogle a fost însă în stare să le găsească.
Dar ordinea parametrilor? Ce s-ar fi întâmplat dacă am fi căutat o funcție care primește întâi o listă, apoi predicatul?
https://www.haskell.org/hoogle/?hoogle=%5BInt%5D+-%3E+%28Int+-%3E+Bool%29+-%3E+%28%5BInt%5D%2C+%5BInt%5D%29
Aceleași rezultate! Hoogle reușește să găsească ce ne dorim chiar și în ciuda ordinii greșite a parametrilor.
Acum nu ne mai rămâne decât să citim descrierile funcțiilor găsite (break
, span
, partition
). Observăm că partition
are comportamentul dorit. Hoogle ne arată faptul că această funcție se află în modulul Data.List
, care trebuie importat:
Prelude> :m + Data.List Prelude Data.List> partition even [1..10] ([2,4,6,8,10],[1,3,5,7,9]) Prelude Data.List>
Concatenând cele două liste, obținem soluția problemei inițiale:
import Data.List evensFirst l = let (e, o) = partition even l in e ++ o
partition
întoarce un tuplu, dar funcția de concatenare (++
) primește pe rând două argumente - este curried. Putem rezolva problema, folosindu-ne de uncurry
.import Data.List evensFirst = uncurry (++) . partition even
Funcția uncurry (++)
are tipul ([a], [a]) -> [a]
.