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].
