===== Haskell Environment ===== ===== Setup ===== Primul pas spre a putea învăța Haskell este să avem o modalitate de a rula codul scris. Puteți descărca platforma Haskell de [[https://www.haskell.org/platform|aici]] (Windows/OS X/Linux) sau puteți căuta pachetul "ghc" în repourile distribuției voastre. Cel mai probabil, ar trebui să rulați: $ sudo apt-get install ghc [[https://wiki.haskell.org/GHC|GHC]] este un compilator de Haskell, similar cu gcc. Pentru început, nu vom compila programe, ci vom rula în modul interactiv, care ne permite să apelăm funcții din consolă și să vedem rezultatul lor. Putem să încărcăm și fișiere cu fragmente de cod din care să apelăm funcții definite de noi. ===== GHCi ===== Pentru modul interactiv, rulați ''ghci''. Ar trebui să vedeți ceva asemănător: GHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help Prelude> Mesajul constă în numele interpretorului și versiunea, urmat de un link către siteul oficial. A doua linie este promptul interactiv; "Prelude" este numele unui modul default care vă pune la dispoziție foarte multe tipuri (e.g. ''Bool'') și funcții (e.g. ''length'') utile. Orice expresie scrieți este evaluată, iar rezultatul afișat pe următoarea linie: Prelude> 2 + 2 4 Prelude> Deasemena tot în GHCi, puteți defini direct variabile sau funcții: Prelude> patru = 4 Prelude> patru 4 Prelude> plus a b = a + b Prelude> plus 2 2 4 Prelude> ==== it ==== GHCi pune la dispoziție un nume special, ''it'', care reprezintă rezultatul ultimei expresii //evaluate corect//. Prelude> 2 + 2 4 Prelude> it + 1 5 Prelude> it + 1 6 Prelude> "this " "this " Prelude> it ++ "is handy" "this is handy" Valoarea lui ''it'' nu este alterată în cazul unei erori, ceea ce funcționează ca o plasă de siguranță, pentru a nu avea nevoie să reevaluăm expresia, atunci când greșim: Prelude> 2 + 2 4 Prelude> "result is " + it :2:1: error: • No instance for (Num [Char]) arising from a use of ‘+’ • In the expression: "result is " + it In an equation for ‘it’: it = "result is " + it Prelude> "result is " ++ show it "result is 4" Prelude> ==== Comenzi GHCi ==== GHCi acceptă comenzi în formatul '': '', care ajută cu încărcarea codului, inspectarea acestuia și înțelegerea limbajului. Comenzile pot fi invocate cu numele complet, sau cu un subșir al acestuia. În tabelul de mai jos, se găsesc cele mai utile comenzi și o explicație a acestora (tot ce e între paranteze pătrate face parte din numele complet al comenzii, dar nu e necesar): ^ Comandă ^ Efect ^ |:cd //// | schimbă directorul curent al GHCi | |:l[oad] //// //// ... | încarcă fișiere Haskell în GHCi (alte fișiere încărcate a priori vor fi descărcate) | |:r[eload] | echivalentul la a rula din nou ultima comandă de "load" | |:m[odule] + //// //// ... | importă modulele* în GHCi (fără a afecta modulele deja importate) | |:i[nfo] //// | afișează informații despre un nume (e.g. funcție, tip, constructor de tip*, clasă*) | |:t[ype] //// | afișează tipul expresiei | |:set +t | afișează tipul fiecărei expresii imediat după, ca și cum ați da comanda ":type it" | |:k[ind] //// | afișează //kind//*-ul unui tip | |:bro[wse] Main | afișează toate definițiile scrise de voi în fișierul încărcat | |:e[dit] | deschide ultimul fișier încărcat în editorul default (după închiderea editorului, se revine în GHCi) | |:h[elp] sau :? | afișează meniul de help cu toate comenzile disponibile | |:q[uit] | închide GHCi | * - nu vă faceți griji dacă nu întâlniți aceste concepte la primele laboratoare. ==== Exemplu de sesiune interactivă ==== Considerăm urmatorul fișier, numit ''work.hs'': -- work.hs data Tree a = Empty | Node (Tree a) (Tree a) constantThree = '3' mySum :: Int -> Int -> Int mySum a b = a + b În ghci: Prelude> :l work.hs [1 of 1] Compiling Main ( work.hs, interpreted ) Ok, one module loaded. *Main> :browse Main type role Tree phantom data Tree a = Empty | Node (Tree a) (Tree a) constantThree :: Char mySum :: Int -> Int -> Int *Main> :k Tree Tree :: * -> * *Main> :i mySum mySum :: Int -> Int -> Int -- Defined at work.hs:7:1 *Main> isDigit constantThree :28:1: error: Variable not in scope: isDigit :: Char -> t *Main> :m + Data.Char *Main Data.Char> isDigit constantThree True *Main Data.Char> constantThree '3' *Main Data.Char> :t isDigit isDigit :: Char -> Bool *Main Data.Char> :q ===== Programe stand-alone ===== Pentru a face programe utile, de sine-stătoare, care nu depind de mediul interactiv din GHCi, este nevoie de intearcțiune cu lumea externă (argumente, fișiere, variabile de mediu etc). Interacțiunea cu lumea externă în Haskell este un concept stufos și complex care depășește nivelul cursului de Paradigme de Programare. Totuși, pentru cei curioși, puteți începe de aici: http://learnyouahaskell.com/input-and-output http://book.realworldhaskell.org/read/io.html ==== Useful snippets ==== Fără însă a intra în detalii despre ''IO'', puteți scrie diverse programe utile pornind de la următoarele două snippeturi, doar respectând interfața funcției ''myFunction'' și înlocuindu-i definiția cu logica implementată de voi: {- - "myFunction" este punctul de intrare în "programul" vostru. - - Funcția primește un șir care reprezintă întreg conținutul stdin și întoarce - un șir care va fi scris la stdout. Pentru a lucra cu alte fișiere, puteți folosi - redirectări de shell: - - $ ./program < input.txt > output.txt - - În final, rezultatul vostru va trebui să fie un șir (hint: căutați funcția - "show") -} myFunction :: String -> String myFunction = undefined -- your logic here main = do input <- getContents putStr (myFunction input) import System.Environment (getArgs) {- - "myFunction" este punctul de intrare în "programul" vostru. - - Funcția primește o listă de șiruri care reprezintă argumentele date - programului în linia de comandă. Spre deosebire de C, aceasta nu conține și - numele programului, deci poate fi și goală. Rezultatul funcției va fi scris - la stdout. Pentru a lucra cu alte fișiere, puteți folosi redirectări de shell: - - $ ./program arg1 arg2 arg3 > output.txt - - În final, rezultatul vostru va trebui să fie un șir (hint: căutați funcția - "show") -} myFunction :: [String] -> String myFunction = undefined -- your logic here main = do args <- getArgs putStr (myFunction args) ==== Hello, world! ==== De exemplu, puteți scrie următorul corp de funcție (în oricare dintre snippeturi, deoarece își ignoră parametrul): myFunction _ = "Hello, world!\n" Presupunând că fișierul ce conține ''main'' se intitulează ''main.hs'', îl putem compila astfel: $ ghc main.hs -o myFirstProgram [1 of 1] Compiling Main ( main.hs, main.o ) Linking myFirstProgram ... $ file myFirstProgram myFirstProgram: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=bcaf317a9ff9eb7b3cf09e9cae 337d10eeb268c3, not stripped $ ./myFirstProgram Hello, world! $ Felicitări, ați obținut primul program Haskell! În loc să compilăm si să obținem un executabil, putem să interpretăm neinteractiv programul, folosind ''runghc'' sau ''runhaskell'': $ runhaskell ./main.hs Hello, world! $