llenguatge de programació From Wikipedia, the free encyclopedia
Haskell és un llenguatge de programació funcional estandarditzat de semàntica no estricta i avaluació tardana de les expressions (ang: lazy evaluation) en el moment que se'n demana el valor i pren el nom del matemàtic Haskell Curry.
Es diu que és un llenguatge funcional pur. El cert és que admet variables d'estat però permet encapsular-ne els canvis (context ST), o bé circumscriure'n els efectes col·laterals al nivell superficial (context IO).
Haskell basa el polimorfisme en el requeriment d'implementació d'interfícies pels tipus dels paràmetres. Les interfícies amb un paràmetre de tipus t defineixen una partició de l'espai dels tipus en classes segons si les implementen o no, i per això s'anomenen classes de tipus.
-- classe de tipus (la interfície)
class Eq t where
(==) :: t -> t -> Bool -- iguals
(/=) :: t -> t -> Bool -- desiguals
-- implementació per defecte
x == y = not (x /= y)
x /= y = not (x == y)
-- caldrà especificar només una de les operacions en definir la implementació
data Bool = False | True -- definició del tipus Bool
-- instància (la implementació) de la classe Eq per al tipus Bool
instance Eq Bool where
(==) False False = True
(==) True True = True
(==) _ _ = False
-- definició del tipus (Llista a) = Nil | Cons a (Llista a)
-- els símbols '[' i ']' designen una llista
data [a] = [] -- el constructor Nil es denota amb "[]"
| a : [a] -- el constructor Cons es denota amb ':' en posició infix
-- * per sintaxi, si un símbol comença per ':', va infix
-- * tot identificador de funció es pot posar en infix si s'envolta de cometes revesses
-- exemple d'ús: "Eq t =>" es llegeix: per aquells tipus t tals que (Eq t)
ésMembre :: Eq t => t -> [t] -> Bool --
ésMembre x [] = False
ésMembre x (cap : cua) = x == cap
|| x `ésMembre` cua -- notació infix amb cometes revesses
Derivació automàtica d'instàncies (implementacions d'interfícies): També podem demanar al compilador que, per a classes de tipus bàsiques, derivi una instància partint de la representació interna del tipus (clàusula deriving en la definició de tipus estructurals (clàusula data).
data Color = Verd | Blau | Lila deriving (Eq, Show) -- el compilador deriva instàncies de les classes esmentades, obtenint la posició i el nom dels valors
Els literals no tenen tipus associat sinó que poden pertànyer a aquells tipus que implementin una determinada classe:
$ ghci -- a l'intèrpret
Prelude> :t 1
1 :: Num a => a -- 1 pot assignar-se a vars. d'aquells tipus que implementin la classe Num (que correspon a l'estructura algebraica d'anell)
Prelude> :t 1.5
1.5 :: Fractional a => a -- 1.5 pot assignar-se a vars. d'aquells tipus que implementin la classe Fractional (que correspon a l'estructura algebraica de cos)
-- la clàusula "default" permet l'assignació de tipus tradicional als literals, esmentant una seqüència de tipus a provar
default (Int, Double)
La sobrecàrrega de literals fa possible incorporar-los a diferents estructures:
{-# LANGUAGE OverloadedStrings, OverloadedLists #-}
import Data.Semigroup((<>))
import Data.Text (Text) -- text com a vectors de UTF-16
import Data.Set (Set)
import Data.Map (Map)
tiraDeText = ("abc" :: Text) <> "def"
llista = ([1..3] :: [Int]) <> [4..6]
cjt = ([1..3] :: Set Int) <> [4..6]
dicc = ([(1,"a"), (2, "b")] :: Map Int String) <> [(3,"c")]
Tipus derivats: Corresponent a la derivació de classes de la P.O.O. Haskell possibilita la derivació de tipus amb la declaració newtype, heretant del tipus base les implementacions d'aquelles classes de tipus que esmentem a la clàusula deriving. Caldrà l'extensió de llenguatge GeneralizedNewtypeDeriving.[24]
Les implementacions no tenen nom. Un tipus no pot tenir diverses implementacions d'una classe de tipus, per ex. diverses ordenacions o diversos monoides per un mateix tipus. Caldrà crear-ne un tipus derivat amb newtype per cadascuna de les implementacions.
{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- amplia les instàncies heretables del Haskell98
-- tipus derivat per implementar un Monoide per a la suma, parametritzat pel tipus base
newtype Sum a = Sum { getSum :: a }
deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num) -- classes de les instàncies a heretar
-- el constructor obté, del tipus base, el tipus derivat
-- l'accessor obté, del tipus derivat, el tipus base
instance Num a => Monoid (Sum a) where -- per aquells `a` que implementin Num (un anell algebraic)
mempty = Sum 0 -- element neutre del Monoide
Sum x `mappend` Sum y = Sum (x + y) -- operació associativa
Les col·leccions heterogènies d'altres llenguatges, aquí es tipifiquen per les operacions requerides, mitjançant tipus existencials (aquells tipus quins components implementin les classes de tipus requerides per al seu tractament).
{-# LANGUAGE ExistentialQuantification #-}
llistaHomo :: [Int]
llistaHomo = [1, 3, 4] -- llista homogènia
-- tipus "existencial": amb un component de tipus variable que compleixi una restricció
data ObjPresentable = forall a. (Show a) => Obj a -- per aquells tipus 'a' tals que "(Show a)"
presentaObj :: ObjPresentable -> String
presentaObj (Obj x) = "Obj " ++ show x
-- llista d'elements amb components heterogenis,
-- als quals podrem aplicar les operacions de les classes especificades a la restricció
llistaHetero :: [ObjPresentable]
llistaHetero = [Obj 1, Obj "abc"]
El control de les operacions sobre els elements dels contenidors es pot fer de diverses maneres:
A* -> A
, a la classe Data.Foldable.[25]A -> A*
. No tenen una classe específica. Es descriuen als mòduls de cada col·lecció.fmap
` a la primera acció, o també, elevant el combinador com a valor amb `pure
` al tipus de l'efecte.La programació genèrica, parametritzada per tipus, es concreta o bé
Haskell destaca en la facilitat per al paral·lelisme, per aprofitar la potència dels processadors multicor.[32][33]
A finals dels anys 1980 es va constituir un comitè amb l'objectiu de recollir en un llenguatge les característiques dels múltiples llenguatges funcionals de l'època, Miranda, ML i altres.
La primera versió va sortir el 1990. La versió més estesa actualment és la que correspon a l'informe Haskell 98.[34] Tanmateix el compilador GHC incorpora l'estàndard Haskell2010 per defecte a partir de la versió 7.0[35]
A principi de 2006 va començar el procés per definir-ne una nova revisió amb el nom de Haskell' ("Haskell prima", ang:"Haskell prime").[36] Diversos compiladors incorporen extensions del llenguatge que per a fer-les servir caldrà precedir el codi font amb la pragma {-# LANGUAGE extensió1, extensió2, ... #-}
o bé el senyal corresp. de compilació (-Xextensió
per al GHC). El wiki de "Haskell Prima" esmenta per cada extensió els compiladors que la implementen.[37]
Haskell 2010:[38] Actualment ha finalitzat el procés de discussió de les incorporacions a la nova versió de l'estàndard,[39][40] així com un nou procés d'evolució amb revisions (bi-)anuals del llenguatge.[41]
Propostes d'evolució del llenguatge aquí.[42]
Crítiques:
head []
) donen, en compilació de producció, informació molt escassa: ni situació de l'error (a partir de GHC 8.0 error ja dona la posició, excepte al paquet base on s'ha mantingut la versió antiga, reanomenada errorWithoutStackTrace), ni la de la crida que incompleix la precondició. Per això es recomana no utilitzar-les en funcions parcials i convertir-les en totals amb resultat opcional caçant el cas no previst en l'anàlisi del retorn. Recentment des de GHC 7.10.2 hi ha la possibilitat d'obtenir el punt de crida d'origen mitjançant paràmetres implícits especials (Vegeu exemple). Alternativa més segura: evitar les funcions parcials (La biblioteca Safe ofereix alternatives a les funcions parcials predefinides del Prelude; el mòdul Data.List.NonEmpty ofereix llistes no buides com a tipus NonEmpty per aplicar de manera segura les funcions que petarien sobre llistes buides, per ex.: head).[46]Problemàtiques:
El GHC (Compilador Haskell de Glasgow) és el compilador / intèrpret amb més possibilitats. Instruccions per obtenir-lo, les trobareu aquí.[52] o potser millor descarregant la Plataforma Haskell (La versió que incorpora la e./s. de caràcters no anglosaxons ja està disponible).
Per a Windows i altres podem descarregar-lo d'aquí.[52]
{-| fitxer hola-mon.hs
* si no hi ha la clàusula ''module'', es pressuposa "module Main where"
-}
import Data.Function ((&)) -- importa l'operador (&): aplicació cap enrere
import Control.Category ((>>>)) -- importa l'op. composició d'esquerre a dreta
-- l'execució comença sempre per la funció inicial ''main'' del mòdul principal
-- alternatives:
main = putStrLn "Hola Món"
main = putStrLn $ "Hola" ++ " Món" -- ($) aplicació endavant, estalvia parèntesis
main = "Hola Món" & putStrLn -- (&) aplicació cap enrere
main = ["Hola", "Món"] -- les claus designen una llista
& unwords -- words separa paraules retornant-ne la llista, unwords és la inversa
& putStrLn
main = print 5
main = putStrLn . show $ 5 -- equivalent
main = show >>> putStrLn $ 5 -- equivalent
main = 5 & (show >>> putStrLn) -- equivalent
-- nota: 'print' converteix a String i imprimeix, equival a (putStrLn. show)
-- show a una String li afegeix l'entrecomillat, amb 'print' una String acaba impresa amb duplicació de cometes.
En un sistema Linux podem tenir GHC i altres compiladors de Haskell i runhaskell es pot configurar per executar una alternativa o altra amb un selector d'alternatives de sistema[54] o bé amb la interfície gràfica galternatives[55] (normalment apunta a runghc). A MSWindows podem fer servir indistintament runhaskell o runghc.
Execució directa, sense generació d'executable, amb runhaskell[56]
$ runhaskell hola-mon # cerca el nom de fitxer amb l'extensió .hs (haskell source) o bé .lhs (literary haskell source)
Hola Món
Amb el meta-gestor stack:
# stack templates -- llista plantilles
# stack new projecte nom-de-plantilla
stack new projecte simple
cd projecte
# caldrà actualitzar el fitxer Main.hs al programa Hola-món
stack setup # configura, i descarrega el compilador
stack build # compila i relliga l'executable
stack exec projecte[.exe]
Amb l'intèrpret GHCi del mateix paquet, comanda ghci. Amb el meta-gestor escriuríem stack ghci.
A l'intèrpret no serveix la mateixa sintaxi del nivell declaratiu dels programes, sinó només la dels blocs "do" que permet especificar seqüencialment l'encadenament de resultats d'efectes mònadics; a banda de les comandes específiques[57] que comencen pel caràcter dos-punts i les expressions a avaluar.
Això canvia a partir de GHC 7.4.1 que admet tota mena de declaracions a l'intèrpret GHCi.[58]
$ ghci
GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> putStrLn "Hola Mon"
Hola Mon
Prelude> let str = "abc"
Prelude> :type str
str :: [Char]
Prelude> str
"abc"
Prelude> let f x y = x + y
Prelude> :type f
f :: Num a => a -> a -> a
Prelude>
Avaluador en-línia, a l'intèrpret d'expressions funcionals tryhaskell.org.
reverse "Hola"
> "aloH"
:: [Char]
map (a -> b) -> [a] -> [b] Ord a => [a] -> [a] Data.Map.insert
En Forma Normal els valors estan avaluats completament.
Els termes Head Normal Form i Weak Head Normal Form són normalitzacions d'expressions del càlcul lambda.
En català està explicat al llibre pdf "Introducció a la programació funcional" de la Universitat de Girona.[61]
La definició del càlcul lambda segueix el document original d'Alonzo Church.
Terminologia del càlcul lambda (en notació Haskell):
(E1 E2 E3) ≡ (E1 E2) E3
(\ x -> \ y -> E) ≡ (\ x -> (\ y -> E))
Cal tenir en compte que
(\ x -> M N) ≡ (\ x -> (M N)) -- el cos de l'abstracció abasta el màxim cap a la dreta
(\ x y -> E) ≡ (\ x -> \ y -> E) -- abreviació d'abstraccions
Una expressió és beta-reduïble (beta-redex) si consisteix en l'aplicació d'una abstracció lambda (funció anònima d'una variable com a mínim) a un paràmetre. És de la forma
(\ x -> E) P -- expressió "beta-reduïble"
-- la reducció 'beta' és la substitució de la variable 'x' pel paràmetre P en l'expressió E
-- el resultat ('reducte') en notació del càlcul lambda: E[x := P]
\ x -> \ y -> \ z -> x y z
-- amb notació de l'índex de De Bruijn seria
\ -> \ -> \ -> v3 v2 v1 -- la variable es refereix a l'argument per l'índex d'aniuament d'abstraccions
Una expressió beta reduïble està en posició de capçalera si és de la forma (en notació Haskell)
\ x1 x2 ... xi -> (\ y -> E) P1 P2 ... Pj -- l'expressió en capçalera és: (\ y -> E) P1 ... Pj
La seva reducció seria
\ x1 x2 ... xi -> E[y := P1] P2 ... Pj
Amb notació Haskell, de la definició del càlcul lambda.
Una abstracció lambda de la forma (\ x -> E x)
és eta-reduïble (substituïble per E) si
-- reducció 'eta': l'expressió "eta-reduïble"
(\ x -> E x)
-- és substituïble per
E
-- sempre que 'x' no sigui una variable lliure de E
La programació tàcita o point-free (sense arguments) estalvia arguments en les definicions aplicant la reducció eta del càlcul lambda.
-- definició
arrelQuad x = sqrt x
-- en forma de lambda
arrelQuad = \x -> sqrt x
-- per la reducció eta, atès que x no és una variable lliure de l'expressió (sqrt)
arrelQuad = sqrt -- versió tàcita o 'point-free' (estalviant arguments)
Una expressió lambda està en "forma normal en capçalera" (HNF) si
Una expressió lambda amb el cos reduïble està en WHNF però no en HNF. El terme va ser encunyat per Simon Peyton Jones per explicitar la diferència entre HNF i el que la reducció de grafs fa a la pràctica: posposar la reducció del cos.[63]
Una expressió està en WHNF si està en HNF o bé és una expressió lambda amb el cos reduïble.[63]
Una expressió en WHNF està en una de les formes següents:[64]
Exemples de situacions d'avaluació a WHNF
Una expressió està en forma normal si no s'hi poden aplicar més reduccions.[61] Està completament avaluada (en profunditat).
Una expressió que comença per un constructor està en HNF, i això impedeix l'avaluació de les expressions dels components (avaluant amb seq). Perquè quedin avaluades, cal, o bé definir-les estrictes als tipus, o bé avaluar amb deepseq o l'equivalent $!! a "forma normal".
La classe NFData (contracció de NormalFormData) del paquet deepseq[65] (a partir de GHC 7.4.1 forma part del conjunt bàsic de biblioteques de GHC) defineix la signatura d'una funció rnf (inicials de Reduce to Normal Form) per l'avaluació en profunditat. Per crear una instància de NFData per a un tipus algebraic, caldrà una implementació de rnf que apliqui recursivament rnf a les variables d'encaix corresponents als components dels constructors.
-- la funció ''rnf'' inicials de la traducció anglesa de "Reducció a Forma Normal"
class NFData a where
rnf :: a -> () -- rnf ha d'avaluar el paràmetre en profunditat a Forma Normal
import Control.DeepSeq
data NomDelTipus = Cons1 T1 T2 | Cons2 T3
instance NFData NomDelTipus where
rnf (Cons1 t1 t2) = (rnf t1) `seq` (rnf t2) `seq` ()
rnf (Cons2 t3) = (rnf t3) `seq` ()
Automatitzable amb el travessament genèric de les estructures algebraiques.[66]
{-# LANGUAGE DeriveGeneric #-}
import Control.DeepSeq
import Control.DeepSeq.Generics (genericRnf)
import GHC.Generics
data NomDelTipus = Cons1 T1 T2 | Cons2 T3 deriving Generic
instance NFData NomDelTipus where rnf = genericRnf
A part de l'optimització en estrictesa dels compiladors (vegeu article "Haskell és un llenguatge estricte")[67] es pot forçar l'avaluació estricta de les següents maneres:
Avaluació del paràmetre abans de la substitució (càlcul lambda). Amb l'operador d'aplicació estricta ($!) com a alternativa de ($) d'avaluació tardana. (seq) avalua el primer operand i retorna el segon. Al Haskell98 (seq) avaluava a HNF[68] però al Haskell2010 avalua a WHNF (posposa la reducció de les lambdes i de les expressions dels constructors).[69]
f $! x = x `seq` (f x)
($!!) amb doble signe d'exclamació, avalua l'operand en profunditat (amb deepseq) a Forma Normal, per als casos on l'operand és un tipus que implementa la classe NFData. La generació d'instàncies de NFData per a tipus algebraics es pot fer amb mecanismes de derivació genèrica (amb recorregut genèric dels tipus algebraics).
import Control.DeepSeq
-- ($!!) :: NFData a => (a -> b) -> a -> b -- infixr 0 $!!
f $!! x = x `deepseq` (f x)
data T = C T1 !T2 ... -- sense prefix al component => aval. tardana, amb (!) avaluació estricta
Quan en una definició de tipus de dades es prefixa el tipus del component amb '!' l'avaluació de l'aplicació del constructor a una expressió corresponent al component es fa amb ($!) i si no hi ha el prefix '!' es fa amb ($).[70]
Vegeu Eficiència i rendiment en tipus de dades[71]
{-# LANGUAGE StrictData #-} -- des de GHC 8.0
data T = C ~T1 T2 ... -- sense prefix al comp. => aval. estricta, amb (~) avaluació tardana
Avaluació estricta als paràmetres formals amb l'extensió {-# LANGUAGE BangPatterns #-}
[72]
{-# LANGUAGE BangPatterns #-}
f !x !y = 2 * x * y -- avalua estrictament (!) els paràmetres actuals (a WHNF)
{-# LANGUAGE Strict #-} -- des de GHC 8.0
f x y = 2 * x * y -- amb Strict avaluació estricta no-irrefutable per defecte, prefix (~) per l'avaluació com abans
Lligams amb avaluació estricta a les variables dels patrons
let !v = expr ... -- avalua estrictament l'expressió (amb (seq)) let (!x, !y) = (exprX, exprY) -- avalua estrict. les expr. de les variables del patró
l'operació foldl (foldLeft) permet fer "transformacions reiterades sobre una estructura de tipus a amb els valors d'una seqüència [b] ((a -> b -> a)), de manera equivalent als bucles for|forEach de la prog. imperativa tradicional.
foldl :: (a -> b -> a) -> a -> [b] -> a -- essent ''a'' l'estructura i ''[b]'' la llista de valors a iterar
-- versió tardana, cost O(n) en allotjament d'operacions pendents d'avaluar
foldl op estructInicial (x:xs) = foldl op (op estructInicial x) xs
-- versió estricta, avaluació primerenca
foldl' op estructInicial (x:xs) = let aplega_x = op estructInicial x
in aplega_x `seq` foldl' op aplega_x xs
-- (seq) avalúa el primer operand i retorna el segon
-- si el tipus de ''aplega_x'' és algebraic (conté constructors (aturen l'avaluació)))
L'ús de tipus algebraics en l'operació, aturant l'avaluació en els constructors, implica la generació d'una pila de thunks (expressions pendents d'avaluar) que poden comportar el sobreiximent de la pila.
El llibre RealWorldHaskell ofereix un exemple on l'operació nucli del plegat estricte foldl' plega sobre un parell, és a dir un tipus algebraic com a acumulador (constructor del parell amb les seves expressions).[74] Els constructors en posició de capçalera (vegeu #Head Normal Form) estan en HNF, per tant les expressions dels components no són avaluades. Això fa que s'amuntegui la seva avaluació pendent a la pila.
import Control.DeepSeq (NFData, ($!!))
import Data.Foldable
-- de la definició de foldl' i foldr', substituint ($!) per ($!!) de DeepSeq per avaluar a Forma Normal
foldl'' :: (Foldable t, NFData b) => (b -> a -> b) -> b -> t a -> b
foldl'' f z0 xs = foldr f' id xs z0
where f' x k z = k $!! f z x -- ($!) substituït per ($!!)
foldr'' :: (Foldable t, NFData b) => (a -> b -> b) -> b -> t a -> b
foldr'' f z0 xs = foldl f' id xs z0
where f' k x z = k $!! f x z -- ($!) substituït per ($!!)
Vegeu "Recursivitat final al Haskell".[75]
duplicaLlista :: [a] -> [a]
duplicaLlista [] = []
duplicaLlista (x : xs) = x : duplicaLlista xs -- crida recursiva "mòdulo cons"
Els encaixos de patrons s'avaluen normalment de manera estricta (primerenca).[78]
Per avaluar un encaix de manera tardana (lazy) cal prefixar-lo amb el caràcter titlla '~'.[79]
L'avaluació tardana de l'encaix (vegeu exemple tot seguit) converteix l'encaix en irrefutable (no rebutjable, no parcial) evitant l'avaluació d'altres opcions.
L'ús de l'avaluació tardana explícita amb tipus amb més d'un constructor és perillós:[80]
f :: [a] -> [a]
f ~(x:xs) = x:xs
-- és traduït internament a
f ys = head ys : tail ys -- compte! funcions parcials!!
-- l'encaix (f ys), encaix amb variable, sempre té èxit.
-- si, per filtrar el cas [] no definit anteriorment, l'avaluem primer ...
f [] = []
f ~(x:xs) = x:xs -- l'avaluació tardana aquí és estúpida perquè l'encaix
-- ja ha estat avaluat estrictament pel cas precedent []
-- si haguéssim posat el cas [] després:
f ~(x:xs) = x:xs
f [] = []
-- el segon encaix (f []) no s'avalua mai, perquè l'anterior és irrefutable!
-- L'ordre és rellevant!!! El compilador ens avisarà del codi inabastable.
Vegeu-ho a GHC
Vegeu-ho a GHC
Vegeu-ho a GHC
A GHC.
Execució de Haskell com a Script a Unix (mitjançant una directiva Shebang).,[81] mitjançant l'eina stack:[82]
#!/usr/bin/env stack # directiva "Shebang" amb l'intèrpret seguit de paràmetres per a `stack script`
{- stack script
--resolver lts-11.22
--package shelly -}
{-# LANGUAGE OverloadedStrings #-}
import Shelly (Sh, shelly, echo)
hola :: Sh ()
hola = echo "hola"
main :: IO ()
main = shelly $ hola
També admet mòduls externs posicionats segons la nomenclatura jeràrquica NomCarpeta.NomDeMòdul respecte del programa script.
L'ús simultani de diferents biblioteques que utilitzin diferents versions d'una mateixa tercera biblioteca[88] comporta:
Un cop has generat l'executable, el següent maldecap són les petades de les funcions parcials, que criden a error per als valors per als quals no estan definides, (head []
), sense cap pista del culpable:
Les crides recursives i els plegats poden generar piles d'expressions pendents d'avaluar, fent petar la pila, si no s'explicita correctament l'avaluació estricta dels acumuladors i dels components de les estructures intermèdies:
La modificació de l'estat global (preferències) en temps d'execució dona lloc al:
Perquè la imatge en memòria del programa no creixi més del compte cal tenir en compte:
El Haskell utilitza un gestor de projectes per resoldre les dependències d'altres biblioteques i facilitar-ne la compilació on s'instal·len, amb independència del compilador emprat.
Aquest gestor es diu Cabal[89][90] (Common Architecture for Building Applications and Libraries)[91] i utilitza arxius de descripció de projecte amb l'extensió ".cabal". Vegeu "Com crear un paquet executable o bé biblioteca"[92][93]
El nom del paquet consta de nom-versió, per ex. shakespeare-css-1.0.1.2, on la versió major són els dos primers components numèrics (un canvi en la versió major indica trencament de compatibilitat amb l'API anterior), la versió menor és el tercer (identifica la API, un canvi significa un augment de l'API sense trencament)), i el quart indica modificacions sense variar l'API.[94]
Els paquets es descarreguen en directoris dins del $HOME/.cabal, els executables de les aplicacions quedaran a $HOME/.cabal/bin que caldrà afegir a la variable d'entorn PATH.
Els paquets ja instal·lats per GHC es gestionen amb el programa ghc-pkg.
Al Linux podem començar treballant amb les biblioteques precompilades en paquets de la distribució de Linux. Si us cal una versió més recent del compilador, caldrà instal·lar-lo prèviament i, en acabat, instal·lar #The Haskell Platform, contràriament al Windows on el paquet #The Haskell Platform l'incorpora.
El rebost oficial de biblioteques i aplicacions és el Hackage[95]
runhaskell Setup clean # neteja arxius producte de compilacions anteriors
runhaskell Setup configure —help # mostra opcions generals i específiques del paquet
runhaskell Setup configure —user —altres-opcions
runhaskell Setup build
[sudo] runhaskell Setup install # ''sudo'' (privilegi d'admin. del sistema) si configures amb —global en comptes de —user
#, per actualitzar el rebost del sistema
Amb "cabal" descarrega, configura, compila i instal·la d'una sola tacada, després d'instal·lar automàticament els paquets dels quals depèn. Preguntes freqüents aquí.[96]
# l'ajuda és per comanda
cabal help install
# --with-compiler=... permet seleccionar el compilador
# <versió> = <digit> {'.' <digit>} ['.' '*']
# <paquet_o_dependència> :== nom | nom-versió | "nom < versió" | "nom == versió"
cabal install paquet1 paquet2 --opcions
# restricció de dependències en compilar
--constraint="nom_biblio < versió"
# o bé
--constraint=nom_biblio==versió # (Els 3 primers components de la versió identifiquen l'API)
# exemples (les cometes només fan falta si hi ha espais o bé
# si l'operador de comparació conté símbols '<' '>' de l'intèrpret de comandes per a la redirecció):
cabal install QuickCheck --constraint=BiblioDeLaQualDepen==2.3.4.*
cabal install -j "wx == 0.13.*"
Tanmateix cal tenir en compte que l'ús simultani de biblioteques que facin servir diferents versions d'un mateix paquet és molt problemàtic.[98] Cal evitar absolutament la comanda cabal upgrade que ens abocaria al problema esmentat.[98]
cabal amb el verb sandbox[99] crea un dipòsit de biblioteques específic del projecte en el subdirectori amagat ".cabal-sandbox". Una sandbox (capsa de sorra literalment) és un entorn tancat per a proves per evitar que els errors que s'hi produeixin repercuteixin a l'exterior. Aquesta funcionalitat es va desenvolupar al programa cabal-dev[100] que ara ha quedat obsolet.
./.cabal-sandbox/bin
que caldrà afegir a la var. d'entorn d'executables PATH.cabal sandbox add-sources camí/al/subprojecte
Per a més info. consulteu cabal sandbox --help
.
Stackage[101] proporciona imatges de conjunts de biblioteques amb compatibilitat verificada de versions, entre elles i una versió específica de GHC, amb noves imatges dels rebostos a mesura que sorgeixen noves versions de dependències o del compilador.
Vegeu Compilador Haskell de Glasgow#Stackage - rebost alternatiu amb dependències verificades
stack new projecte plantilla
Un cop creat, la comanda cabal sense nom de paquet treballa sobre el fitxer de projecte del directori de treball:
cabal sandbox init # genera un rebost de biblioteques específic del projecte
# els executables quedaran a la carpeta ".cabal-sandbox/bin"
cabal sandbox add-sources camí/al/subprojecte # afegir subprojecte
cabal sandbox delete # elimina el rebost específic
cabal clean # neteja tots els fitxers generats per la compilació
cabal configure # estableix opcions i comprova compatibilitat de les dependències
cabal build # compila
cabal install # configura, compila, relliga i instal·la
cabal freeze # fixa les versions de les dependències com a restricció
# al fitxer "cabal.config" de restriccions a la carpeta del projecte
cabal repl # engega l'intèrpret 'ghci' havent carregat mòduls i opcions esmentades al fitxer de projecte
Vegeu també "Com escriure (i empaquetar) un programa en Haskell".[102] També "Actualitzant un paquet a noves versions de GHC i biblioteques".[103]
Amb ghc-pkg comprova paquets instal·lats a les BDD de paquets de l'usuari i del sistema.[104]
$ ghc-pkg list
$ ghc-pkg check # comprova dependències trencades per la reinstal·lació d'aquestes
En un entorn de desenvolupament aïllat en capsa-de-proves (sandbox):
cabal sandbox hc-pkg ...
L'aplicació cabalg descarrega (amb git-clone a subcarpeta) i instal·la (amb cabal).[105]
cabalg https://github.com/usuari/projecte-github # descarrega a subcarpeta de nom "projecte-github"
L'aplicació Stack[106][107][108][109] sobreposa la seva operativa a la del gestor cabal millorant-la en diversos aspectes:
stack templates # llista les plantilles de fonts per a nous projectes
stack new projecte <template> # genera un projecte nou (amb fonts i projecte.cabal) partint d'una plantilla
#
cd projecte
stack setup # cerca la versió de Stackage que contingui les dependències del projecte, i descarrega el compilador GHC corresponent.
stack build # compila i instal·la -- les dependències a (~/.stack), i els paquets locals del projecte a (./.stack-work)
# l'executable queda a .stack-work/install/<arquitectura>-<sist_op>/<versió_de_stackage>/<versió_de_ghc>/bin
stack exec projecte # nom de l'executable al fitxer de projecte .cabal (pot variar amb sufixos o extensions)
stack install local-pkgs/el-meu-paquet-personalitzat/
Vegeu ref.[111]
Els paquets s'identifiquen amb un número de versió amb dígits separats per un punt, amb un mínim de tres components A.B.C on A.B s'anomena versió major i C la versió menor.
The Haskell Platform vol ser un instal·lador empaquetant el compilador GHC (només a Windows, a Unix i derivats cal descarregar-lo prèviament), una biblioteca i un paquet d'eines incorporant paquets de rutines[114] del rebost Hackage.[95] amb encaix verificat respecte a la versió de la biblioteca Base[115] inclosa. (Preguntes freqüents)[116]
Backpack[117][118][119] (cat: Motxilla) permet substituir una dependència directa d'un paquet en una funció, tipus o paquet per una o més signatures. El desenvolupador adjunta un mòdul d'interfície abstracta de requeriments (clàusula Signature) i l'usuari decideix el paquet d'implementació que hi relligarà.
Backpack evita haver de mantenir diverses versions d'una mateixa biblioteca que facin servir tipus definits externament lleugerament diferents però amb la mateixa interfície (per exemple diferents tipus de cadenes: Data.ByteString.Lazy i Data.ByteString.Strict o també diferents implementacions d'un servei) i haver de relligar-les totes si se'n vol disposar.[120]
Per evitar les múltiples versions del codi, Backpack emula els mòduls paramètrics functors de ML Estàndard, però amb l'estil de mix-in imitant el sistema de mòduls de MixML.[121][122]
Caldrà que el codi client faci referència al tipus definit abstracte en un mòdul d'interfície (nova clàusula Signature) que descriurà la funcionalitat requerida, i que empaquetarem en un paquet functor.
Després, podrem fer-ne ús directament o també, generar paquets instàncies del functor, i donar nom específic als aparellaments de mòdul client de la signatura amb el mòdul que la implementa (clàusula "mixins" de Cabal, o bé clàusula "dependency" en el nou format .bkp).[120][123]
Backpack requereix GHC >=8.2 i Cabal >=2.0.[123]
Per a l'ús de Backpack amb el format tradicional de paquets i en paquets multi-biblioteca vegeu la ref.[124] L'extensió dels fitxers de Signatura haurà de ser ".hsig"
Mentre Stack no suporti Backpack, es pot fer servir de moment mitjançant les "capses de prova Sandbox" cabal sandbox
, fins i tot amb rebostos Stackage especificant el rebost desitjat ("remote-repo") en un fitxer de configuració local "cabal.config"
La versió de GHC per a la Màquina virtual Java anomenada "Eta" també suporta Backpack.[125]
GHC suporta un format nou amb extensió (.bkp) de codi font amb múltiples clàusules unit com a unitat de compilació equivalent a biblioteques, que engloba diversos mòduls habituals d'implementació i mòduls d'interfície per la clàusula signature.[123] Vegeu exemple.
{-| fitxer hello.bkp -}
unit main where -- unitat de compilació
module Main where
main = putStrLn "Hello world!"
# per compilar (GHC >= 8.1.20161007)
ghc --backpack hello.bkp
# genera subcarpeta de compilats amb el nom de la unitat de compilació
# executable a ./<unit>/Main
-- paquet functor
unit regex-indef where -- unitat de compilació
signature Str where -- interfície "motxilla" abstracta de requeriments
-- que haurà d'implementar un mòdul d'un paquet extern,
data Str
instance Eq Str
null :: Str -> Bool
singleton :: Char -> Str
splits :: Str -> [(Str, Str)]
parts :: Str -> [[Str]]
module Regex where -- mòdul client
import Str -- importa l'espai de noms de la interfície "motxilla"
...
Vegeu[132]
Incorpora "concurrència","[135]paral·lelisme","[136]Memoria Transaccional per Software"[137][138] i opcions d'optimització. Pot generar codi nadiu, codi C, o codi optimitzat mitjançant LLVM.[139]
A partir de la versió 6.10 incorpora "Data Parallel Haskell"[140][141] per al tractament paral·lel de vectors.
L'intèrpret ghci genera codi intermedi i admet l'execució mixta de codi intermedi i codi nadiu.
Vegeu enllaç[153]
- o bé dins un doc. de text, prefixant les línies de codi amb '>' (anomenat estil rastre d'ocell assimilant la forma '>' a les petjades d'un ocell)
- o bé dins un doc. LaTeX situant el codi entre \begin{code} i \end{code} Vegeu http://people.cs.uu.nl/andres/lhs2tex/ Arxivat 2010-03-15 a Wayback Machine.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.