Haskell

De Viquipèdia
Dreceres ràpides: navegació, cerca
Per a altres significats vegeu «Haskell (desambiguació)».

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 el canvis, o circumscriure'n els efectes col·laterals al nivell superficial.

Haskell destaca en la facilitat per al paral·lelisme, per aprofitar la potència dels processadors multicor.[1][2]

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.[3] Tanmateix el compilador GHC incorpora l'estàndard Haskell2010 per defecte a partir de la versió 7.0[4]

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").[5] Diversos compiladors incorporen extensions del llenguatge que per a fer-les servir caldrà acompanyar de la pragma {-# LANGUAGE extensió #-} o l'opció corresp. del compilador ( -Xextensió per al GHC) El wiki de "Haskell Prima" esmenta per cada extensió els compiladors que la implementen.[6]

Haskell 2010:[7] Actualment ha finalitzat el procés de discussió de les incorporacions a la nova versió de l'estàndard,[8][9] així com un nou procés d'evolució amb revisions (bi-)anuals del llenguatge.[10]

Haskell 2014 en preparació.[11]

Crítiques:

  • S'ha criticat que no hi hagi un sistema d'extensió de registres[12] i/o especialització. A l'intèrpret Hugs se'n va desenvolupar un anomenat TRex[13] (exemple) que GHC no ha incorporat.
En realitat es pot aconseguir facilitat en l'especialització de comportament, desacoblant la funcionalitat de l'estructura amb propietats (getters/setters) com es descriu a l'Encapsulament estil O.O..

Problemàtiques:

Vegeu secció #Problemàtiques.

Taula de continguts

Programa Hola Món[modifica | modifica el codi]

El GHC (Compilador Haskell de Glasgow) és el compilador / intèrpret amb més possibilitats. Instruccions per obtenir-lo, les trobareu aquí.[14] 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í.[14]

-- fitxer hola-mon.hs
main = putStrLn "Hola Món"
-- fi de fitxer

En un sistema Linux podem tenir GHC i altres compiladors de Haskell i runhaskell es pot configurar per executar una o altra alternativa amb un selector d'alternatives de sistema[15] (normalment apunta a runghc o runghc6). A MSWindows podem usar indistintament runhaskell o runghc.

Execució per GHC sense compilació prèvia[16]

 runhaskell hola-mon.hs
 
 Hola Món

Amb l'intèrpret GHCi del mateix paquet. Denominació ghci o ghci6 segons instal·lació.

A l'intèrpret no serveix la mateixa sintaxi del nivell declaratiu dels programes, sinó només la dels blocs d'operacions d'entrada / sortida, que corresponen al tipus Mònada, a banda de les comandes específiques[17] que comencen pel caràcter dos-punts i les expressions a avaluar.

Això canvia a partir de GHC 7.4.1 que admet tot tipus de declaracions a l'intèrpret GHCi.[18]

>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, eviteu-hi operacions de sortida (print, putStr, etc ..) i de tipus mònada.

 reverse "Hola"
 > "aloH"
  :: [Char]

Cercadors[modifica | modifica el codi]

  • Hayoo: on es pot consultar quines biblioteques exporten un identificador, a les biblioteques del rebost oficial (Hackage).
Permet cercar operadors no alfanumèrics com ara (>>=)
  • Hoogle: permet, a més a més, cerca de funcions per signatura. Exemples de cerca:
map
(a -> b) -> [a] -> [b]
Ord a => [a] -> [a]
Data.Map.insert

Característiques[modifica | modifica el codi]

separació de codi funcional pur i codi amb efectes col·laterals[modifica | modifica el codi]

L'assignació de tipus específics al codi impur (amb efectes sobre l'entorn: ent./sort., canvis d'estat, ...) facilita la distinció del codi pur (el que només depèn dels paràmetres) per a millores de rendiment mitjançant el paral·lelisme (procés en paraŀlel).

-- codi pur.  L'ordre de les operacions és irrellevant i per tant es podrien paraŀlelitzar
f x y = (x * 2) + (y * 4)
 
-- codi impur (modif. de l'entorn o de l'estat). L'ordre és rellevant
acció :: IO Int
acció = do
   putStrLn "entreu nombre enter"
   hFlush stdout
   str <- getLine
   let x = read str
       y = x * 2
   return y

separació d'especificació i execució[modifica | modifica el codi]

El codi d'un mòdul no s'executa en l'ordre d'especificació, sinó mitjançant l'avaluació no-estricta, partint de l'expressió arrel main del mòdul Main que processa els arguments de la crida des de la consola de comandes.

avaluació no-estricta, normalment tardana - Thunks[modifica | modifica el codi]

En avaluació tardana les expressions, definides en variables i paràmetres, no es calculen quan es formulen sinó en el moment que se'n demana el valor en una altra expressió. Correspon a l'expressió anglesa lazy evaluation.

Haskell és d'avaluació no-estricta, que vol dir que l'avaluació va de l'arrel a les branques en l'arbre d'operacions de l'expressió. L'avaluació comença per l'expressió definida a la clàusula main. En cas de (b * c + a) primer s'avalua el (+) i després el (*), al revés dels llenguatges estrictes.[19] Les altres definicions s'avaluen sota comanda (avaluació tardana). D'aquesta manera només s'avaluen les subexpressions que facin falta, estalviant càlculs.

A la pràctica Haskell no és un llenguatge purament tardà. L'encaix de patrons és, normalment, estricte —almenys s'avalua si tindrà èxit—, i un analitzador d' estrictesa[20] determina quan un terme serà sempre requerit en les expressions, i converteix la seva avaluació en primerenca (ang.: eager evaluation) .[19] El programador també pot forçar l'estrictesa com s'explica més avall.

Thunk és el nom de la càpsula de codi corresponent a cada expressió pendent d'avaluar.[21]

separació d'estructura i operacions (comparat amb la POO)[modifica | modifica el codi]

Haskell. a diferència dels lleng. amb orientació a objectes separa estructures de dades i comportament.

Els tipus de dades no impliquen operacions. Si són simples (ex.: Int, Double), defineixen el conjunt de valors, i si són compostos (tipus producte o tipus suma) defineixen l'estructura (clàusula data).

Les operacions sobre el tipus poden agrupar-se en "classes de tipus" que els tipus han d'implementar (amb la clàusula instance)

class CForma t where      -- una classe defineix la ''signatura'' (tipus de params i resultat)
                -- d'un grup d'operacions. Per a un tipus t ...
 
 perímetre :: t -> Double   -- les variables de tipus als mètodes coincidents a la declaració de classe
 àrea :: t -> Double          -- es refereixen a aquells tipus que implementin la classe.
 
type TCostat = Double       -- ''type'' dóna un àlies a una expressió de tipus
 
data TRectangle = Rectangle TCostat TCostat  -- tipus de dada amb ''constructor'' seguit de components
 
instance CForma TRectangle where  -- TRectangle implementa CForma definint-ne les operacions
 perímetre (Rectangle x y) = 2 * (x + y)
 àrea (Rectangle x y) = x * y
{-# LANGUAGE NamedFieldPuns #-} -- sintaxi d'encaix de registres simplificada
 
data TPersona = Persona {nom::String, edat::Int} deriving (Eq, Show)
 
data TLlenguatge = Lleng_FORTRAN | Lleng_JAVA | Lleng_HASKELL  deriving (Eq, Show, Ord, Enum)
 
data TProgramador = Programador { persona::TPersona, llenguatges::[TLlenguatge] } deriving (Eq, Show)
 
class CPersona t where   -- un tipus t que implementi CPersona haurà de proveir les funcions ..
  fer_anys :: t -> t
 
-- defineix la classe CProgramador per aquells tipus t
--    tals que (CPersona t) -- que implementin CPersona
 
class (CPersona t) => CProgramador t where
  aprendre_llenguatge :: TLlenguatge -> t -> t
 
-- fem que el tipus TPersona implementi la signatura de la classe de tipus CPersona
--   i n'esdevingui una instància
 
instance CPersona TPersona where
  fer_anys p @ (Persona {edat}) =    -- es llegeix: ''p com Persona quina edat és edat''
                       p {edat = edat +1} -- el primer és el nom del camp, el segon és l'argument.
 
instance CPersona TProgramador where
  fer_anys prog @ (Programador { persona}) =
                     prog { persona = fer_anys persona}
 
instance CProgramador TProgramador where
  aprendre_llenguatge nou_llenguatge prog @ (Programador {llenguatges}) =
                          if not (nou_llenguatge `elem` llenguatges)
                            then prog {llenguatges = nou_llenguatge : llenguatges}
                            else prog
 
-- valors
 
joan_persona = Persona { nom="Joan", edat=50}
 
joan_programador = Programador { persona = joan_persona
                               , llenguatges = [Lleng_FORTRAN, Lleng_JAVA]}
 
fer_més_savi = fer_anys . (aprendre_llenguatge Lleng_HASKELL)
 
joan_més_savi = fer_més_savi joan_programador
 
main = print joan_més_savi

Per ser un llenguatge d'avaluació no-estricta, l'execució comença per l'avaluació de la clàusula d'engegada main, calculant les expressions que calguin quan se'n demana el valor (avaluació tardana) i no quan es defineix l'expressió (optimitzacions a banda).

associació única de valors a identificadors[modifica | modifica el codi]

No es poden fer dues associacions al mateix identificador en el mateix àmbit (a nivell de mòdul, let o where)

Composició de tipus[modifica | modifica el codi]

Els tipus (conjunts de valors) es poden compondre, partint dels tipus primitius i enumeracions, en tipus producte (etiquetats: els registres; anònims: les tuples) i tipus suma (unió discriminada), distingint-los amb un constructor que ens permetrà diferenciar-los en l'encaix de patrons. Vegeu #tipus algebraics.

Les famílies de tipus permeten generalitzar els tipus basant-se en un o més índexs i concretar-ne instàncies de diferent estructura.

Combinació aplicativa i funcional[modifica | modifica el codi]

Aplicacions parcials com a paràmetres d'ordre superior.

Vegeu també #operadors de funcions.

Composició de comportament[modifica | modifica el codi]

Les classes designen la signatura (operacions) referides a variables de tipus i no a tipus concrets (com els interface del Java). Les classes poden requerir operacions d'altres classes esmentades com a context necessari (en el Java s'esmentarien a la clàusula extends) establint la jerarquia de subtipatge de comportament. Vegeu #Encapsulament estil O.O..

Els tipus poden aportar implementacions específiques de la signatura d'una classe o incorporar les implementacions per defecte definides a la classe.

No hi ha l'herència pare/fill com entre les classes de la O.O. estalviant el temps esmerçat en el despatx per taula de mètodes virtuals (vtable) i els conflictes de l'herència.

Les implementacions poden establir un context de requeriments per a les variables de tipus, addicionals als establerts a la signatura.

Vegeu exemple

Genèrics[modifica | modifica el codi]

Els mòduls genèrics, paramètrics en tipus, d'altres llenguatges, aquí prenen la forma de classes multiparàmetre. Vegeu #Estructures TAD genèriques.

Coŀleccions heterogènies[modifica | modifica el codi]

Les llistes són de tipus homogenis. Per encabir elements de tipus divers per un tractament comú (poder-hi aplicar una mateixa operació), cal caracteritzar-los amb un tipus existencial que admeti un component de tipus divers tal que implementi la classe corresp. a l'operació.

Variables[modifica | modifica el codi]

Haskell disposa de variables globals IORef's similars a les ref del ML Estàndard, i variables STRef's d'àmbit local per a actualitzacions destructives.

Per al cas de concurrència amb baldes (blocant), les variables MVar (amb monitor) que es buiden en consultar-les, ofereixen una triple funcionalitat de variables amb accés sincronitzat, bústia d'un sol element i semàfor binari. Per al cas de cues, Chan i BoundedChan són cues amb blocatge.

Per a la concurrència amb memòria transaccional per programari (STM), hi ha les variables TVar, TMVar i cues TChan.

Efectes coŀlaterals[modifica | modifica el codi]

La seqüenciació (precedència temporal) de computacions amb efectes col·laterals es pot obtenir amb tres abstraccions diferents.

  • El TAD Mònada, on (>>=) lliga el resultat d'una acció amb una funció (amb possibles efectes col·laterals) sobre el resultat precedent, mentre que (>>) encadena sense lligar resultats. Els blocs do constitueixen una sintaxi específica per als encadenaments de mònades.
  • El TAD Applicative, permet combinar resultats individuals d'una cadena d'accions deslligades.
import Control.Applicative ((<*>))
import System.IO (hFlush, stdout)
 
data ElMeuT = ElMeuC Int String Double deriving (Eq, Show)
 
-- encadenat Applicative (<*>)
-- fmap combina els resultats amb una funció (aquí el constructor ElMeuC)
--   quina aritat ha de coincidir amb el nombre d'accions encadenades.
llegeixElMeu :: () -> IO ElMeuT
llegeixElMeu () = ElMeuC `fmap` llegeixNum () <*> llegeixStr () <*> llegeixNum ()
 
-- encadenats Mònada (>>) a acció, i (>>=) a funció del resultat precedent
 
llegeixStr () = getLine
llegeixNum () = getLine >>= (return . read)  -- el tipus s'ajusta en aplicar la funció
 
monàdic () = putStrLn "Entreu valors:" >> hFlush stdout >> llegeixElMeu () >>= print
 
main = monàdic ()

En un encadenament (>>=) de Mònada, la funció de la dreta proporciona accés al resultat precedent, com a paràmetre formal, a totes les accions posteriors de la cadena (bloc do).

En un encadenament de filtres Fletxa, els resultats precedents que es vulguin disponibles posteriorment, s'han d'agrupar amb el resultat de l'acció actual en tupla2 a la sortida del filtre, i fer que el filtre subsegüent tracti l'entrada com a parell.

Composició d'efectes[modifica | modifica el codi]

Per a les mònades vegeu Transformadors de mònades.

L'equivalent per a les fletxes són els Arrow Functors. Vegeu doc. "Generalizing Monads to Arrows".[22]

Lèxic[modifica | modifica el codi]

Haskell98 admet caràcters no anglosaxons als identificadors segons la codificació Unicode.[23] S'admeten caràcters accentuats, la ç, l'apòstrof, el caràcter ŀ (ela geminada), si el podeu compondre (AltGr+L al Linux), però no el punt volat (ang:mid dot) (·).

L'apòstrof també pot formar part del nom, per a un ús amb estil matemàtic

v, v', v''

, però també com a part interna d'un identificador.

A l'intèrpret GHCi sobre Linux/Ubuntu amb LANG=ca_ES.UTF-8:

 ghci     # a l'intèrpret les assignacions van precedides de ''let''
       # igual que al codi dins els blocs ''do''
       # distingint-les de les expressions a avaluar
       # Si la versió < 7.4, no admet declaracions de tipus (cal carregar-les d'un fitxer).
       # Ajuda teclejant '':help''

 Prelude> let alçada = 1.5
 Prelude> print alçada
 1.5
 Prelude> let opinió = 2
 Prelude> let l'internauta=123
 Prelude> let coŀlegi = "abc"   -- amb ela geminada Unicode (AltGr+l al Linux)

La pragma {-# LANGUAGE UnicodeSyntax #-} permet la substitució de determinades seqüències per caràcters Unicode equivalents.[24]

ent/sort. amb caràcters no anglosaxons
A partir de la versió 6.12.1, el compilador GHC incorporarà la codificació de caràcters del sistema subjacent per al tractament d'ent./sort. de les tires de caràcters.
Si no disposem d'aquesta versió podem tractar fitxers UTF-8 com a l'exemple més avall amb el paquet utf8-string.

Grafia[modifica | modifica el codi]

començant per lletra[modifica | modifica el codi]
  • cas de primera lletra minúscula, indica variable (de valor o de tipus segons el context)
començant per símbol[modifica | modifica el codi]
cas de guió baix
el comodí '_', i també, precedint un identificador (_abc123) : variables esmentades en els patrons per a documentació però no utilitzades.[25]
començant per ':' (caràcter dos-punts) 
constructors no alfanumèrics,[26] per exemple:
  • (:) constructor cons de llistes (ex.: cap : cua)
  • (:+) constructor de nombres complexos (ex.: part_real :+ part_imag)
  • (:.) constructor de dimensions i d'índexs per a vectors multidimensionals (ex.matriu 3x3: (Z :. 3 :. 3)) al paquet Repa
començant per altres símbols
operadors

Sintaxi[modifica | modifica el codi]

Vegeu. produccions sintàctiques del Haskell2010.[27]

comentaris[modifica | modifica el codi]

 {-
  comentari multilínia
  {- comentari niuat
  -}
 -}
 -- comentari fins a fi de línia

comentaris d'autodocumentació[modifica | modifica el codi]

Comentaris[28] per a ésser extractats a un resum mitjançant el programa haddock[29]

{-| comentari en bloc que precedeix una declaració
-}
 
-- | comentari en línies que precedeix una declaració
--   continuació del comentari
 
-- invocació per generar documentació en html (opció -h):
 
haddock -h fitxers.hs -o docdir
 
{- poden sortir missatges "Warning: Haddock could not find link destinations for ..(paquets)"
 
per evitar-los cal generar enllaços a la doc. dels altres paquets, esmentant per cada paquet l'opció
 --read-interface (-i) amb el camí de la doc i el fitxer d' ''interfície haddock'' (ext.: .haddock)
 (que es pot generar amb l'opció --dump-interface) per exemple:
 
-i /usr/share/doc/ghc6-doc/html/libraries/base-4.2.0.0,/usr/lib/ghc-6.12.1/haddock/base-4.2.0.0/base.haddock
 
-}

definició de blocs[modifica | modifica el codi]

De dues maneres possibles:

  • mitjançant claus {} i separant les instruccions per punt-i-coma
  • segons el marge esquerre de la disposició (sagnat).[30] Els punt-i-coma al final de línia es poden estalviar.

Identificadors[modifica | modifica el codi]

Espais de noms[modifica | modifica el codi]

Hi ha sis menes de noms que constitueixen espais independents (es pot fer servir el mateix identificador per al que convingui en cada espai)

  • variables
  • constructors (els de la dreta de l'assignació en una instrucció data)
  • variables de tipus
  • noms (constructors) de tipus
  • noms de classes de tipus
  • noms de mòdul

Exemples de possible confusió:

  • L'identificador ArithException és un constructor (dins el tipus Exception de H98) i també un nom de tipus ArithException

Vegeu-ho aquí

Funcions binàries en posició infix en expressions[modifica | modifica el codi]

Qualsevol funció de dos operands es pot emprar en posició infix (entremig) si es posa el seu nom entre cometes revesses, fetes amb la tecla de l'accent greu seguida de la tecla espai. En anglès s'hi refereixen per backquotes

 mes x y = x + y
 
 resultat = 4 `mes` 3

operador $[modifica | modifica el codi]

L'operador d'aplicació ($) és molt freqüent i estalvia parèntesis en aplicar una funció a una expressió de més d'un terme.

  • excepte quan el paràmetre o el retorn són funcions polimòrfiques sense declaració de tipus, com s'explica a la ref.[31]
  • ($) té la menor de les precedències dels operadors i associativitat per la dreta.[32]
f $ x = f x
 
f $ g z $ x + y === f ( g z (x + y))  -- excepte si g no té declaració de tipus

Tipus i classes[modifica | modifica el codi]

El tipus indica únicament el domini, la classe les operacions que pot implementar un tipus.

Les classes de tipus no són com les classes de C++, que són classes d'objectes, sinó com els Interface de Java

classes i clàusula deriving[modifica | modifica el codi]

La clàusula deriving demana al compilador que derivi instàncies (implementacions)[33][34] de classes bàsiques[35] a partir de les representacions internes dels tipus. Aquestes classes són:

classe context descripció operacions
(requeriments de
les variables)
Eq α[36] Igualable (==) (/=)
Ord α[37] Eq α Ordenable (<), (<=), ..., (compare), (max), (min)
Enum α[38] Enumerable (succ: successor) (pred: predecessor)
(toEnum: des de sencer) (fromEnum: cap a sencer)
Ix α[39] Ord α Que pot indexar (range), (inRange), (rangeSize), (index: índex basat en 0)
Bounded α[40] Acotat (minBound: cota inferior) (maxBound: cota superior)

Classes per a representació textual:

classe descripció operacions
Show α[41] Convertible en String (show), (showList), (showsPrec).
Read α[42] Convertible des de String (readList), (readsPrec)
  • el mòdul Numèric proporciona un ventall més ampli de conversions entre text i nombres.[43]

Classes de tipus numèrics:

classe context descripció operacions
Num α[44] Numèric
(operacions comunes
a sencers, racionals i reals)
(+), (-), (*), (negate), (abs), (signum), (fromInteger)
Bits α[45] Num α Operacions s/. bits (.&. :i), (.|. :o), xor, complement,
shift, rotate, ...
Real α[46] (Num α, Ord α) Que implementa
conversió a racional
(toRational)
Integral α[47] (Real α, Enum α) Que implementa divisió sencera (div), (mod), (quot: quocient), (rem: romanent),
(toInteger: a sencer de precisió il·limitada)
-- quot i div són similars però difereixen en els valors negatius (quot parteix cap a zero)
quot (-5) 3 == -1 ---- div (-5) 3 == -2
  • Classes per al suport de racionals i reals.
classe context descripció operacions
Fractional α[48] Num α Fraccionable (/), (recip: recíproc), (fromRational)
RealFrac α[49] (Real α, Fractional α) components / info
sobre fraccions
(truncate), (round),
(ceiling: menor sencer superior), (floor: major sencer inferior),
(properFraction: fracció pròpia)
RealFloat α[50] (RealFrac α, Floating α) components / info
dels reals en coma flotant
(significand: normalitzat a l'interval [0.5 .. 1) * signe,
(exponent: segons l'equació: r == (significand r) * (2 ^^ (exponent r))); 1 == 0.12 * 2^^1
-- no correspon a l'exponent de la notació científica
(isNaN: és No-Numèric?),
(isInfinite: resultat de sobreiximent)
Floating α[51] Fractional α ops. en coma flotant
logaritmes i trigonometria
(**: exponenciació), (exp), (log), (sqrt), (sin), (cos), (pi), ...

Els racionals no estan definits com a classe. Són en un mòdul a banda.[52] El tipus està parametritzat pel tipus dels components.

  • (Ratio a): Ops. dels racionals: (%: constructor) (numerator), (denominator), ..

Els reals de Coma fixa estan definits al mòdul Data.Fixed[53]

GHC estén el mecanisme de derivació d'instàncies a algunes classes més. Vegeu ref.[54]

Tipus[modifica | modifica el codi]
bàsics
tipus constructors / àlies mòdul descripció
() -- Unit () Prelude equival al "void" o buit del llenguatge C
Bool[55] True |False Data.Bool
Ordering[37] LT|EQ| GT Data.Ord
Char[56] Data.Char Unicode
String[56] = [Char] Data.Char
Data.String
llista de caràcters
numèrics
tipus context constructors / àlies mòdul descripció
Int, Int<N> N∈{8,16,32,64}[57] Data.Int Sencers (cas de Int: rang [-2^29 .. 2^29-1])
Word, Word<N> N∈{8,16,32,64}[58] Data.Word Naturals, aritmètica sense signe
Integer Prelude Sencer de precisió il·limitada
Ratio t[52] Integral t Data.Ratio Racionals t * t
Rational[52] = Ratio Int Data.Ratio Racionals Int * Int
Float, Double Prelude
Uni, Deci, Centi,
Milli, Micro,
Nano, Pico[53]
Data.Fixed Coma fixa de representació sencera
valor = representació * resolució del tipus
Complex t[59] RealFloat t = !t :+ !t Data.Complex amb prefix '!' d'avaluació estricta
altres
tipus context constructors / àlies mòdul descripció
(t1,t2,...,tn) (,)
(,,)
(,,...,)
Data.Tuple tupla
Maybe t Nothing | Just x Data.Maybe resultats d'operacions parcialment definides
o bé paràm. opcionals
Either tipError tipResultat Left error | Right resultat Data.Either Opció doble dret Right o tort Left
, freq. usat per al resultat
de func. parcialment definida
amb info de l'error
IO tipResultat System.IO operacions d'entrada/sortida
IOError System.IO.Error Excepció en expressions IO
Exception Control.OldException tipus algebraic d'error del Haskell98
obsolet—la nova classe Exception permet construir
excepcions de tipus definits per l'usuari.
SomeException Exception e SomeException e Control.Exception nou tipus genèric de les excepcions
  • classes que implementen (a banda de Eq,[36] Show[41] i Read)[42]
Ord[37] Enum[38] Ix[39] Bounded[40]
() -- Unit
Bool
Ordering
Char
Ord[37] Enum[38] Ix[39] Bounded[40] Num[44] Bits[45] Real[46] Integral[47] Fractional[48] RealFrac[49] RealFloat[50] Floating[51]
Int, Int<N>,
Word, Word<N>,
Integer
No
Rational No No No No No
Float, Double No No No No
Uni, Deci, Centi,
Milli, Micro,
Nano, Pico
No No No No No
Complex t No No No No No No No No No

Vegeu també:

El cas dels tres operadors d'exponenciació[modifica | modifica el codi]

El domini de l'exponent determina el context del domini d'aplicació:

  • (^) exponent natural. Per calcular-lo el domini d'aplicació només ha d'implementar l'operació producte definida a Num, context: (Num t).

De fet, per als naturals de l'exponent, s'utilitza qualsevol tipus que implementi la classe Integral, disparant excepció si no és positiu.

 (^) :: (Num a, Integral b) => a -> b -> a
Prelude> let ex = -2
Prelude> :type 10 ^ ex
10 ^ ex :: Num a => a
Prelude> 2 ^ex
*** Exception: Negative exponent
  • (^^) exponent sencer => l'exponent negatiu requereix que el domini d'aplicació implementi, a més a més de (*), l'elem. invers del producte (recip: recíproc definit a Fractional), context: (Fractional t)
  • (**) exponent en coma flotant => l'exponenciació requereix les operacions exp i logBase definits a Floating. Context (Floating t)
literals[modifica | modifica el codi]

Els literals numèrics no s'associen a un tipus (per la tinença o no del punt decimal) sinó a una classe (signatura) i les seves operacions.

Amb l'intèrpret GHCi podem consultar el tipus d'una expressió amb

Prelude> :type 1
1 :: (Num t) => t  -- tipus t tal que implementa la signatura de ''Num'' ( (+) (-) (*) i altres ops.)
                   --   la classe Num és la més genèrica (implementada per tots els tipus numèrics)
Prelude> :type 1.5
1.5 :: (Fractional t) => t   -- tipus t tal que implementa la signatura de ''Fractional''
                -- implementada per Float, Double, Rational, Uni, Deci, Centi, Milli, Micro, Nano, Pico
 
 0x41  -- hexadecimal :: (Num t) => t
 0o377 -- octal :: (Num t) => t
 
 1E10  -- (1E10) :: (Fractional t) => t
 
 -- caràcters: 'A', notació decimal: '\65', hexadec: '\x41', apòstrof: '\''

El tipus concret es determina, o bé explícitament, o bé per la signatura de l'operador, o bé per una clàusula default.[60] Per exemple la signatura de (+) requereix que el segon operand tingui el tipus del primer. Al literal del segon operand se li assignarà el tipus del primer.

 -- mateixa variable de tipus en paràmetres diferents exigeix tipus idèntics
 -- l'operació (+) :: (Num a) => a -> a -> a
 
 Prelude> (1::Int) + 2   -- per (+) al segon operand 2 se li assigna el tipus del primer
 3
 Prelude> (1::Float) + 2
 3.0
 
-- Des de GHC 7.8 els sencers poden tenir literals amb notació científica
 
n = (1.2E4 :: Int)   -- amb la pragma {-# LANGUAGE NumDecimal #-}
-- la clàusula default és una manera de resoldre les ambiguïtats dels literals
--   reduint les assignacions de tipus explícites
  default (Int, Float, Double)  -- seqüència de tipus assignables

L'extensió de llenguatge OverloadedStrings facilita que els literals de cadenes de caràcters admetin també l'assignació del tipus requerit per l'operador, sempre que el tipus de l'operand implementi la classe IsString, facilitant-ne la conversió.[61]

{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text)
import qualified Data.Text as T
 
-- T.take :: Int -> Text -> Text
 
abc = T.take 3 "abcdef"  -- el literal "abcdef" pren tipus Text en comptes de String
conversions[modifica | modifica el codi]
  • Des de Sencer
fromIntegral :: (Integral a, Num b) => a -> b
 
Prelude> fromIntegral (5::Int) :: Integer     -- a sencer de precisió il·limitada
5
Prelude> fromIntegral (5::Integer) :: Int
5
Prelude> fromIntegral (5::Int) :: Float
5.0
Prelude> fromIntegral (5::Int) :: Double
5.0

Les conversions entre Int i Word preserven la representació (no pas el signe).[62]

  • Des dels tipus que implementen la classe Real (matemàticament parlant, els reals inclouen a més dels pròpiament anomenats així, els sencers i els racionals). Són instància de Real els Float, Double, Int, IntN, Integer, Word, WordN, Racionals (Integral a => Ratio a), .[63]
realToFrac :: (Real a, Fractional b) => a -> b
 
fromRational :: Rational -> a
 
Prelude> realToFrac  (1.0::Float) :: Double
1.0
Prelude> realToFrac  (1.1234567890::Double) :: Float
1.1234568
# incorporem el mòdul de coma fixa Data.Fixed
Prelude> :m +Data.Fixed
Prelude Data.Fixed> realToFrac (1.5 ::Float) :: Centi
1.50
# incorporem el mòdul dels racionals
Prelude> :m +Data.Ratio
Prelude Data.Ratio> realToFrac 1.50 :: Rational
3 % 2
Prelude Data.Ratio> fromRational (2 % 3) :: Float
0.6666667

tipus algebraics[modifica | modifica el codi]

enumeracions[modifica | modifica el codi]

Cal afegir-hi la clàusula deriving perquè el compilador instancii les operacions bàsiques per al tipus definit.

 data DiaSetm = Dl | Dm | Dc | Dj | Dv | Ds | Dg
                deriving (Show, Eq, Ord, Enum, Bounded)
Tipus producte[modifica | modifica el codi]
data TRectangle = Rectangle Int Int -- (Int * Int) amb constructor Rectangle

El constructor equival a una funció que retorna un element del tipus a partir dels components. De fet a GHC (amb l'extensió GADTs) es pot escriure

 data TRectangle where
          Rectangle :: Int -> Int -> TRectangle

Encaix dels paràmetres a partir del constructor

 perímetre :: TRectangle -> Int
 perímetre (Rectangle x y) = 2 * x * y
Registres[modifica | modifica el codi]

Tipus producte amb noms de camps com a funcions accessores als components. No hi ha registres anònims (cal distingir-los pel constructor).

  • (els noms dels camps pertanyen a l'espai de noms del mòdul i convé afegir-los-hi un prefix per evitar col·lisions)
 data TPersona = Persona {pNom::String, pEdat::Int, pAlçada::Float} deriving (Show, Eq)
 data TQuisso = Quisso {qNom::String, qEdat::Int, qAlçada::Float} deriving (Show, Eq)
 
 x = Persona {pNom="Joan", pEdat=33, pAlçada=1.50}
 
 -- equivalent amb inicialització posicional
 x = Persona "Joan" 33 1.50
 
 -- accedint per nom de camp, el tipus de l'accessor pNom :: TPersona -> String
 nom = pNom x
 
 -- encaix posicional
 (Persona nom _ _) = x
 
 -- actualització
 y = x {pAlçada=1.72}
 
 -- encaix per nom -- ''p @ (Persona {pEdat = v_edat})'' es llegeix: ''p com (Persona quina pEdat és v_edat)''
 
 aniversari p @ Persona {pEdat = v_edat} =    -- el tipus de pEdat :: TPersona -> Int
   p { pEdat = v_edat +1}
 
 -- amb encaix simplificat {c} equival a {c = c} a GHC amb {-# LANGUAGE NamedFieldPuns #-}
 
 aniversari p @ Persona {pEdat} =   -- el tipus de pEdat en l'àmbit de la def. és :: Int
   p { pEdat = pEdat +1}
 
 -- ''NamedFieldPuns'' també desdobla {camp} com a {camp = camp} a l'actualització (dreta de la def.)
 estableixEdat pEdat p = p {pEdat}

Per la sintaxi simplificada a GHC vegeu[64]

Reversibilitat de tipus en registres d'un sol component[modifica | modifica el codi]

És una construcció força utilitzada per què en aquests registres, el constructor i l'accessor són funcions inverses l'una de l'altra.

data NouTipus a = Constructor { accessor :: a}
 
--         Constructor :: a -> NouTipus a
--         accessor :: NouTipus a -> a
 
--   (accessor . Constructor) == id
Tuples[modifica | modifica el codi]

tipus producte anònim amb constructors com: (,) ; (,,) ; ... .[65] Ops. definides a Data.Tuple [66]

 ()     -- la tupla buida és un valor del tipus Unit (el dels procs. que no retornen res)
        -- i també designa el tipus esmentat.
 
 parell = (1,"abc")  -- valor
 (Int, String)    -- tipus
 
 fst parell      -- funció primer (fst) i segon (snd) (només implementades per a tupla2),
         -- no definit a tupla3.. i següents
 snd parell      -- segon
 (,)       -- constructor de tupla2: (,) a b == (a,b)
 
 -- encaix
 (primer, segon) = parell
tipus suma[modifica | modifica el codi]

Unió de tipus discriminada per etiquetes (constructors), seguits dels components dels membres

 -- en aquest exemple l'etiqueta o ''constructor'' coincideix amb el nom del tipus que el segueix
 --     en diverses ocasions però és vàlid perquè són espais de noms diferents
 
data Exception  -- a GHC, aquest tipus ha passat al mòdul Control.OldException
 = IOException  IOException     -- IO exceptions
 | ArithException       ArithException  -- Arithmetic exceptions
 | ArrayException       ArrayException -- Array-related exceptions
 | ErrorCall            String          -- Calls to 'error'
 | ExitException        ExitCode        -- Calls to 'System.exitWith'
 | NonTermination           -- Program is in an infinite loop
 | UserError      String     -- General purpose exception
 ...
 
type TCoord = Double
 
-- estil algebraic
data TPunt = Punt2D TCoord TCoord
             | Punt3D TCoord TCoord TCoord
             deriving (Eq, Show)
 
-- estil GADTs ( algebraic generalitzat) -- requereix pragma: {-# LANGUAGE GADTs #-}
-- per cadascun dels constructors se'n descriu el tipus:
data TPunt where
  Punt2D :: TCoord -> TCoord -> TPunt
  Punt3D :: TCoord -> TCoord -> TCoord -> TPunt
  deriving (Eq, Show)
 
p2D = Punt2D 2 4
p3D = Punt3D 4 6 8
 
-- encaix discriminant pel ''constructor''
 
mòdul :: TPunt -> TCoord
mòdul (Punt2D x y) = sqrt $ x^2 + y^2
mòdul (Punt3D x y z) = sqrt $ x^2 + y^2 + z^2
tipus polimòrfics[modifica | modifica el codi]

Amb variables de tipus (els ident. que comencen per minúscula):

 -- el tipus Maybe : opcionalitat de valor
 -- per a paràmetres opcionals o resultats de funcions definides parcialment
 data Maybe a   = Nothing | Just a deriving (Eq, Ord, Read, Show)
 
 -- tipus recursius
 data Arbre tip_node = Fulla tip_node | Branca (Arbre tip_node) (Arbre tip_node)
  • tipus amb context. Aquesta construcció està subjecta a controvèrsia i potser s'eliminarà[67]
 -- tipus amb context restringint els tipus dels components
 data (Eq a) => Conjunt a = ConjBuit | ConjElem a (Conjunt a)

àlies o sinònims de tipus[modifica | modifica el codi]

Els sinònims de tipus són com macros al nivell de tipus.[68]

-- EBNF "type" nom_de_tipus [" " param { " " param}] "=" expressió_de_tipus
type String = [Char]
type LaMevaEstructura = (String, Int, Double)
sinònims paramètrics[modifica | modifica el codi]

El següent sinònim inclou paràmetres de tipus.

type LlistaNumerada a = [(Int, a)]
type Element t         -- #"Àlies de Tipus" associats a la classe
sinònims liberalitzats (extensió LiberalTypeSynonyms)[68]
type Discard a = forall b. Show b => a -> b -> (a, String)

llistes[modifica | modifica el codi]

El tipus Llista amb constructors "[]" (llista buida, anomenat nil)[69] i ":" (tip_elem * llista_elem, anomenat cons)[69][70]

data [a] = []  |  a : [a]  deriving (Eq, Ord)
[]    -- llista buida
[1,2,3] -- valors homogenis
[a] -- en una decl. de tipus, llista d'elements de tipus a
[1,3..10] == [1, 3, 5, 7, 9]
[1,3..] == [1, 3, 5, 7, 9, ...  -- llista infinita
elem_cap : llista_cua   -- ':' és el constructor de llistes (en pos. infix)
llista !! posició—consulta element, el primer és llista!!0
iterate f x == [x, f x, f (f x), ...] -- llista infinita per aplicació reiterada d'una funció
repeat x               -- llista infinita per repetició
cycle xs—converteix la llista xs en circular—elem: pertinença a la llista
ésFeiner dia = dia `elem` [Dl, Dm, Dc, Dj, Dv]

Més operacions a contenidors

llista per comprensió[modifica | modifica el codi]

amb generadors i filtres separats per comes

s = [ x+y | x <- [0,2..], y <- [1,3..20], x^2 > 3, y `mod` 9 /= 0 ]
print $ take 5 s  -- imprimeix els primers
llista per comprensió monàdica[modifica | modifica el codi]

Vegeu-ho a Mònada_(programació_funcional)#Llista_per_comprensió_monàdica

llistes per comprensió a l'estil de SQL[modifica | modifica el codi]

Consultes a l'estil de SQL sobre generadors. Vegeu-ho a GHC

tires de text[modifica | modifica el codi]

-- el tipus String (cat: Cadena) és una llista de caràcters i és el tipus de les tires incloses al codi. String no és definit com a estructura (data) sinó com a àlies o sinònim (type) de l'expressió:

type String = [Char]
-- literals: "" "abc" "\"abc\""

Vegeu exemple de cadenes més eficients a #Cadenes de text implementades amb vectors

  • Llegiu l'article "How to pick your string library in Haskell"[71]

Expressions[modifica | modifica el codi]

funcions[modifica | modifica el codi]

A la declaració cal especificar els tipus dels paràmetres separats per fletxes; si son genèrics en minúscula

  • la repetició d'una variable de tipus, en la signatura, indica que el tipus ha de ésser idèntic que el del paràmetre on apareix primer.
-- (el que ve abans de "=>" és el context de prerequisits dels tipus dels paràmetres)
ésApreciable :: (Ord a) => a -> a -> Bool    -- cal que el tipus ''a'' implementi  Ord ("Ordenable")
ésApreciable llindar valor = valor >= llindar

Funcions d'ordre superior: quan algun dels paràmetres o el resultat és una funció

flip :: (a -> b -> c) -> b -> a -> c
flip f x y  =  f y x
casuística per encaix de patrons dels paràmetres[modifica | modifica el codi]

El nom de la funció precedeix cada cas.

-- _ és el comodí
llargada :: [a] -> Int
llargada [] = 0            -- [] patró constructor nil de llista buida.
llargada (_:xs) = 1 + llargada xs    -- (_:_) patró constructor cons com a (cap:cua)

!! GHC no avisa quan els encaixos son incomplets (no exhaustius),[72] excepte que ho demanis explícitament amb l'opció -fwarn-incomplete-patterns o -Wall

La determinació de si un encaix és viable s'avalua de manera estricta. Per a avaluar-los de manera tardana cal prefixar-los amb ~[19]

assignació del valor encaixat a una variable[modifica | modifica el codi]

l'operador @ (as-pattern) com a variable @ patró unifica la variable i el terme encaixat al patró

descriuLlista ll @ (_:_) = "llista no buida: " ++ show ll
Signatures de tipus als patrons[modifica | modifica el codi]

Totes les variables de patrons admeten signatures, quines variables de tipus han de pertànyer a l'àmbit limitat pel context.[73]

  f :: forall a. [a] -> (Int, [a])
  f xs = (n, zs)
    where
      (ys::[a], n) = (reverse xs, length xs) -- OK
      zs::[a] = xs ++ ys                     -- OK
 
      Just (v::b) = ...  -- Not OK; b is not in scope
Guardes[modifica | modifica el codi]

Casuística de definició de funcions basada en condicions

 signe x
     | x > 0  = 1
     | x == 0 = 0
     | otherwise = -1
Guardes amb encaix (ang: pattern guards)[modifica | modifica el codi]

Haskell2010. Casuística de definició de funcions basada en alternatives d'encaix, però no sobre els paràmetres sinó sobre expressions que els incorporen.[74]

Cada cas pot contenir una seqüència d'encaixos separada per comes. En notació EBNF:

 alternativa ::=  "|" patró "<-" expressió {"," patró "<-" expressió} "=" definició

Exemple

addLookup env var1 var2
   | Just val1 <- lookup env var1
   , Just val2 <- lookup env var2
       = val1 + val2
   | otherwise = ...
Lligams d'àmbit local[modifica | modifica el codi]

Vegeu "Let vs Where".[75]

estil declaratiu (ús abans de la definició) 
permet definir variables esmentades als patrons i guardes de la definició (a cada patró el seu where)
-- en una definició amb (=) o en una alternativa de case (amb (->))
    patró | guarda = expressió
              | guarda2 = expressió2
    where declaracions_locals
estil imperatiu (definició abans de l'ús)
estableix àmbits successius permetent redefinir un mateix símbol si és en àmbits diferents
-- ghci
Prelude>let x = 1 ; y = x in let x = y +1 in print x
2  -- resultat

Als blocs do els subblocs let (que no duen la paraula reservada in al final) permeten definir expressions basades en resultats d'accions (efectes laterals) precedents

main = do
         s <- getLine
         let x = (read s :: Int)
             y = x+1
         let x = y +2
         print x
Àmbit dinàmic - paràmetres implícits[modifica | modifica el codi]

Amb variables lligades a l'àmbit del procediment que fa la crida.

  • Cal prefixar-ne les variables amb '?' i esmentar-les als requisits de context.
  • Cal l'extensió de llenguatge {-# LANGUAGE ImplicitParams #-}. Vegeu ref.[76]
{-# LANGUAGE ImplicitParams #-}
 
ambParàmetreVImplícit :: (?v :: Int) => Int -> Int
ambParàmetreVImplícit x = ?v * x
 
cridaIntermèdia :: (?v :: Int) => Int
cridaIntermèdia = ambParàmetreVImplícit 2
 
prova = let ?v = 2
        in cridaIntermèdia
 
main = print prova

Alternatives if, case[modifica | modifica el codi]

-- if del haskell98
if expr_booleana then expr1 else expr2
 
-- if del haskell2010, es pot partir en línies
if expr_booleana
    then expr1
    else expr2
 
case expr of
    patró | guardaA -> exprA
          | guardaB -> exprB
    patró2 | guarda2 -> expr2
 
-- alternativa múltiple
case () of
    _ | condició1 -> expr1
      | condició2 -> expr2
      | otherwise -> expr3
 
-- versió moderna (desde GHC v. 7.6.1 !!) requereix pragma d'extensió de llenguatge MultiWayIf:
{-# LANGUAGE MultiWayIf #-}
if | x == 0    -> [...]
   | x > 1     -> [...]
   | x < 0     -> [...]
   | otherwise -> [...]

Plegat -- iteracions a l'estil de for / foreach sense efectes col·laterals[modifica | modifica el codi]

L'operació foldl (foldLeft) permet acumular "transformacions reiterades sobre una estructura" amb una funció (a -> b -> a) i els valors d'una seqüència [b], de manera equivalent als bucles for de la prog. imperativa tradicional, per als casos sense efectes col·laterals. La funció equivaldria al nucli de la iteració.

-- essent ''a'' el tipus de l'estructura i ''[b]'' la llista de valors a iterar
 
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl op estructura     []     = estructura
foldl op estructInicial (x:xs) = foldl op (op estructInicial x) xs

Vegeu també iteracions a espai constant de la pila amb plegat per l'esquerra estricte

L'operació mapAccumL de Data.List és una combinació de foldl i map. Permet transformar una llista amb una funció d'un acumulat:

mapAccumL :: (a -> b -> (a, c)) -> a -> [b] -> (a, [c])

Expressions lambda com a funcions anònimes[modifica | modifica el codi]

La barra invertida, caràcter del teclat que més s'assembla a la lletra grega lambda, introdueix una funció anònima com a expressió.

 \ x y -> x + y

definició d'operadors[modifica | modifica el codi]

 (++) :: [a] -> [a] -> [a]   -- concatenació de dues llistes
 infixr 5 ++         -- associativitat dreta o esq. (''infixr'' | ''infixl'') o no (''infix''),
               -- i precedència de l'operador (per defecte ''infixl'' 9)
 []   ++ ys = ys       -- cas simple
 (x:xs) ++ ys = x : (xs ++ ys) -- cas recursiu

Respecte les precedències vegeu la ref.[32]

operadors de funcions[modifica | modifica el codi]

( . ) composició: Punt envoltat d'espais, (sense espais el punt és el qualificador de noms importats)

f . g

($) aplicació normal (tardana) f $ x = f x. Permet estalviar parèntesis en aplicar operacions sobre el darrer paràmetre quan és una expressió (hi ha més d'un terme).

Restricció: Hi ha objeccions a la seva aplicació en cas que el paràmetre o el retorn siguin funcions polimòrfiques que no tinguin declaració de tipus.[31]
f $ g $ x + y === f ( g (x + y))
 
-- exemple:
print $ "el resultat és" ++ show result
-- en comptes de
print ("el resultat és" ++ show result)
lectura de dreta a esquerra en la composició[modifica | modifica el codi]

En aplicar una composició de funcions (f . g . h) obj sobre una variable o expressió, l'entendrem més fàcilment si les analitzem tenint en compte el tipus de l'objecte (domini) de l'aplicació i llavors les funcions que s'hi apliquen, començant per l'última de la composició, en sentit de dreta cap a l'esquerra.

Es pot escriure la composició a l'estil de flux de dades (d'esquerra a dreta), definint un operador (per exemple (.$) = flip ($) seguint l'exemple del llenguatge F# (|>) anomenat pipeline),[77] associatiu per l'esquerra

import Control.Exception (assert)
import Control.Category ((>>>))   -- morfismes
 
-- definició d'un operador binari per a l'aplicació cap enrere (sobre l'objecte, com (|>) a L'"F sharp")
x .$ f = f x
 
-- o bé definit en base a ($)
-- (.$) = flip ($)
 
obj = "0"
f = (++"3")
g = (++"2")
h = (++"1")
 
prova0 = f (g (h obj))       -- pels aimants dels parèntesis (enyorats del LISP)
prova1 = f $ g $ h obj       -- estil aplicatiu
prova2 = (f . g . h) obj     -- estil funcional
prova3 = (h >>> g >>> f) obj -- estil composició de fletxes (morfismes)
prova4 = obj .$ h .$ g .$ f  -- estil navegació de dades (com a la O.O. obj.mètode),
 
comprovació = all (== "0123") [prova0,prova1,prova2,prova3,prova4]
main = do
         print prova0 ; print prova1
         print prova2 ; print prova3 ; print prova4
 
         assert comprovació $ print "són iguals"

Aplicació parcial[modifica | modifica el codi]

funció a partir d'una altra funció ometent el(s) darrer(s) paràmetre(s)[78]

-- també
 suma = (+)                -- lliga el nom 'suma' a l'op. binari (+)
 suma3 = suma (3::Int)     -- :: Int -> Int        -- aplicació parcial
 total = suma3 4
Secció d'un operador infix[modifica | modifica el codi]

Aplicació parcial dels operadors infix. Vegeu ref.[79]

 suma3 = (3+)           -- aplicació parcial de l'expressió

Currificació[modifica | modifica el codi]

  • curry: converteix una func. d'una Tupla2 en una que admet els paràm. separats
+ possibilita l'aplicació parcial de part dels paràmetres
  • uncurry: conv. una funció de dos param. en una que els admet aparellats en Tupla2
 curry::((a,b) -> c) -> a -> b -> c
 
 uncurry::(a -> b -> c) -> (a,b) -> c
 
 --
 f = \(x,y) -> 2*x+y
 f_currificada = curry f
 
 assert (f (3, 4) == f_currificada 3 4) "equivalència de currificació"
 
 -- aplicació parcial
 op :: a -> b -> c
 (`op` segon) :: a -> c
 (primer `op`) :: b -> c

excepcions[modifica | modifica el codi]

Les excepcions només poden ser caçades "dins d'una expressió mònada o clàusula do"[80]

catch
la clàusula catch té dos parametres, l'operació vetllada i el manipulador d'excepcions

Les excepcions del Haskell98 (tipus Exception) a GHC han passat al mòdul Control.OldException[81]

-- excepcions del Haskell98.
import Control.OldException
 
catch
  (evaluate (x/y) >>= print)   -- ''evaluate'' força l'avaluació
  (\excep -> case excep of
          ArithException ae | ae == DivideByZero -> print "divisió per zero"
          _ -> print ("excepció per " ++ show excep)
  )

Per a les excepcions noves de GHC (qualsevol tipus que implementi la classe Exception), són al mòdul Control.Exception[82]

{-# LANGUAGE ScopedTypeVariables #-}
import Prelude hiding (catch)
{- Del codi del Prelude:
-- Non-I\/O exceptions are not caught by this variant; to catch all
-- exceptions, use 'Control.Exception.catch' from "Control.Exception".
-}
import Control.Exception
 
f = (evaluate expr) `catch` \ (excep :: ArithException) -> tracta_Arith excep
          `catch` \ (excep :: IOException)    -> tracta_IO excep
          `catch` \ (excep :: SomeException)  -> tracta_Altres excep
try
la clàusula try (assajar) no tracta les excepcions, les retorna en un tipus mixt
retorna en un tipus (Either tipusExcepció tipusResultat) "o bé Left amb l'error o bé Right amb el resultat. (Left i Right, a banda d'esquerra i dreta, també tenen el significat de tort i dret respectivament)


-- prova.hs
import Control.Exception (try, evaluate, ArithException)
import Data.Fixed (Uni, Deci, Centi, Milli, Micro, Nano, Pico) -- coma fixa de representació sencera
--                                                        (sencer * resolució_del_tipus)
 
-- try :: IO a -> IO (Either Exception a)
 
type TipDada = Micro -- coma fixa, proveu també TipDada = Double
 
main = do
         let x = 0.0001 :: TipDada
             y = 0 :: TipDada
         result <- try (evaluate (x/y)) :: IO (Either ArithException TipDada)
         case result of
             Right valor -> putStrLn $ "correcte: " ++ show valor
             Left excep -> putStrLn $ "excepcio: " ++ show excep
  • evaluate s'utilitza per forçar l'avaluació de l'expressió en el context de la mònada IO[83]

Amb TipDada == Micro (Coma fixa) l'excepció salta

excepcio: divide by zero

Amb TipDada == Double, no hi ha excepció (IEEE 754iDivisió entre zero), dóna:

correcte: Infinity
Funcions d'error[modifica | modifica el codi]
 userError :: String -> IOError
 userError missatge        -- construeix una excepció IOError de l'aplicació
Llançadors[modifica | modifica el codi]
 throw excep   -- llança excep dins el codi funcional
 throwIO excep  -- llança excep. dins la mònada IO (expressió IO o clàusula ''do''),
         --       garanteix l'ordre en operacions IO.
 ioError :: IOError -> IO a -- llança una excepció IOError dins la mònada IO,
         --      exemple: ioError (userError missatge)
 annotateIOError :: IOError -> String -> Maybe Handle -> Maybe FilePath -> IOError
         -- afegeix localització i informacions a un IOError

Vegeu ref.[84]

Errors irrecuperables d'estats inconsistents o resultat d'operacions no definides per als valors dels paràmetres.
 error missatge          -- genera una excepció ErrorCall
{-# LANGUAGE ScopedTypeVariables #-}
import Prelude hiding (catch)
import Control.Exception
 
llistaBuidada = drop 5 [1::Int,2,3]
 
main = (evaluate $ head llistaBuidada)
       `catch` \ (excep :: ErrorCall) -> putStrLn "les llistes buides no tenen cap!!"
Assercions, Precondicions i Postcondicions[modifica | modifica el codi]

assert avalúa la condició, i si és certa retorna el segon argument i ,si no, peta dient-nos on.[85]

L'ús de l'opció d'optimització (-O ó -On |n>0) elimina les assercions del codi objecte.

assert condició expr --
     -- si Fals, genera l'excepció AssertionFailed indicant nom_del_fitxer i línia

per exemple:

 import Control.Exception (assert)
 
 -- amb ''asserts'' de precond. i postcond. en diferents línies així sabrem quina peta
 
 unaFunció p1 p2 = assert (precondició p1 p2)
                   $ assert (postcondició p1 p2 resultat) resultat
  where
   resultat = ...
Vegeu també: Compilador Haskell de Glasgow#Depuració. Afegint punts de control per detectar qui ha cridat l'asserció fallida
"No finalització": Símbol ⊥[modifica | modifica el codi]

Valor intern que indica que el programa acaba prematurament (s'encalla).[86] per exemple l'última opció d'un case és sempre ⊥, que encalla el programa quan els encaixos no són exhaustius i no hi ha cap encaix vàlid per al patró.

En anglès s'anomena "bottom" (com a verb: fondejar, tocar fons, enfonsar, encallar!). En català es podria anomenar "aterratge" per assimilació al mateix símbol "terra" en l'electrònica.

undefined
valor ⊥ per explicitar el tipus d'una funció pendent d'implementar i poder així passar la compilació. Crida a la funció error.
identificador patrons = undefined :: tipus
excepcions en la gestió de recursos[modifica | modifica el codi]

Clàusules similars al try ... finally d'altres lleng.

bracket
enclou en tres paràmetres la vetlla d'excepcions en un càlcul amb un recurs, i els manipuladors per adquirir-lo i per alliberar-lo en cas d'excepció o en acabar.
  • el recurs obert a la primera part es passa com a paràmetre a les altres dues.
 bracket :: IO a ->    -- (adquisició de recurs), ex.: (openFile nom_fitxer mode) :: IO Handle
      (a -> IO b) ->  -- (alliberament del recurs), per ex.: hclose :: Handle -> IO ()
      (a -> IO c) ->  -- (computació amb el recurs), ex.: (\handle -> do {...}) :: Handle -> IO c
      IO c       -- retorna el resultat de la computació
exemple: bracket (openFile nom_fitxer mode) hClose computació_amb_el_descriptor_del_fitxer
A System.IO: withFile nom_fitxer mode = bracket (openFile nom_fitxer mode) hClose
   ús: withFile nom_fitxer ReadMode (\descriptor_del_fitxer ->
                     do {acció_amb_el_descriptor_del_fitxer})
finally
assegura l'exec. de la segona part malgrat excepcions en la primera
finally (computació) (coses_a_fer_en_acabar)
-- se sol veure més en notació ''infix''
(computació) `finally` (coses_a_fer_en_acabar)
onException
com finally però només en cas d'excepció, que és rellançada en acabar el segon bloc

Traçabilitat dins el codi funcional[modifica | modifica el codi]

El codi funcional no permet l'ús de print. Cal fer servir Debug.Trace.trace.[87] Imprimeix el primer paràmetre, via unsafePerformIO i retorna el segon.[88]

-- prova.hs
import Debug.Trace (trace)    -- trace :: String -> a -> a
 
quadrat x = resultat'
  where
    resultat' = trace msg resultat
    resultat = x * x
    msg = "traça de quadrat de "++ show x ++ " és: " ++ show resultat
 
cub x = x * quadrat x
 
main = putStrLn $ "cub de 2: " ++  show (cub 2)

A partir de GHC v.7.4.1 hi ha també traceStack :: String -> a -> a[89] que a més bolca la traça de crides si s'ha compilat amb opcions de perfilat.

Subtituïm trace per traceStack i ...

ghc --make -prof -fprof-auto-calls prova.hs
./prova
traça de quadrat de 2 és: 4
Stack trace:
  Main.quadrat.resultat' (prova.hs:6:17-39)
  Main.cub (prova.hs:10:13-21)
  Main.cub (prova.hs:10:9-21)
  Main.main (prova.hs:12:42-46)
  Main.main (prova.hs:12:36-47)
  Main.main (prova.hs:12:19-47)
  Main.main (prova.hs:12:8-47)
  Main.CAF (<entire-module>)
cub de 2: 8

Comportament: Classes de tipus[modifica | modifica el codi]

Els tipus només descriuen el domini de valors.

Les classes de tipus no són com les classes de C++, que són classes d'objectes, sinó com els Interface de Java.

Les classes designen la interfície (signatures) d'un conjunt d'operacions que els tipus han d'implementar mitjançant la instrucció instance. La instrucció class admet la implementació de mètodes per defecte, que les instàncies poden redefinir

Especialització, instanciació per tipus[modifica | modifica el codi]
-- per aquells tipus t que implementin les classes ''Base''
class (ClasseBaseA t, ClasseBaseB t, ...) => ClasseDerivada t where ...
{-# LANGUAGE NamedFieldPuns #-}  -- sintaxi simplificada de registres
class PropXY t where
  getXY :: t -> (Int, Int)
  setXY :: Int -> Int -> t -> t
 
-- funcionalitat 2D requereix que el tipus implementi PropXY
 
class (PropXY t) => IFun2D t where
  desplaça2D :: Int -> Int -> t -> t
 
  -- implementació per defecte a la classe
 
  desplaça2D dx dy punt = setXY (x+dx) (y+dy) punt
    where
      (x,y) = getXY punt
 
data TPunt2D = Punt2D {x, y ::Int}
 
instance PropXY TPunt2D where
  getXY Punt2D {x, y} = (x, y)
  setXY x y punt = punt {x, y} -- equival a punt {x = x, y = y}
 
instance IFun2D TPunt2D  -- TPunt2D incorpora la implementació per defecte

Vegeu exemple més complet més avall

Instanciació genèrica[modifica | modifica el codi]

Quan totes les operacions es basen en operacions de les classes base i no en particularitats d'un tipus, es pot definir la instanciació a nivell de classe amb efecte automàtic. (Requereix les extensions de llenguatge esmentades).

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
 
class PropXY t where
  getXY :: t -> (Int, Int)
  setXY :: Int -> Int -> t -> t
 
class (PropXY t) => IFun2D t where
  desplaça2D :: Int -> Int -> t -> t
 
-- (PropXY t) implica (IFun2D t) després del que segueix
 
instance (PropXY t) => IFun2D t where
  desplaça2D dx dy punt = setXY (x+dx) (y+dy) punt
    where
      (x,y) = getXY punt

Coŀleccions polimòrfiques[modifica | modifica el codi]

Tipus existencial[modifica | modifica el codi]

És un mecanisme de tipus per poder acomodar components de tipus divers que tinguin una interfície en comú i així poder fer-ne llistes i altres coŀleccions.

Vegeu [90] [91] El tipus existencial, originàriament és una mena de tipus abstracte, relacionat amb registres que poden prendre diverses formes concretes que se'n consideren subtipus, donant lloc a un conjunt de tipus.

(pseudocodi)
T = ∃x { v: x; f: (x -> Int); }   -- tipus de registre, variant el tipus x
IntT = { v: Int; f: (Int -> Int); }       -- subtipus on x = Int
FloatT = { v: Float; f: (Float -> Int); }    -- subtipus on x = Float
  • El compilador GHC utilitza la paraula reservada forall (quantificador ∀), per als tipus existencials apel·lant a un isomorfisme.[92][93]
  • Altres compiladors de Haskell fan servir la paraula reservada exists com a quantificador existencial, el EHC/UHC segons la ref.,[94] i el JHC també,[95] en comptes del forall del GHC.
 -- la variable del quantificador no ha d'aparèixer a l'esquerra del '='
 data T = forall a. ConstructorT { v::a, f:: (a->Int)}
 
 unValorIntT = ConstructorT { v = 1::Int, f = identitat}
 unValorFloatT = ConstructorT { v = 1.5::Float, f = part_sencera}
 
 -- afegint-li requeriments
 data T = forall a (Ord a, Bounded a) => ConstructorT { v::a, f:: (a->Int)}

Tipificació més habitual, per exemple, objectes amb un component textualitzable (Show a)

 data TObj = forall a (Show a) => Obj a
 
 unValorNumèric = Obj 1
 unValorString = Obj "abc"
 
 -- equivalent amb sintaxi GADTs, sense necessitat del quantificador
 data TObj where
       Obj :: (Show a) => a -> TObj

Llistes homogènies d'elements amb components heterogenis[modifica | modifica el codi]

Vegeu.[96] El #Tipus existencial permet definir tipus de dades polimòrfics amb components caracteritzats per admetre un mateix grup d'operacions.

D'aquesta manera es pot treballar amb llistes d'elements que incorporen components de tipus divers que implementin una mateixa classe de tipus, i tractar-los amb les operacions de la classe.

Cal especificar l'opció de compilació "-XExistentialQuantification" o la pragma {-# LANGUAGE ExistentialQuantification #-}

{-# LANGUAGE ExistentialQuantification #-}
 
 -- tipus amb constructor Obj i un component
 --    d'aquells tipus ''a'' que implementin la classe Show
 
 data TObj = forall a. (Show a) => Obj a
 
 xs :: [TObj]
 xs = [Obj 1, Obj "foo", Obj 'c']
 
 mostra :: [TObj] -> String
 mostra [] = ""
 mostra ((Obj x):xs) = show x ++ mostra xs  -- aplica al component x l'operació show
                                            -- de la classe Show que els components d'Obj implementen

Vegeu-ne l'exemple #Tipus existencial - Emulació del despatx dels mètodes virtuals de la O.O.

Efectes coŀlaterals[modifica | modifica el codi]

Les operacions d'efectes col·laterals (canvis d'estat, interaccions amb el món exterior) es formulen, principalment, mitjançant el TAD Mònada que assegura l'ordre de les operacions, indeterminat en Haskell, mitjançant l'encadenament.

En demanar, de manera tardana, el resultat d'un encadenament cada lligada (>>=) o (>>) requereix l'avaluació prèvia de l'operació precedent en la cadena, establint l'ordre seqüencial.

Les funcions del mòdul Control.Monad (forM_ llista acció, forever acció, replicateM_ n acció) fan la feina de les clàusules de control imperatiu (forEach, while true do ..., for i = 1 to n do ...) d'altres llenguatges.[97]

Entrada / Sortida - Mònades[modifica | modifica el codi]

Haskell modela l'entrada/sortida com una seqüència encadenada d'operacions, encapsulant els canvis d'estat d'accés a fitxers. Aquest model encaixa amb el concepte de mònada, donant lloc a la mònada IO.

La funció inicial main de qualsevol programa ha de ser una expressió d'operacions d'ent./sortida i per tant del tipus de la mònada IO.

 -- expressions de la ''mònada'' IO (el tipus porta com a paràmetre el tipus del resultat de les operacions)
 getLine              -- captura línia d'entrada -- tipus ''IO String''
 getLine >>= putStr        -- imprimeix línia introduïda -- tipus ''IO ()''
 putStr "polseu Intro" >> getLine >> putStrLn "Fet" >> return True    -- tipus ''IO Bool''
 return "abc" >>= putStrLn   -- estableix "abc" com a resultat monàdic i l'imprimeix tot seguit
 
 -- >>= -- encadena aplicant la funció següent al resultat de l'operació precedent
 -- >>  -- encadena sense passar resultats
 -- return x  -- op. generadora d'una mònada que contindrà x quedant del tipus IO X
        -- no pressuposa ''retorn'' d'enlloc; per ex. (return 5) :: IO Int
 -- fail missatge  -- dispara excepció en cas d'error en encaixar patrons dins una clàusula do
          -- i retorna l'element neutre de les operacions, ex. IO ()
          -- el tipus de l'excepció depèn de la instanciació de la mònada (IOError cas d'IO),<ref name='rwh_fail'/>
Blocs Do: notació especial de les expressions monàdiques[modifica | modifica el codi]

Encadenament de les instruccions d'un bloc. Vegeu mònada - Blocs Do

La clàusula do exposa l'encadenament de manera seqüencial, amb una expressió monàdica per línia, introduint l'estil imperatiu.

bloc = do
    x <- getLine
    putStrLn x
    putStrLn "Fet"
    return x
 
-- equival a l'expressió, relligant cada línia amb (>>=) o bé (>>)
 
bloc = getLine >>= \ x ->    -- expressió lambda (funció anònima)
                  putStrLn x >>
                  putStrLn "Fet" >>
                  return x
Clàusula let dins el bloc do[modifica | modifica el codi]

NO ens referim al let..in de les expressions. Sense l' in, permet assignar variables a expressions basades en resultats de computacions prèvies (instruccions precedents del mateix bloc).

main = do
          print "Entreu un mot: "
          x <- getLine
          print "Entreu-ne un altre: "
          y <- getLine
 
          -- subbloc ''let'' d'expressions sobre resultats precedents
          let { z = x ++ y ; w = z ++ "." }
 
          -- equivalent amb sagnat
          let z = x ++ y
              w = z ++ "."  -- cal aliniar el bloc a la variable definida
 
          putStrLn ("La concatenació és: " ++ show w)
Blocs do niuats[modifica | modifica el codi]

Per posar més d'una línia d'instruccions en una branca d'un "case" o d'un "if" cal agrupar-les dins un subbloc "do"

 import System.IO (stdout, hFlush)
 
 -- pels caràcters no anglosaxons a l'entrada/sortida, si versió GHC < 6.12 utilitzar paquet utf8-string
 -- a partir de GHC >= 6.12.1 incorporarà la codificació de caràcters local del sistema
 --              i no caldrà la substitució de funcions.
 
#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__>=612
 -- no substituir
 msg = "ghc modern"
#else
 import Prelude hiding (putStr, putStrLn, getLine) -- amaguem les funcions predefinides no UTF8 que volem UTF8
 import System.IO.UTF8 (putStr, putStrLn, getLine) -- cal haver instal·lat el paquet "utf8-string"
 msg = "ghc antic"
#endif
 
 -- c_trencada = '\231'
 
 main = do
     putStrLn msg
     putStr "Entreu lletres tot seguit: "
     hFlush stdout
 
     x <- getLine
     case x of
      lletra:_ -> do
             putStr "comença per "
             putStrLn [lletra]
 
#endif

Compilant amb preprocés:[98]

runhaskell -cpp prova.hs
Bloc do com a paràmetre[modifica | modifica el codi]

El bloc do no és més que un artifici sintàctic per escriure una expressió de tipus mònada. Com a tal expressió es pot posar com a paràmetre.

Aquí el posem com a paràmetre (operand de '$') d'un llaç (Monad.forever) amb control d'excepcions.

import Control.Monad as Monad
 
main = do
     print "Farem l'eco fins que l'entrada sigui buida."
     catch(
      Monad.forever $ do           -- repetir indefinidament
        x <- getLine
        case x of
         [] -> ioError (userError "Línia buida") -- llança excepció per sortir del "forever"
         _ -> putStrLn x
      )
      (\excep -> print excep
      )
Recursivitat en els blocs do[modifica | modifica el codi]

Vegeu Mònada_(programació_funcional)#Recursivitat_en_els_blocs_do

Canvis d'estat - Variables[modifica | modifica el codi]

Haskell permet crear objectes mudables, que poden canviar d'estat mitjançant operacions sempre dins d'una mònada (sinó l'ordre, i per tant el resultat, no estarien garantits).

La mònada IO caracteritza l'estat global de l'aplicació.

La mònada ST caracteritza un estat local, permetent encapsular actualitzacions in situ dins de codi funcional pur.

La mònada STM (inicials de Software Transactional Memory) fa possible la sincronització d'accés a variables des de diferents fils d'execució, mitjançant les transaccions en memòria (similars a les de bases de dades)

Caldrà especificar el tipus de la mònada com a tipus resultat de l'expressió o bloc do.

Variables d'estat global no sincronitzades IORef[modifica | modifica el codi]

Amb les referències IORef,[99][100] equivalents de les ref del ML Estàndard, com a efectes dins la mònada IO.

No és convenient utilitzar una mateixa IORef en diferents fils d'execució. Protegir-ne més d'una mitjançant atomicModifyIORef està desaconsellat[101] (les MVar hi són més indicades).

 import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)
 import qualified Control.Monad as Monad
 import System.IO (stdout, hFlush)
 
 tornem_hi :: IORef Bool -> IO ()
 tornem_hi ref_estat = do
 
     x <- readIORef ref_estat     -- llegeix variable ref_estat
     writeIORef ref_estat $ not x   -- escriu
 
     -- modifyIORef ref_estat (not)  -- alternativa: modifica la ref. amb la funció
 
     if x then putStrLn "Blanc"
          else putStrLn "Negre"
 
     putStr "Premeu intro:"
     hFlush stdout
     getLine     -- espera tecleig Intro i ignora el resultat del getLine
                 -- sense variable (v <- getline),
                 --    el bloc do composa les línies amb (>>) en lloc de (>>=)
     return ()
 
 main = do
     ref_estat <- newIORef False    -- crea variable del tipus del valor inicial
                      --  i n'obté la referència
     Monad.forever $ tornem_hi ref_estat  -- repeteix seqüencialment

encapsulament d'efectes col·laterals (amb variables d'àmbit local STRef)[modifica | modifica el codi]

La mònada ST[102][103] encapsula canvis d'estat dins de codi funcional pur i permet fer computacions amb actualitzacions in situ mitjançant referències STRef[104]

en memòria local[modifica | modifica el codi]
  • aplicant runST, en l'espai específic de l'estat del fil d'execució actual, encapsulant els efectes col·laterals de la computació. Cal especificar el tipus de la mònada com a (ST s tipusDelResultat) on la variable s, que indica la zona de memòria, es deixa lliure, indicant que és la del fil d'execució.
 import Control.Monad.ST (ST, runST, stToIO)
 import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef, modifySTRef)
 import Control.Monad as Monad
 
 -- fold_left: aplica operació binària ''f'' acumulant resultat
 --            sobre un valor inicial i els elements d'una llista d'esq. a dreta
 
 foldlST :: (a -> b -> a) -> a -> [b] -> ST s a
 foldlST f acc_ini xs = do
  ref_acc <- newSTRef acc_ini    -- Crea una variable per a l'acumulador amb el valor inicial acc_ini
 
  Monad.forM_ xs $ \x -> do     -- encadena l'aplicació a tots els x de la llista xs...
 
    a <- readSTRef ref_acc     -- llegeix l'acumulador
    writeSTRef ref_acc (f a x)   -- aplica f a l'acumulador i a x i en desa el resultat a la variable
 
    -- modifySTRef ref_acc $ (flip f) x  -- alternativa, flip altera l'ordre dels paràmetres formals
 
  readSTRef ref_acc         -- finalment llegeix l'acumulat que passa a ser
                            -- el valor final de la mònada del bloc do
 
 resultat = runST $ foldlST (+) 0 [1,2,3,4] -- runST aplica el bloc ''do'' en l'espai del fil actual
 
 main = do
      putStrLn $ "total: " ++ show resultat
en memòria global[modifica | modifica el codi]
  • fem operar la mònada ST en memòria global (RealWorld), especificant el tipus de la mònada com a (ST RealWorld tipusDelResultat), i stToIO passa el resultat a tipus (IO tipusDelResultat)
 -- igual que a l'exemple anterior amb els següents canvis:
 
 foldlST :: (a -> b -> a) -> a -> [b] -> ST RealWorld a
 
 -- implementació igual que l'anterior
 
 main = do
    resultat <- stToIO $ foldlST (+) 0 [1,2,3,4]
    putStrLn $ "total: " ++ show resultat

accés sincronitzat amb variables MVar (Mutable variable)[modifica | modifica el codi]

Vegeu-ho a Haskell concurrent

vectors mudables[modifica | modifica el codi]

Vegeu vectors d'elements d'allotjament directe

Fletxes (seqüenciació per encadenament de morfismes)[modifica | modifica el codi]

Generalitza l'encadenament amb funció (>>=) de les mònades prenent les funcions com a morfismes afegint al tipus, a més del del resultat, el de l'entrada. [105][106] (El nom de fletxa s'associa amb la representació dels morfismes).

La composició de fletxes ha de mantenir l'operació generadora (arr) i el tractament de parells.[106]

arr (f >>> g) = arr f >>> arr g          -- per a les funcions f i g
 
first (f >>> g) = first f >>> first g    -- per a les fletxes f i g

El tipus de la fletxa incorpora com a paràmetres el tipus del domini d'origen i el de destí. Les fletxes es poden compondre resultant una expressió de tipus fletxa.

Exemples: Ent./Sort. amb fletxes de Kleisli i l'tractament de XML amb fletxes sobre subarbres XML.[107]

Compiladors que suporten Fletxes: GHC,[108] Hugs [109]

Functors Aplicatius (seqüenciació sense encadenar resultats)[modifica | modifica el codi]

Permeten combinar resultats, sense passar-se'ls entre computacions.

Vegeu Mònada (programació funcional)#Seqüències de computacions sense lligar resultats - Functors aplicatius

Gestió de recursos a l'Entrada/Sortida - Els Iterats (ang:Iteratees)[modifica | modifica el codi]

Vegeu ref.[110] Un error comú amb l'Ent./Sortida tardana és el següent

-- imprimeix nombreDeLínies
incorrecte = do
    dades <- withFile "test.txt" ReadMode hGetContents
    putStrLn $ show $ length $ lines dades

El fitxer es tanca abans que putStrLn n'obtingui les dades. El resultat és la sortida buida. El compilador no avisa de l'error !!

La versió correcta és ficar el consumidor dins el withFile.

correcte = do
    nombreDeLínies <- withFile "test.txt" ReadMode $ \hd -> do
        dades <- hGetContents hd
        return $ length $ lines dades
    putStrLn $ show nombreDeLínies

L'abstracció dels Iterats descompon l'entrada sortida en productors del corrent de dades, consumidors i transformadors.[110]

  • Els Iterats es descriuen com una abstracció componible per al procés incremental d'una seqüència de parts de l'entrada de dades per l'obtenció d'un resultat. A cada pas es llegeix un element (o bé un troç de fitxer (ang:chunk)), s'actualitza l'estat i es combina l'element (o bé el troç de fitxer), i quan la seqüència s'acaba s'ofereix el resultat.
  • L'Enumerador és l'abstracció que genera el corrent de dades per al consum dels Iterats.
  • Els Enumerats (ang: Enumeratee) actuen com a consumidors i productors alhora i fan de transformadors interposats.

Algunes implementacions inclouen una capa de gestió de recursos (ex. el transformador de mònades ResourceT[111] als conduits[112]) que en ésser cridat (runResourceT), executa les accions incrementals i a continuació les accions d'alliberament prèviament registrades.

import Data.Conduit
import Data.Conduit.Binary as CB
import Data.Conduit.List as Cl
import Data.Conduit.Text as Ct
 
main :: IO ()
main = do
    let canonada = CB.sourceFile "test.txt"           -- productor
                   $$ Ct.decode Ct.utf8               -- transformador
                   =$ Ct.lines                        -- transformador
                   =$ Cl.fold (\x _ -> x +1) (0::Int) -- consumidor
 
    nombreDeLínies <- runResourceT $ canonada
    putStrLn $ show nombreDeLínies
Nota el sufix -ee[113]
En anglès, donat un verb afegint-li el sufix -er tenim un nom subjecte d'una acció com ara del verb employ el substantiu Employer.
El sufix -ee obté de l'acció un objecte passiu, així el parell (Employer, Employee) es podria traduir per (Empleador, Empleat).
Així els termes tècnics {Iteratee, Enumeratee} en català serien {Iterat, Enumerat}.

Mòduls[modifica | modifica el codi]

encapsulament[modifica | modifica el codi]

No hi ha doble especificació (interfície / implementació) per als mòduls. S'exporten els identificadors llistats a la clausula module, o tots si no hi ha llista.

module A(func1, tipus2, classe3, module B) where cos_del_mòdul

tipus opacs[modifica | modifica el codi]

L'elipsi (..) al costat d'un identificador de tipus exportat, exporta tots els identificadors interns com ara constructors o camps d'un registre o símbols d'enumeracions, o les operacions d'una classe.

  • Es pot substituir l'el·lipsi per la llista dels que volem exportar.
  • Si no els exportem, el tipus serà opac (només se'n podran manipular els valors per les operacions del seu tipus).
module A(
  func1,
  tipus2(..),                                       -- ho exporta tot
  tipus3,        -- tipus opac (no exporta constructors
                 --    ni accessors als camps en cas de ser registre)
  unAltreTipus( constructorA, constructorB),        -- només els esmentats
  classe4(..),                                      -- classe transparent
  unaAltraClasse( mètodeA, mètodeB),        -- classe que fa privats els mètodes que no publica
  module X                      -- reexporta aquells mòduls importats anomenats o reanomenats X
  ) where

import MòdulQ as X    -- el mateix àlies permet reexportar
                      --    el contingut de diversos mòduls de cop
import MòdulR as X
import MòdulS as X
...

importació[modifica | modifica el codi]

 import ModulA hiding (id1, id2) -- ''hiding'': importa els exportats, excepte els mencionats
 import qualified ModulB as B  -- ''qualified'': caldrà qualificar els símbols: B.ident
 import ModulB as B [hiding (...] -- sense ''qualified'': qualificació opcional
 import ModulD (id1, id2, id3)  -- enumeració: importa només els especificats
importació d'instàncies (implementacions de classes)
 import ModulA () -- llista buida, per importar només les instàncies
  • les instàncies visibles d'un mòdul s'exporten sempre; qualsevol importació del mòdul les importa totes.[114]
importacions especificant el paquet desitjat
Cal esmentar la pragma {-# LANGUAGE PackageImports #-}
import "nom_del_paquet" Nom_del_mòdul
-- com ara:
import "network" Network.Socket
import qualified "network" Network.Socket as NS
evitar la importació automàtica de les funcions predefinides (mòdul Prelude)
Cal esmentar la pragma {-# LANGUAGE NoImplicitPrelude #-}

Exemple:

-- Per a l'exemple que substitueix l'entrada/sortida de tipus String per la de tipus Text
module ElMeuPrelude (
  module PreludeMancat,
  putStr, putStrLn, getLine
) where
 
import Prelude as PreludeMancat hiding (putStr, putStrLn, getLine)
import Data.Text.IO (putStr, putStrLn, getLine)

ús:

-- no importis el Prelude que ja tinc el meu.
{-# LANGUAGE NoImplicitPrelude #-}
module ... where
import ElMeuPrelude
...

espai de noms de mòdul jeràrquic[modifica | modifica el codi]

El mòdul per nom "A.B.C" correspon al fitxer A / B / C.{hs,lhs,x,..}. En compilar, el senyal -i<dirs> indica a GHC la llista de directoris base de la recerca dels fonts[115]

És corrent reanomenar els noms jeràrquics amb un nom curt

 import qualified Control.Monad.ST.Strict as ST

mòduls amb referències mútues (mútuament recursius)[modifica | modifica el codi]

Vegeu-ho a Compilador Haskell de Glasgow#Mòduls amb referències mútues (mútuament recursius)

arrencada[modifica | modifica el codi]

El mòdul principal ha de dur per nom de mòdul "Main" i ha d'exportar la funció "main". Si no hi ha clàusula module es pren per defecte "module Main where", exportant tots els identificadors (útil per a proves a l'intèrpret).

Exemple senzill. (System.Environment no té en compte la codif. local, vegeu comentari):

-- per una lectura correcta (dels caràcters no anglosaxons) de la línia d'ordres a Linux
--  cal esmentar "System.Environment.UTF8" del paquet utf8-string
--  , en lloc de "System.Environment"
 
import System.Environment (getProgName, getArgs)   -- a Linux System.Environment.UTF8
import System.Exit (exitSuccess, exitWith, ExitCode(..))
import Text.Printf (printf)
 
main = do
         args <- getArgs
         nomProg <- getProgName
         case args of
            [] -> do
                    printf "%s: cal especificar un paràmetre.\n" nomProg
                    exitWith (ExitFailure 1)
 
            (arg:_) -> do
                         printf "el primer param. és: %s\n" arg
                         exitSuccess

En l'execució, a més dels paràmetres del programa, s'hi poden afegir paràmetres per al Run Time System a partir del separador +RTS tancant opcionalment amb -RTS[116] en cas d'haver d'afegir, tot seguit, paràmetres pel programa

arrencada amb opcions[modifica | modifica el codi]

Per afegir opcions a la línia d'ordres vegeu l'enllaç.[117][118][119]

import System.Environment (getProgName, getArgs)   -- a Linux System.Environment.UTF8
import System.Exit (exitSuccess, exitWith, ExitCode(..))
import System.Console.GetOpt
 
import Data.Maybe( fromMaybe )
import Text.Printf (printf)
 
-- tipus per a les opcions de línia de comanda (anomenades també senyals, en ang.: ''flags'')
data Flag = FVersio | FEntrada String | FSortida String | FArgLliure String
              deriving (Eq, Show)
 
opcions :: [OptDescr Flag]
opcions = [
          -- opció curta, opció llarga, descriptor, missatge d'ajuda
          -- amb paràmetres obligatoris (ReqArg), opcionals (OptArg), o sense (NoArg)
  Option ['V'] ["versió"]  (NoArg FVersio) "mostra versió",
  Option ['i'] ["entrada"] (ReqArg FEntrada "FILE") "entrada requereix un nom de fitxer",
  Option ['o'] ["sortida"] (OptArg creaFlagSortidaAmbValorPerOmissió "FILE") "sortida"
 ]
 
-- fromMaybe valorPerOmissió valorOpcional
--     retorna el contingut de l'opcional o, en cas de Nothing, el valorPerOmissió
 
creaFlagSortidaAmbValorPerOmissió :: Maybe String -> Flag
creaFlagSortidaAmbValorPerOmissió ms = FSortida ( fromMaybe "sortida_per_defecte" ms )
 
-- embolcalla els arg. lliures com a ''flags'' per un tractament unificat
argQueNoEsOpció :: String -> Flag
argQueNoEsOpció arg = FArgLliure arg
 
msg_com = "Ús: nom_prog [OPCIÓ ...]"
 
processa flags = do
                  print flags
                  exitSuccess
 
error_als_args nom_prog msgs_error = do
    printf "%s error als paràmetres\n" nom_prog
    putStrLn $ concat msgs_error ++ usageInfo msg_com opcions
    exitWith $ ExitFailure 1  -- sortir amb el codi de finalització
 
main = do
 nom_prog <- getProgName
 args <- getArgs
 case getOpt (ReturnInOrder argQueNoEsOpció) opcions args of
   -- cas de manca d'errors (llista buida al 3er de la tupla)
   (flagsCorrectes, [],   [])  -> processa flagsCorrectes
   -- altrament
   (_,   _,    msgs_error)  -> error_als_args nom_prog msgs_error

funcions i tipus predefinits i biblioteques bàsiques[modifica | modifica el codi]

El mòdul de funcions predefinides es coneix com a "Prelude". Vegeu Prelude estàndard.[120] Prelude del GHC.[121]

Les API's de les biblioteques bàsiques són aquí[122]

Podeu consultar a quins mòduls pot pertànyer un identificador mitjançant el cercador de l'API del Haskell Hayoo

Precisió dels tipus bàsics[modifica | modifica el codi]

Vegeu-ho a GHC#Precisió dels tipus bàsics

Vectors[modifica | modifica el codi]

Per interfície[modifica | modifica el codi]

Vectors immutables
interfície (classe) IArray[123] Vegeu #Vectors immutables del H98
Vectors mudables
interfície (classe) MArray[124] Més eficients que els immutables, exemple a #Vectors mudables de dades no encapsulades
Vectors de tractament paral·lel (paral·lelisme de dades)
interfície (classe) PArray. (cada op. conclou havent avaluat tots els elements) Només a GHC.[125]
Vectors amb allotjament contigu, per a ésser accedits des de mòduls forans (ex.lleng. C)
interfície Storable.[126]
Vectors pluridimensionals
a GHC amb el paquet REPA (Regular Parallel Arrays)
Immutables, per implementació i allotjament dels elements[modifica | modifica el codi]
amb actualització de còpia completa[modifica | modifica el codi]

Ineficients en espai.

data Ix idx => Array idx elem  -- Elements allotjats indirectament (encapsulats, avaluació tardana)[127]
data Ix i => UArray i e -- Elements allotjats directament, (no encapsulats Unboxed, avaluació estricta)[128]
-- exemple
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (listArray)
 
vec :: UArray Int Float  -- vector de Floats amb índex de tipus Int
vec = listArray (0,2) [0.5, 1.5, 2.5]
 
-- (//) actualització amb una llista de parells (índex, nou_valor)
nou_v = vec // [(indx, nou_valor)]   -- còpia completa
amb actualització per aplicació de diferències. DiffArrays[modifica | modifica el codi]
  • Vegeu DiffArrays.[129] s'afavoreix l'accés en temps constant a la versió més recent; els valors de referències antigues s'obtenen per aplicació de les diferències i per tant l'accés és més lent com més antic, és a dir, més vegades s'hagi modificat.[130]
import Data.Array.Diff (DiffUArray)
import Data.Array.IArray (listArray)
 
vec :: DiffUArray Int Float
vec = listArray (0,2) [0.5, 1.5, 2.5]
 
-- l'accés a ''nou_v'' serà més ràpid que al seu antecessor ''vec''
nou_v = vec // [(indx, nou_valor)]
Mudables, per implementació i allotjament[modifica | modifica el codi]
  • Vectors d'accés directe en memòria global, a la mònada IO.[131]
data IOArray i e   -- amb allotjament indirecte dels elements (encapsulats, avaluació tardana)
data IOUArray i e  -- amb allotjament directe dels elements (no-encapsulats: Unboxed, avaluació estricta)
  • Vectors d'accés directe en memòria local, a la mònada ST.[132]
data STArray s i e  -- amb allotjament indirecte dels elements (encapsulats)
data STUArray s i e -- amb allotjament directe dels elements (no-encapsulats)

Exemples a #Vectors mudables de dades no encapsulades.

Vectors immutables del H98[modifica | modifica el codi]

La classe Array combinada amb la Ix (índexs), especificades en el Haskell98, implementen vectors immutables amb elements d'avaluació tardana.[133]

 -- vector d'una dimensió, s'inicialitza amb
 --      les dimensions i una llista exhaustiva de parells (índex, valor)
 import Array
 vec = array (menor, major) [ (i, expr_valor) | i <-[ menor..major ]]
 element = vec!indx     -- (!) valor per a l'índex indx
 
 -- (//) actualització amb una llista de parells (índex, nou_valor)
 nou_v = vec // [(indx, nou_valor)]
 
 -- vector de dues dimensions
 w = array ((menor_i1, menor_i2), (major_i1, major_i2)) [( (i, j), expr_valor) |
        i <- [menor_i1..major_i1], j <- [menor_i2..major_i2]]
  • per fer un vector amb índex de tipus enumerat cal que l'enumerat implementi la classe dels índexs Ix (A GHC: biblio. Data.Ix)
 class Ord a => Ix a where
  range :: (a, a) -> [a]
  index :: (a, a) -> a -> Int
  inRange :: (a, a) -> a -> Bool
  rangeSize :: (a, a) -> Int
 import Array
 import Text.Printf
 data DiaFeiner = Dl | Dm | Dc | Dj | Dv 
          deriving (Show, Eq, Ord, Enum, Ix) -- el compilador instancia Ix
 encàrrecs :: Array DiaFeiner Int
 encàrrecs = array (Dl,Dg) [(Dl,5),(Dm,6),(Dc,3),(Dj,7),(Dv,8)]
 main = do
     printf "Per dimecres tinc %d encàrrecs\n" (encàrrecs!Dc)
     putStr "De Dilluns a Divendres: "
               -- aplicació parcial de l'operació (!)
     putStrLn $ show $ map (encàrrecs!) $ range (Dl,Dv)

A part d'aquests vectors, que generen una còpia a cada actualització, n'hi ha d'altres de força més eficients.[134][135] Entre aquests vegeu més avall #Vectors mudables de dades no encapsulades.

Vectors mudables de dades no encapsulades[modifica | modifica el codi]

Amb allotjament directe (no encapsulat) dels elements.

Als vectors d'elements no encapsulats (Unboxed) no cal passar-los els elements amb el sufix #

En memòria local[modifica | modifica el codi]
  • En memòria local amb la mònada (ST s tipus_del_vector) i tipus del vector: (STUArray s tipus_de_l_índex tipus_de_l_element).
el tipus de l'índex ha d'implementar la classe Ix igual que els vectors de la def. del 98.
import Control.Monad.ST
import Data.Array.ST
 
obtenirParell :: ST s (Int, Int)    -- a la mònada (ST s a) (param. ''s'' lliure === allotjament local)
obtenirParell = do
     -- vector de sencers unboxed STUArray amb índex sencer (1 a 10) inicialitzat a 37
 
     -- indiquem el tipus de ''newArray '' per indicar el tipus del vector STUArray
     --    (si volguéssim vectors d'elements d'allotjament indirecte i avaluació tardana utilitzaríem STArray)
      -- i perquè els literals 1,10,37, són de tipus indeterminat (vegeu secció "literals" més amunt)
      -- i cal concretar-ne el tipus.
 
     arr <- newArray (1,10) 37 :: ST s (STUArray s Int Int) -- :: entre parèntesis el contingut de la mònada
     a <- readArray arr 1
     writeArray arr 1 64
     b <- readArray arr 1
     return (a,b)
 
main = do
     let x = runST obtenirParell
     print x
En memòria global[modifica | modifica el codi]
  • En memòria global amb la mònada IO i tipus del vector: (IOUArray tipus_de_l_índex tipus_de_l_element)
import Data.Array.IO
 
obtenirParell :: IO (Int, Int)
obtenirParell = do
     -- vector de sencers unboxed IOUArray amb índex sencer (1 a 10) inicialitzat a 37
 
     -- indiquem el tipus de ''newArray '' per indicar el tipus del vector IOUArray
     --    (si volguéssim vectors d'elements d'allotjament indirecte i avaluació tardana utilitzaríem IOArray)
 
        arr <- newArray (1,10) 37 :: IO (IOUArray Int Int)
        a <- readArray arr 1
        writeArray arr 1 64
        b <- readArray arr 1
        return (a,b)
 
main = obtenirParell >>= print

Estructures TAD genèriques[modifica | modifica el codi]

L'equivalent dels mòduls genèrics d'altres llenguatges (paramètrics en tipus), es concreta afegint variables de tipus a les classes com a paràmetres o bé com a tipus associats (famílies de tipus).

Els paràmetres de tipus que depenen d'altres paràmetres es poden expressar mitjançant

  • o bé amb dependències funcionals
  • o bé amb famílies de tipus associades a la classe, com a funció dels paràmetres de tipus de la mateixa, amb instruccions type (sinònims) o data, quin valor de tipus cal definir en instanciar la classe.

Amb classes multiparàmetre i dependències funcionals[modifica | modifica el codi]

A les classes multiparàmetre pot ser que una part dels paràmetres vingui determinada per altres (per exemple el tipus de la col·lecció determina el tipus de l'element).[136]

La sintaxi de les dependències funcionals és:

| tip_col -> tip_elem , ..

indicant que una instància del tipus tip_col determina una única instància del tipus tip_elem.

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
 
class Col_leccio tip_col tip_elem | tip_col -> tip_elem where
 nova_col :: tip_col
 inserta :: tip_elem -> tip_col -> tip_col
 és_membre :: tip_elem -> tip_col -> Bool
 
-- per a tip_col = [tip_elem]
 
instance Eq tip_elem => Col_leccio [tip_elem] tip_elem where
 nova_col     = []
 inserta elt xs = elt : xs
 és_membre elt []  = False
 és_membre elt (x:xs)
  | elt == x  = True
  | otherwise  = és_membre elt xs
 
main = do
     let v1 = inserta 1 $ inserta 2 $ nova_col::[Int]
     print $ és_membre 2 v1
     print $ és_membre 5 v1
--

"Àlies de Tipus" associats a la classe[modifica | modifica el codi]

Nou. A partir de GHC 6.10.1. es pot reconvertir els paràmetres dependents en #famílies de tipus associades a la classe, (indexades pels paràmetres-de-la-classe), mitjançant "àlies de tipus" (clàusula type), solucionant els problemes d'ambigüitat de les dependències funcionals.

GHC admet declaracions type i data abstractes a les classes, concretables a la instanciació de les classes. Vegeu presentació[137] i tipus associats a Famílies de tipus(Tipus indexats per tipus)[138]

De l'exemple anterior, el tipus tip_elem determinat funcionalment per tip_col, passa a especificar-se type Element tip_col.

{-# LANGUAGE TypeFamilies #-}
 
class Col_leccio tip_col where
  type Element tip_col   -- ''àlies de tipus'' associat, amb param. ''tip_col''
              -- llegit "Element de tip_col" o Element funció de tip_col
              -- és abstracte (sense def. "= ...") ; pendent de concretar a la instància.
  nova_col :: tip_col
  inserta :: Element tip_col -> tip_col -> tip_col
  és_membre  :: Element tip_col -> tip_col -> Bool
 
-- per a tip_col = [tip_elem]
 
instance Eq tip_elem => Col_leccio [tip_elem] where
  type Element [tip_elem] = tip_elem     -- instància de (Element tip_col)
 
  nova_col = []
  inserta elt xs = elt : xs
  és_membre elt []  = False
  és_membre elt (x:xs)
    | elt == x  = True
    | otherwise  = és_membre elt xs
 
-- definició de l'op. ''pipeline'' de l'F#
(.$) = flip ($)
 
main = do
  let v1 = (nova_col :: [Float])
           .$ inserta 2
           .$ inserta 1.5
 
  print $ és_membre 2 v1
  print $ és_membre 5 v1
  --

Imitant els functors de ML[modifica | modifica el codi]

Vegeu "Mòduls de primera al Haskell".[139] i Functors a OCaml

  • Amb classes i tipus associats i canviant l'agregació de llista a vector:
 cabal install vector   -- instal·lar paquet ''vector'' per a l'exemple
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}
 
import Data.Vector as Vector
 
class Set tip_conjunt where       -- signatura del model de Conjunt
  type Element tip_conjunt        -- Element és funció de tipconjunt
 
  buit :: tip_conjunt
  afegeix :: Element tip_conjunt -> tip_conjunt -> tip_conjunt
  enLlista :: tip_conjunt -> [Element tip_conjunt]
 
-- functor ConjuntVectorAmbIgualtat
--   la var. del genèric és tip_elemt
--   els requeriments del tipus de la var. del genèric s'expliciten en el ''context =>''
--      i queda restringit existencialment (aquells tip_elemt tals que ''Eq tip_elemt'')
 
instance Eq tip_elemt => Set (Vector tip_elemt) where   -- la var. del genèric és tip_elemt
                           -- el tipus de la var és "aquells tip_elemt tals que Eq tip_elemt"
 
  type Element (Vector tip_elemt) = tip_elemt       -- obtenció de tip_elemt
  buit = Vector.empty
  afegeix = \x xs -> Vector.cons x $ Vector.filter (/= x) xs
  enLlista = Vector.toList
 
-- aplicació del functor al tipus ''Int'' mitjançant una restricció de tipus,
--  substituint la variable existencial ''tip_elemt'' del genèric
--  pel tipus concret Int com a subtipus (compleix els requeriments (def. d'igualtat) de ''tip_elemt'')
 
conj_buit = buit :: Vector Int       -- ConjuntVectorDeSencers
 
ok = enLlista $ afegeix 1 conj_buit
 
--------------------------------------------
-- Ampliació amb noves funcions
 
class (Set tip_conjunt) => SetHelp tip_conjunt where     -- signatura noves funcions
  type ElementBis tip_conjunt
 
  afegeixLlista :: tip_conjunt -> [ElementBis tip_conjunt] -> tip_conjunt
 
instance Eq tip_elemt => SetHelp (Vector tip_elemt) where  -- estructura complementària
  type ElementBis (Vector tip_elemt) = tip_elemt
 
  afegeixLlista conjunt llista = Vector.foldr afegeix conjunt (Vector.fromList llista)
 
ok2 = enLlista $ afegeixLlista conj_buit [1,2]
 
main = do
        print ok
        print ok2

Temes avançats[modifica | modifica el codi]

Monomorfisme, polimorfisme i quantificadors explícits[modifica | modifica el codi]

Vegeu ref.[140]

polimorfisme universal[modifica | modifica el codi]

El polimorfisme per defecte dels paràmetres de funcions és l' universal.[141] El nombre de nivells de quantificació universal en una funció s'anomena ordre del tipus de la funció (ang: type rank). Vegeu Rank-N types (tipus d'ordre N).[142]

 -- declaracions idèntiques -- per a tot tipus ''a''
 id :: a -> a     -- (tipus de 1er ordre)
 id :: forall a . a -> a   -- amb quantificador universal explícit. (tipus de 1er ordre)
 
 -- forall explícit als paràmetres
 foo :: (forall a. a -> a) -> (Char,Bool)  -- funció amb tipus de 2on ordre (ang: rank-2 type)

Vegeu també

  • Rank-2 types, ($) and the Monomorphism restriction[31]
  • Extensió {-# LANGUAGE NoMonomorphismRestriction #-}[143]
  • Des de GHC 7.2 Monomorfisme als lligams locals (let|where), excepte que s'expliciti la generalització.[144]
polimorfisme existencial[modifica | modifica el codi]

Els tipus dels paràmetres formals, condicionats per un context, defineixen el conjunt de tots aquells tipus que implementin, entre d'altres, les classes que el context requereix al paràmetre de tipus.

 funció_sobre_tipus_ordenable_i_acotat :: (Ord a, Bounded a) => a -> a

Podríem formular el paràmetre formal de manera existencial definint el conjunt de tipus acceptat:

{-# LANGUAGE RankNTypes #-}
import Control.Exception (ArithException(Overflow), throw, evaluate)
 
incr :: forall a. (Num a, Ord a, Bounded a) => a -> a
incr x = if xIncrementat < x then throw Overflow
                             else xIncrementat
      where xIncrementat = x + 1
 
-- incrementa el més gran del tipus, per fer saltar l'excepció
main = do
         res <- evaluate $ incr (maxBound::Int)
         print res


Tipus de Dades Algebraics Generalitzats (GADTs)[modifica | modifica el codi]

Admeten diversificar el tipus de dades retornat. Vegeu [145] Cal esmentar l'opció de compilació -XGADTs o la pragma equivalent {-# LANGUAGE GADTs #-}

  • El guany és el refinament de tipus en fer l'encaix de patrons.
{-# LANGUAGE GADTs #-}
 
data Terme a where        -- amb GADTs, els constructors no cal
                          --      que tornin tots el tipus definit
  Literal  :: Int -> Terme Int               -- tipus (Terme Int)
  Successor :: Terme Int -> Terme Int            -- tipus (Terme Int)
  EsZero   :: Terme Int -> Terme Bool           -- tipus (Terme Bool)
  SiCondició :: Terme Bool -> Terme a -> Terme a -> Terme a -- tipus (Terme a)
  Parell   :: Terme a -> Terme b -> Terme (a,b)      -- tipus (Terme (a,b))
 
avalua :: Terme a -> a -> a
avalua (Literal i) j = i+j  -- l'operació + és permesa per què es dedueix
                            -- que ''Literal i'' és de tipus (''Terme Int'')
                            -- i per tant, en aquest encaix, ''a'' és Int
                            -- ja no caldrà declarar que el tipus de ''a''
                            --    ha d'implementar la suma: (Num a) => ...

newtype: tipus derivats[modifica | modifica el codi]

newtype permet fer abstracció d'un tipus preexistent mitjançant un nou tipus, sobre el qual podem definir noves operacions, o publicar-ne només una part (vegeu subsecció).[146]

newtype defineix la nova abstracció mitjançant una estructura d'un sol component, en forma de registre. De fet els registres funcionen com els tipus producte posicionals amb els noms de camps com a funcions d'accés.

Queda definida una relació entre ambdós tipus. El constructor del registre constitueix una aplicació del domini del tipus del component al nou, i la funció accessora (nom del camp) constitueix l'aplicació inversa com es mostra a l'exemple.

  • Caldrà però re-instanciar-ne les classes que el compilador no pugui derivar automàticament (clàusula deriving) (H98: especifica la derivació de Eq, Ord, Enum i Bounded). GHC permet ampliar aquesta llista com s'esmenta a la propera secció.
-- Sensor acotat
 
import Control.Exception (throw, ArithException( Overflow, Underflow))
 
newtype TSensor = Sensor {int_Des_De_Sensor :: Int} deriving (Eq, Ord, Show)
 
  -- un registre d'un sol camp defineix dues operacions de conversió de tipus inverses l'una de l'altra
  --    Sensor         :: Int -> TSensor    -- ''constructor''
  -- int_Des_De_Sensor :: TSensor -> Int    -- ''inversa del constructor''
 
-- Acotació
instance Bounded TSensor where
  minBound = Sensor (-30)
  maxBound = Sensor 50
 
sumaAcotada :: TSensor -> TSensor -> TSensor
 
sumaAcotada x y | resultat > maxBound = throw Overflow
                | resultat < minBound = throw Underflow
                | otherwise = resultat
 where
    resultat = Sensor (i + j)
    i = int_Des_De_Sensor x
    j = int_Des_De_Sensor y
 
valor = Sensor (-25) `sumaAcotada` Sensor (-10)
main = print valor
etiquetar tipus bàsics[modifica | modifica el codi]
  • newtype també pot servir per redefinir un tipus publicant (clàusula deriving) només una part de les operacions de l'original mitjançant les classes respectives:

GHC permet derivar automàticament les instàncies de més classes que el H98 (no totes) del tipus original al nou, per no haver de redefinir-les, especificant-les a la clàusula deriving, sempre que haguem esmentat l'extensió {-# LANGUAGE GeneralizedNewtypeDeriving #-}[147]

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
-- fent que el compilador discrimini diferents elements que es poden comptar
 
newtype TPomes = Pomes Int deriving (Eq, Show, Ord, Num)
newtype TPeres = Peres Int deriving (Eq, Show, Ord, Num)
 
valor = Pomes 2 + Peres 3   -- sumem TPomes i TPeres, error de tipus !!

dóna l'error:

  Couldn't match expected type `TPomes'
      against inferred type `TPeres'

Vegeu també

  • "Keyword arguments in Haskell"[148] (cat: "pas de paràmetres etiquetat per evitar errors de transposició de paràmetres")
  • DrIFT[149] Preprocessador de Haskell que automatitza la instanciació de classes no suportades pels compiladors a la clàusula "Deriving"
  • Data.Derive[150] Alternativa a DrIFT amb alguns avantatges. Manual.[151]
tipus derivats de funcions[modifica | modifica el codi]

També s'utilitza per definir un tipus com a abstracció d'un tipus de funció. Això permet definir operacions de combinació de funcions del tipus específic, com a les Fletxes

 -- sigui, per exemple, f :: a -> [b]  -- el tipus de funció sobre el que volem definir operacions
 --           x :: a
 
 newtype TFiltre a b = Filtre { runFiltre :: a -> [b] }
 
 --  Filtre  :: (a -> [b]) -> TFiltre   -- ''constructor''
 --  runFiltre :: TFiltre -> (a -> [b])   -- ''inversa_del_constructor''
 
 filtre_f = Filtre f       -- element del nou tipus aplicant el constructor a la funció
 
 runFiltre filtre_f       -- retorna la funció f original, aplicant la ''inversa_del_constructor''
 
 runFiltre filtre_f x      -- aplica la funció original a x

kind[modifica | modifica el codi]

És una mena de tipus per als constructors de tipus, o, dit d'altra manera, un tipus d'ordre superior.[152]

Defineix un conjunt de tipus,

  • o bé amb una màscara que descriu l'aritat del tipus, i la dels components de tipus d'ordre superior (ex.: (* -> *) -> * -> *),
El Kind té dos constructors '*' i (->)
expressió de tipus kind comentari
(Int -> Double) -> Int -> Double (* -> *) -> * -> * quan un dels paràmetres és una funció (ordre superior)
Int * tipus d'aritat 0 (no és funció de cap paràmetre de tipus)
Maybe * -> * constructor de tipus que és funció d'un component (Maybe a) (funció de tipus d'aritat 1)
Maybe Bool * aplicant el constructor a un tipus, tenim un tipus d'aritat 0

Són d'aplicació a les #Famílies de tipus, l'especificació de kind pot indicar l'aritat de la família i, en conseqüència, els paràmetres addicionals que requereixen les instàncies.

Vegeu també "GHC - Quantificació de kind explícita".[153] Requereix extensió {-# LANGUAGE KindSignatures #-}

sort

Els Kind tenen una categoria que els classifica (tipus a nivell de kind) que s'anomena sort. Només hi ha un valor de sort que és BOX.[154]

  • Promoció de tipus: GHC facilita automàticament la promoció de tipus a Kinds i de constructors a constructors de Kind, amb l'extensió DataKinds.[154]
data Nat = Zero | Succ Nat
 
-- és promogut a:
 
Nat :: BOX
Zero :: Nat
Succ :: Nat -> Nat
{-# LANGUAGE DataKinds, PolyKinds, KindSignatures, GADTs, TypeOperators #-}
data Nat = Zero | Succ Nat
 
data Vec :: * -> Nat -> * where
  Nil  :: Vec a Zero
  Cons :: a -> Vec a n -> Vec a (Succ n)
 
data HList :: [*] -> * where
  HNil  :: HList '[]
  HCons :: a -> HList t -> HList (a ': t)
 
data Tuple :: (*,*) -> * where
  Tuple :: a -> b -> Tuple '(a,b)
kind Constraint[modifica | modifica el codi]

kind que defineix el conjunt de tipus per les restriccions de context que han de complir.

Hi ha 3 menes de restriccions:[155]

  • implementació de classe, com a (Show a)
  • paràmetres implícits, com a (?x::Int) -- cal l'extensió ImplicitParams
  • requeriment d'igualtat, com a (a ~ T b)[156] -- amb l'extensió TypeFamilies o GADTs

A partir de GHC 7.4, un conjunt de restriccions adopta la categoria de kind Constraint i se li pot assignar un àlies.[157]

(Show a, Read a) :: Constraint
(?x::Int) :: Constraint
(a ~ Int) :: Constraint
 
-- els podem assignar un àlies
 
type Textualitzable a = (Show a, Read a)
 
viaString :: Textualitzable a => a -> a
viaString = read . show

Famílies de tipus[modifica | modifica el codi]

Són una generalització dels tipus associats a les classes,[158] i permeten una diversificació de l'estructura en instàncies per tipus de l'índex, així com les classes admeten una diversificació del comportament també en instàncies per tipus.

Designen un conjunt de tipus mitjançant un constructor de la família, i una variable anomenada l'índex i, opcionalment, una especificació de #kind, l'ordre del tipus.

Vegeu ref.[159] Requereix l'extensió de llenguatge TypeFamilies.

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
 
-- família de llistes
data family XList t
 
-- instància de llista XList per al tipus Char
data instance XList Char = XCons Char (XList Char) | XNil
                           deriving (Eq, Show)
 
-- instància de llista XList per al tipus () -- Unit (buit)
  -- com que el tipus () té un únic valor, estalviem recursos
  --    especificant directa i únicament la llargada de la llista
data instance XList () = XListUnit Int
                         deriving (Eq, Show)
 
-- -------------- ús, requereix extensió FlexibleInstances
 
class TéLlargada t where
  llargada :: t -> Int
 
instance TéLlargada (XList ()) where
  llargada (XListUnit len) = len
 
instance TéLlargada (XList Char) where
  llargada XNil = 0
  llargada (XCons x xs) = 1 + llargada xs
 
laMevaLlistaDeBuits = XListUnit 10
 
main = print $ llargada laMevaLlistaDeBuits

L'especificació de kind permet conèixer els paràmetres de tipus que cal afegir-hi:

-- família de diccionaris per tipus de clau
data family Dicc k :: * -> *
 
--      el ''kind'' :: * -> * indica funció de tipus d'aritat 1,
--      o sigui que la instància requereix un altre paràmetre de tipus, el de l'element
 
-- instància per a claus de tipus String
data instance Dicc String e = DiccStringNil | DiccStringCons (String, e) (Dicc String e)
 
-- instància per a claus de tipus Unit (un únic valor de clau
   -- i per tant un sol element possible)
data instance Dicc () e = DiccUnitNil | DiccUnitSimple e

Quan la família es defineix en una classe, la instància de la família es defineix ensems amb la instància de la classe, llavors la paraula reservada instance seria redundant i no s'hi posa.

{-# LANGUAGE TypeFamilies #-}
 
class Pila t where
  type Element t          -- tipus associat: família de tipus indexada al param. de la classe
  buida :: t
  apila :: Element t -> t -> t
  ésBuida :: t -> Bool
  daltIDesapila :: t -> (Element t, t)
 
instance Pila [a] where
  type Element [a] = a    -- instància de la família de tipus
  buida = []
  apila = (:)  -- cons
  ésBuida = null
  daltIDesapila [] = error "daltIDesapila: error llista buida!"
  daltIDesapila (x : resta) = (x, resta)

Noves extensions als tipus[modifica | modifica el codi]

Vegeu novetats de GHC 7.4.1[160]

tipus dependents de valors[modifica | modifica el codi]

Vegeu Glasgow_Haskell_Compiler#Tipus_dependents_de_valors

Afinant el rendiment[modifica | modifica el codi]

Modes d'avaluació d'expressions[modifica | modifica el codi]

Una expressió en HNF o WHNF possibilita diferir l'avaluació dels components no estrictes (sense prefix '!') de les estructures (data) que conté, mentre que en Forma Normal els components de les estructures estan completament avaluats.

Head Normal Form[modifica | modifica el codi]

La reducció a HNF d'una expressió deixa en suspens l'avaluació dels components de les dades.

Una expressió està en HNF si no té expressions beta-reduïbles Beta-Redexes en posició frontal (paràmetre actual substituïble en l'abstracció lambda).[161] Els constructors no són substituïbles com a paràmetres i aturen l'avaluació HNF.

seq avalua a HNF[162]

Weak Head Normal Form[modifica | modifica el codi]

La reducció a WHNF d'una expressió que conté funcions anònimes (lambda), deixa en suspens l'avaluació del cos de les lambdes,[163] a banda de deixar en suspens l'avaluació dels components de les dades.

Una expressió està en WHNF si està en HNF o és una abstracció lambda.[164]

Una expressió en WHNF està en una de les formes següents:[165]

  • Un Constructor, eventualment aplicat a expressions no reduïdes dels components
  • Una funció aplicada a menys arguments del que correspon a la seva aritat
  • Una abstracció lambda

evaluate de Control.Exception s'aplica en les computacions amb efectes col·laterals (tipus mònada) per forçar l'avaluació (a WHNF) d'una expressió.

Forma Normal[modifica | modifica el codi]

Una expressió està en forma normal si no s'hi poden aplicar més reduccions.[166]

La classe NFData del paquet deepseq[167] (a partir de GHC 7.4.1 forma part del conjunt bàsic de biblioteques de GHC) defineix la signatura d'una funció rnf (Reduce to Normal Form). Per crear una instància de NFData per a un tipus algebraic, caldrà una implementació de rnf que apliqui recursivament rnf als paràmetres corresponents als components dels constructors.

Avaluació estricta explícita[modifica | modifica el codi]

A part de la optimització en estrictesa dels compiladors (vegeu article "Haskell és un llenguatge estricte")[20] es pot forçar l'avaluació estricta de les següents maneres:

Aplicació estricta (Crida per valor)[modifica | modifica el codi]

A les expressions amb l'operador d'aplicació estricta ($!) com a alternativa de ($) d'avaluació tardana.

f $! x = x `seq` (f x)
-- (seq) avalua el primer operand (a HNF) i retorna el segon
Aplicació estricta a Forma Normal[modifica | modifica el codi]

($!!) amb dos signes '!' avalua l'operand amb deepseq a Forma Normal

import Control.DeepSeq
 
f $!! x = x `deepseq` (f x)    -- cal que el tipus de 'x' implementi la classe NFData
Estrictesa als components dels tipus de dades[modifica | modifica el codi]
data T = C !T1 !T2 ...

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ó (redex) corresponent al component es fa amb ($!) i si no hi ha el prefix '!' es fa amb ($).[168]

Vegeu Eficiència i rendiment en tipus de dades[169]

Estrictesa als paràmetres formals[modifica | modifica el codi]

Avaluació estricta als paràmetres formals amb l'extensió {-# LANGUAGE BangPatterns #}[170]

f !x !y = 2 * x * y         -- avalua estrictament els paràmetres actuals (a WHNF)!
Estrictesa als patrons d'encaix[modifica | modifica el codi]

Lligams amb avaluació estricta a les variables dels patrons

let !v = expr   ...      -- avalua estrictament l'expressió (amb (seq))
let (!x, !y) = (exprX, exprY)   -- avalua les expr. dels components de l'encaix
foldl': iteracions amb plegat per l'esquerra estricte[modifica | modifica el codi]

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 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ó)))
-- caldrà utilitzar ''deepseq'' (que avalua a ''forma normal'') en comptes de ''seq''

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.

Treballar a espai constant de la pila[modifica | modifica el codi]

El llibre RealWorldHaskell[171] esmenta que (seq) avalua a WHNF (redueix substituint tots els paràmetres aplicables de la funció més externa, però no redueix el cos de les lambdes (funcions anònimes)) i s'atura en els contructors (no en redueix les expressions dels components). Degut a això, per treballar a espai constant de la pila, si operem sobre una estructura de tipus algebraic (amb Constructors), caldria avaluar completament l'acumulador, o bé marcant estrictesa explícita als tipus dels components acumuladors (prefix !) o bé amb deepseq a #Forma Normal en comptes de seq.

-- o bé: (data T = C !T1 !T2 ...)   -- '!' als comp. del tipus de l'acumulador (les expr. seran avaluades amb ($!))
-- o bé: op !acc y = op' acc y    -- avaluació a HNF de l'acumulador
-- o bé: op acc y = acc `deepseq` op' acc y    -- avaluació a forma normal de l'acumulador (paquet ''deepseq'')
Estrictesa en les crides recursives[modifica | modifica el codi]

Vegeu "Recursivitat final al Haskell".[172]

  • La "recursivitat final mòdulo cons" (recursivitat diferida):[173] quan la crida recursiva és en l'expressió del component d'una dada que es retorna, no serà avaluada perquè els constructors de Tipus de dades algebraics aturen l'avaluació HNF.
  • En cas de recursivitat final normal, el compilador optimitza alguns casos per evitar generar una pila d'expressions pendents d'avaluar (thunks)[21] però cal assegurar-se'n. Vegeu "Limitations of strictness analysis".[174]

Programació de nivell baix[modifica | modifica el codi]

Tipus primitius que es passen per valor (no encapsulats, ang:unboxed)[modifica | modifica el codi]

Vegeu-ho a GHC

Tuples no encapsulades per al retorn de múltiples valors[modifica | modifica el codi]

Vegeu-ho a GHC

Cadenes de text implementades amb vectors[modifica | modifica el codi]

Més eficients en la lectura, i concatenació, menys en afegir caràcters per devant (cons).

  • amb vectors de caràcters de 16 bits (UTF16) definits a la biblioteca "text"[175]
#if __GLASGOW_HASKELL__>=612
 
 import System.IO (stdout, hFlush)
 
 -- amaguem les funcions predefinides per a String que volem Text (vector de UTF16)
 -- cal haver instal·lat el paquet "text"
 
 import Prelude hiding (putStr, putStrLn, getLine)
 import Data.Text.IO  (putStr, putStrLn, getLine)
 import Data.Text (pack, unpack,      -- conversió
          empty, singleton, cons, -- generadors
          uncons)
 
 text1 = pack "Entreu lletres tot seguit: "  -- pack :: String -> Text
 text2 = pack "comença per "
 
 main = do
     putStr text1
     hFlush stdout
 
     x <- getLine
         case uncons x of          -- uncons :: Text -> Maybe (Char, Text)
          Just (lletra, _resta) -> do
                         putStr text2
                         putStrLn $ singleton lletra  -- singleton :: Char -> Text
 
          Nothing -> putStrLn $ pack "entrada buida"
#else
  #error "no provat en versions anteriors"
#endif

compilant amb preprocés

runhaskell -cpp prova.hs

L'extensió OverloadedStrings permet fer servir literals de tipus String com a valors de tipus que n'implementin la conversió, instanciant la classe IsString.

Especialització d'operacions sobrecarregades[modifica | modifica el codi]

Vegeu-ho a GHC

Biblio ràpida de contenidors[modifica | modifica el codi]

A GHC.

Problemàtiques[modifica | modifica el codi]

El problema de la manca d'informació de situació en les petades[modifica | modifica el codi]

Quan una funció definida parcialment, crida la funció error, el programa peta oferint, escassament, el missatge de la funció error, sense més pistes. A partir de GHC 7.4.1 s'ha incorporat, només quan es compila amb opcions de perfilat, un sistema de pila de crides, amb bolcat en cas d'excepció.[176]

Exposició i alternatives a GHC.

El problema de les configuracions[modifica | modifica el codi]

L'accés, des del codi funcional, a variables globals carregades en temps d'execució es pot abordar des de diverses perspectives, cap d'elles plenament satisfactòria.[177]

  • Amb la mònada Reader[178] que proporciona accés a variables d'entorn d'un entorn canviant. Desavantage: no-accessible des del codi funcional, caldrà, llavors, passar-lo com a paràmetre.
    • Intent de solució mitjançant paràmetres implícits: Desavantatge: cal propagar el paràmetre implícit a totes les declaracions de tipus de les rutines intermèdies.
  • Accedint via unsafePerformIO, assegurant-se bé que l'escriptura de les variables ha estat avaluada ("the unsafePerformIO hack").[179] Desavantatge: hi ha el risc que les ops. no s'efectuïn en l'ordre correcte si no se'n controla bé l'avaluació.
  • Altres sistemes més sofisticats:
    • Segons Joachim-Breitner[180][181]
    • Segons Oleg Kiseliov i Chung-chieh Shan[182]

Una solució: El hack unsafePerformIO[modifica | modifica el codi]

hack, en informàtica, segons la viqui. anglesa vol dir "solució poc elegant però que permet sortir del pas",[183] aquí en diríem "un pedaç".

unsafePerformIO[184] és la porta del darrere de la mònada IO. Permet accedir a variables globals des del codi funcional, però l'ordre de les ops. dependrà del moment que se n'avaluï el thunk corresponent. (Vegeu "IO inside: The dark side of the IO Monad".)[185]

unsafePerformIO :: IO a -> a
  • per emprar amb seguretat unsafePerformIO en el codi funcional, cal que l'acció no-funcional mantingui la idempotència (no tingui efectes col·laterals i sigui independent de l'entorn).[184]
{-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}  -- recomanació de System.IO.Unsafe
import System.IO.Unsafe
import Data.IORef
 
-- variable de prova, a l'estil del llenguatge ML
{-# NOINLINE intRef #-} -- recomanació de System.IO.Unsafe
intRef :: IORef Int
intRef = unsafePerformIO $ newIORef 0
 
-- funció de ''unit'' () per evitar la ''memoització'' de les crides sense paràmetres
{-# NOINLINE llegeix #-}
llegeix :: () -> Int
llegeix _ = unsafePerformIO $ readIORef intRef
 
llegeix_100_cops :: () -> [Int]
llegeix_100_cops _ = replicate 100 $ llegeix ()
 
main = do
        -- cal que l'escriptura sigui avaluada
        --    que no sigui part d'un càlcul amb resultat NO requerit
        writeIORef intRef 42
        print $ llegeix_100_cops ()
        writeIORef intRef 30
        print $ llegeix_100_cops ()

El problema del sobreiximent de la pila (Stack overflow)[modifica | modifica el codi]

L'ús del "plegat per l'esquerra estricte" foldl' no garanteix que es treballi a espai constant de la pila.

En cas de funcions amb recursivitat final,

  • les expressions (o variables definides localment) en la crida recursiva generen thunks pendents d'avaluar, que s'apilen mentre no s'arriba al cas no recursiu.
  • l'avaluació estricta (amb '!' BangPatterns (a WHNF)) en els paràmetres acumuladors tampoc ho garanteix, si l'avaluació es topa amb un constructor.

Vegeu "Stack overflow" al HaskellWiki[186] i la secció #Avaluació estricta explícita (treballar a espai constant de la pila). També "Retainer profiling" per investigar "Perquè l'avaluació d'un objecte és retinguda"[187]

La solució més fàcil és que el tipus de l'acumulador algebraic estigui definit estricte (prefix '!') en tots els seus components.

Altrament el paquet deepseq[167] aporta funcions d'avaluació a #Forma Normal que sobrepassen les limitacions de seq. El paquet deepseq-th[188] facilita la derivació d'instàncies NFData, només per als tipus algebraics. Altrament caldrà instanciar-la manualment, definint la funció rnf de manera que per cada constructor n'avaluï tots els components.

-- ''deepseq'': versió de ''seq'' que avalua també els components
--    dels tipus de dades, si aquests instancien NFData,
--    derivable, si el tipus és algebraic, amb el paquet deepseq-th.
deepseq :: NFData a => a -> b -> b

El problema de les fugues de memòria[modifica | modifica el codi]

Vegeu enllaços "Memory leaks","[189]Space leak zoo"[190]

Vegeu també: "GHC: Perfilant l'ús de memòria".[191]

Exemples[modifica | modifica el codi]

Encapsulament estil O.O.[modifica | modifica el codi]

Cercant una aproximació funcional (amb immutabilitat) a l'encapsulament de la orientació a objectes i simulació d'herència de propietats del component:

-- Sigui un TipusEspecialitzat en forma de registre
--   que conté un component amb nom de camp ''super''
--   propagarem una Propietat del tipus del super
 
instance PropietatDelSuper TipusEspecialitzat where
  getProp TipusEsp_Constructor1 {super} = getProp super
  setProp p1 p2 obj @ TipusEsp_Constructor1 {super} = obj {super = setProp p1 p2 super}
  • Exemple. Els tipus d'objecte TPunt2D i TPunt3D (en terminologia Java classes (d'objectes) sense operacions), implementen (els en terminologia Java interfaces), el primer IFun2D i el segon IFun3D que requereix també IFun2D.
data TPunt2D = Punt2D { x,y ::Int}
-- especialització estructural per composició
data TPunt3D = Punt3D { punt2D :: TPunt2D, z :: Int}

Un mòdul per cada tipus, encapsulant dades i operacions.

  • Es pot evitar duplicar les implementacions d'alguns mètodes desacoblant-los de l'estructura, definint classes de propietats (mètodes getters/setters), i classes basades en aquestes, implementant els mètodes per defecte a la classe en un mòdul a banda, i sobreescrivint-los quan convingui a la instanciació invalidant el mètode per defecte. (ang.:overriding)

amb especialització de comportament per classes de tipus (interfícies)[modifica | modifica el codi]

-- fitxer ClassesPropietats.hs  -- (getters/setters)
module ClassesPropietats (PropXY(..), PropZ(..)) where
 
class PropXY t where
  getXY :: t -> (Int, Int)
  setXY :: Int -> Int -> t -> t
 
class PropZ t where
  getZ :: t -> Int
  setZ :: Int -> t -> t
-- fitxer ClassesBasadesEnPropietats.hs
-- Especialització per Interfícies
--
-- En estar desacoblades dels tipus de dades ''data''
--    no caldrà repetir-ne els mètodes a les instàncies
--
module ClassesBasadesEnPropietats (IFun2D(..), IFun3D(..)) where
 
import ClassesPropietats
 
-- funcionalitat 2D
 
class (PropXY t) => IFun2D t where
  desplaça2D :: Int -> Int -> t -> t
  desplaça2D dx dy punt = setXY (x+dx) (y+dy) punt
    where
      (x,y) = getXY punt
 
-- funcionalitat 3D
 
class (IFun2D t, PropZ t) => IFun3D t where
  desplaça3D :: Int -> Int -> Int -> t -> t
  desplaça3D dx dy dz punt = setZ (z+dz) $ desplaça2D dx dy punt
    where
      z = getZ punt
  • (IFun2D t) requereix (PropXY t)
  • (IFun3D t) requereix (IFun2D t, PropZ t)
-- fitxer Punt2D.hs per al tipus TPunt2D
{-# LANGUAGE NamedFieldPuns #-} -- sintaxi de l'encaix de registres simplificada
module Punt2D (TPunt2D, nouPunt2D) where
 
import ClassesPropietats
import ClassesBasadesEnPropietats
 
data TPunt2D = Punt2D { x,y ::Int} deriving (Eq, Show)
 
-- generadors
nouPunt2D :: Int -> Int -> TPunt2D
nouPunt2D x y = Punt2D { x, y}   -- {x} equival a {x=x} amb l'extensió ''NamedFieldPuns''
-- o també amb inicialització posicional
-- nouPunt2D = Punt2D
 
-- propietats
instance PropXY TPunt2D where
  getXY (Punt2D {x,y}) = (x, y)
  setXY x y p = p { x, y}    -- {x, y} al cos equival a {x = x, y = y}
 
instance IFun2D TPunt2D       -- incorpora funcionalitat 2D
-- fitxer Punt3D.hs per al tipus TPunt3D
{-# LANGUAGE NamedFieldPuns #-} -- sintaxi de l'encaix de registres simplificada
module Punt3D (TPunt3D, nouPunt3D) where
 
import ClassesPropietats
import ClassesBasadesEnPropietats
 
import Punt2D (TPunt2D)
import qualified Punt2D as P2D
 
-- definició de (.$) (estil flux de dades) com (|>) a l'F# (F sharp)
(.$) = flip ($)
 
data TPunt3D = Punt3D { punt2D :: TPunt2D, z :: Int} deriving (Eq, Show)
 
-- generador
nouPunt3D :: Int -> Int -> Int -> TPunt3D
nouPunt3D x y z = Punt3D { punt2D = P2D.nouPunt2D x y, z}
 
-- propietats
instance PropXY TPunt3D where
  getXY (Punt3D {punt2D}) = getXY punt2D
  setXY x y punt @ (Punt3D {punt2D}) = punt {punt2D = punt2D .$ setXY x y}
 
instance PropZ TPunt3D where
  getZ (Punt3D {z}) = z    -- a l'encaix {z} equival a {z = z}
  setZ z punt = punt {z}   -- al cos també, {z} equival a {z = z}
 
-- funcionalitat addicional
instance IFun2D TPunt3D      -- incorpora funcionalitat 2D
instance IFun3D TPunt3D      -- incorpora funcionalitat 3D

prova:

-- fitxer Main.hs
module Main where
 
import ClassesPropietats
import ClassesBasadesEnPropietats
import Punt2D
import Punt3D
 
-- definició de (.$) (com l'op. (|>) ''pipeline'' de F#) estil navegació de dades.
(.$) = flip ($)
 
p2D = nouPunt2D 1 2
        .$ setXY 3 4
        .$ desplaça2D 1 1
 
p3D = nouPunt3D 1 2 3
        .$ desplaça2D 2 2
        .$ desplaça3D 1 1 1
 
main = do
        putStrLn $ "p2D: " ++ show p2D
        putStrLn $ "p3D: " ++ show p3D

execució

runhaskell Main

especialització d'implementacions amb instanciació genèrica[modifica | modifica el codi]

Vegeu ref.[192] Separant la implementació del subtipatge (especialització d'interfícies). Vegeu ref. "OOP vs type classes"[193]

Quan totes les operacions d'una classe derivada es basen en les de les classes base i no en particularitats dels tipus, es pot definir la instanciació a nivell de classe. L'acompliment de ser instància de les classes base implica automàticament l'acompliment de la classe derivada.

-- Amb classes + instanciació particular a cada tipus
class (PropXY t) => IFun2D t where ... -- (IFun2D t) requereix (PropXY t)
 
-- Amb classes + instanciació genèrica
instance (PropXY t) => IFun2D t where ...
 
  -- per tant, per tot tipus t tal que (PropXY t), també implementa (IFun2D t)
  --   dit d'altra manera, a partir d'aquí: (PropXY t) implica (IFun2D t)

Modificacions a l'anterior exemple (caldran les extensions de llenguatge FlexibleInstances, UndecidableInstances):

-- fitxer ClassesBasadesEnPropietats.hs
--
-- Especialització entre instàncies
--
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module ClassesBasadesEnPropietats (IFun2D(..), IFun3D(..)) where
 
import ClassesPropietats
 
-- funcionalitat 2D
 
class (PropXY t) => IFun2D t where
  desplaça2D :: Int -> Int -> t -> t
 
instance (PropXY t) => IFun2D t where
  desplaça2D dx dy punt = setXY (x+dx) (y+dy) punt
    where
      (x,y) = getXY punt
 
-- funcionalitat 3D
 
class (IFun2D t, PropZ t) => IFun3D t where
  desplaça3D :: Int -> Int -> Int -> t -> t
 
instance (IFun2D t, PropZ t) => IFun3D t where
  desplaça3D dx dy dz punt = setZ (z+dz) $ desplaça2D dx dy punt
    where
      z = getZ punt
  • (PropXY t) implica (IFun2D t)
  • (IFun2D t, PropZ t) implica (IFun3D t)
  • caldrà eliminar les declaracions d'instàncies IFun2D i IFun3D dels fitxers Punt2D.hs i Punt3D.hs, que es basen en mètodes per defecte de les classes, que aquí ja no hi són.
-- fitxer Punt2D.hs
instance IFun2D TPunt2D
-- fitxer Punt3D.hs
instance IFun2D TPunt3D
instance IFun3D TPunt3D

execució, amb el mateix resultat:

runhaskell Main

Tipus existencial - Emulació del despatx dels mètodes virtuals de la O.O.[modifica | modifica el codi]

Vegeu ref.[96] El tipus existencial (designen un cjt de tipus amb característiques pròpies) dóna solució al tractament de col·leccions heterogènies incorporant els elements heterogenis com a components amb quantificació existencial (caracteritzats per implementar, com a mínim, classes de tipus específiques), sense trencar la homogeneïtat de les llistes.

module Forma (CForma(..)) where
 
class CForma t where
  perímetre :: t -> Double
  àrea :: t -> Double
  nom :: t -> String
module Cercle (TCercle, cercleNou) where
 
import Forma (CForma(..))
 
type TRadi = Double
 
data TCercle  = Cercle TRadi deriving (Eq, Show)
 
cercleNou :: TRadi -> TCercle
cercleNou = Cercle
 
instance CForma TCercle where
  perímetre (Cercle r) = 2 * pi * r
  àrea (Cercle r) = pi * r ^ 2
  nom _ = "Cercle"
module Rectangle (TRectangle, rectangleNou) where
 
import Forma (CForma(..))
 
type TCostat = Double
 
data TRectangle = Rectangle TCostat TCostat deriving (Eq, Show)
 
rectangleNou :: TCostat -> TCostat -> TRectangle
rectangleNou = Rectangle
 
instance CForma TRectangle where
  perímetre (Rectangle x y) = 2 * (x + y)
  àrea (Rectangle x y) = x * y
  nom _ = "Rectangle"
 -- fitxer existquant.hs
 {-# LANGUAGE ExistentialQuantification #-}
 
import Forma (CForma(..))
import Cercle (cercleNou)
import Rectangle (rectangleNou)
 
import Control.Monad as Monad
import Text.Printf
 
-- tipus existencial (designa un cjt. de tipus amb característiques pròpies)
--   característica: incorpora un component d'aquells tipus ''t'' que implementin CForma
 
data TForma = forall t. CForma t => Forma t
 
-- implementació del tipus existencial amb
--   despatx dels mètodes propis als mètodes del component
 
instance CForma TForma where
  perímetre (Forma forma) = perímetre forma
  àrea (Forma forma) = àrea forma
  nom (Forma forma) = nom forma
 
 -- llista de valors del tipus existencial
formes :: [TForma]
formes = [Forma (cercleNou 2.4), Forma (rectangleNou 3.1 4.4)]
 
 -- definició de (.$) com l'operador ''pipeline'' (|>) de l'F# (estil flux de dades)
(.$) = flip ($)
 
 -- forM_ mapeja sequencialment als elems. de la llista una func.
 --                  amb efectes col·laterals de tipus mònada (m tipResultat), descartant resultats.
main = do
  Monad.forM_ formes $ \forma -> do
      printf format (forma .$ nom) (forma .$ perímetre) (forma .$ àrea)
  where
    format = "Forma %s, perímetre %.2f, àrea %.2f\n"
runhaskell existquant.hs

dóna la següent sortida:

Forma Cercle, perímetre 15.08, àrea 18.10
Forma Rectangle, perímetre 15.00, àrea 13.64

Entrada / Sortida amb descriptor regionalitzat[modifica | modifica el codi]

withFile / withBinaryFile (de System.IO) obre el fitxer, en passa el descriptor a la funció que executa l'entrada/sortida (Handle -> IO r) i, en retornar el control o en cas d'excepció, tanca el fitxer alliberant recursos. Basat en la clàusula bracket a la secció #excepcions.

  • Evita l'ús del descriptor "handle" (mànec) del fitxer més enllà del retorn (recurs circumscrit a l'àmbit o "regionalitzat").

Vegeu també mònades per a l'allotjament de recursos en regions apilades[194][195]

 -- Fitxer prova.hs (decodifica nombres i n'imprimeix el factorial)
 module Main(main) where
 
  import qualified Control.Monad as Monad
  import qualified Numeric
  import Control.Exception (try)
  import Text.Printf
  import System.IO
  import System( getArgs)
 
  llegirSencerDecimal :: String -> Maybe (Int, String)
  llegirSencerDecimal text =
    case Numeric.readDec text of -- retorna llista d'interpretacions sintàctiques
      [(n_descodificat, resta)] -> Just (n_descodificat, resta)  -- una sola interpretació
      [] -> Nothing       -- cap ni una => no hi ha dígits
      _ -> Nothing       -- diverses interpretacions => entrada ambigua
 
  -- Integer: precisió arbitrària, Int: precisió s/. paraula de la màquina
 
  fact :: Int -> Integer
  fact 0 = 1
  fact n | n > 0 = fact_rf 1 n
 
   where fact_rf :: Integer -> Int -> Integer
         fact_rf acum n | n > 1  = fact_rf (acum * toInteger n) (n-1)
                        | n == 1 = acum
 
  imprimeix_fact n resta_de_linia = do
        printf "n=%d => fact n =%d" n (fact n)   -- escriu n, (fact n)
        case resta_de_linia of
             [] -> putStrLn ""   -- escriu salt de línia
             (sep:_) | sep `elem` " ," -> putStrLn " (seguit de separador)"
             _ -> putStrLn " (hi ha text enganxat al nombre)"
 
  analitza_linia_i_tracta_nombre :: Handle -> IO (Either IOError ())
  analitza_linia_i_tracta_nombre h1 = try  -- retorna excepció com a resultat per controlar-la més tard
  (do
    línia <- hGetLine h1
    case llegirSencerDecimal línia of
      Just (n, resta) -> imprimeix_fact (abs n) resta
      Nothing -> putStrLn "error de descodificació o nombre inexistent"
  )
 
  processa_linia_fitxer:: Handle -> IO ()
  processa_linia_fitxer h1 = do
         resultat <- analitza_linia_i_tracta_nombre h1
         case resultat of    -- controla excepció al resultat de tipus Either IOError ()
           Left excepcio -> do
                -- tasques de finalització
                -- hClose h1     -- tanca fitxer (amb withFile ja no caldrà)
                ioError excepcio -- dispara excepció
           Right _valor -> return ()
 
  processa_nom_fitxer nom = catch
       ( do
          withFile nom ReadMode $ \handle ->
           -- llaç fins que es dispari una excepció per fi de fitxer
           Monad.forever $ processa_linia_fitxer handle
       )
       (\excep -> putStrLn $ show excep
       )
 
  main = do
    args <- getArgs
    case args of
      [] -> print "afegiu nom_del_fitxer\n"
      -- mapM_ mapeja la func. que retorna tipus mònada als arguments, descartant resultat
      _ -> Monad.mapM_ processa_nom_fitxer args
 
 -- fi de prova.hs
-- fitxer nums.txt
0
1ABC
2,
10
15—fi de nums.txt
runhaskell prova.hs nums.txt

produeix la sortida següent:

n=0 => fact n =1
n=1 => fact n =1 (hi ha text enganxat al nombre)
n=2 => fact n =2 (seguit de separador)
n=10 => fact n =3628800
n=15 => fact n =1307674368000
nums.txt: hGetLine: end of file

Concurrència simple amb MVars - Productor-consumidor[modifica | modifica el codi]

Vegeu-ho a Haskell concurrent

Cues d'entrada (Chan) - Client-servidor[modifica | modifica el codi]

Vegeu exemple a Haskell concurrent

Paral·lelisme de tasques - Compilació per a processadors multicor[modifica | modifica el codi]

Només a GHC. Vegeu Compilador Haskell de Glasgow#Paral·lelisme de tasques - Compilació per a processadors multicor.

Concurrència condicionada amb TVars - Mònada STM - Memòria transaccional[modifica | modifica el codi]

Vegeu exemple a Haskell concurrent

Exemples de Fletxes (seqüenciació d'efectes basada en l'encadenament de morfismes)[modifica | modifica el codi]

Ent./Sort. amb fletxes de Kleisli (mònades elevades a fletxes)[modifica | modifica el codi]

Vegeu exemple a Fletxa (programació funcional)#Ent./Sort. amb fletxes de Kleisli (mònades elevades a fletxes)

Extreure informació d'un document XML[modifica | modifica el codi]

Vegeu exemple a Fletxa (programació funcional)

Quasi-Quotations: Plantilles per a expressions, patrons, tipus i declaracions[modifica | modifica el codi]

Vegeu Glasgow_Haskell_Compiler#Quasi-Quotations

Paraŀlelisme[modifica | modifica el codi]

Vegeu Glasgow_Haskell_Compiler#Paral·lelisme de tasques - Compilació per a processadors multicor

Amb Estratègies[modifica | modifica el codi]

Vegeu a GHC: Estratègies i també Reducció en paraŀlel mitjançant estratègies

Amb la mònada Par[modifica | modifica el codi]

Vegeu Haskell_concurrent#Encadenament de càlculs simultanis - la mònada Par

Amb paral·lelisme de dades DPH[modifica | modifica el codi]

Vegeu QuickSort amb Data Parallel Haskell

Gestor de paquets de biblioteques i aplicacions[modifica | modifica el codi]

El Haskell utilitza un gestor de paquets 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[196] i utilitza arxius de descripció de paquets amb l'extensió ".cabal". Vegeu "Com crear un paquet"[197]

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 i la versió menor és el tercer. L'API d'un paquet s'identifica per la versió major.menor (tres components numèrics).[198]

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.

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à instalar-lo prèviament i, en acabat, instaŀ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[199]

  • instal·lació manual: Si no es disposa de l'executable cabal del paquet cabal-install, per instal·lar un paquet cal descarregar-lo, buscar el fitxer Setup.hs o bé Setup.lhs i executar
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 si configures amb—global en comptes de—user
                                     # , per actualitzar el rebost del sistema
  • instal·lació automatitzada d'un paquet del rebost Hackage:

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í.[200]

 # 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
  • per llistar els paquets que ja tenim a GHC, "ghc-pkg list". No existeix encara l'equivalent "hugs-pkg"[201]
  • en cas de problemes per biblioteques que no compilen bé, és util limitar-ne la versió amb l'opció—constraint=dependència
--constraint="nom_biblio < versió" o --constraint=nom_biblio==versió (l'API s'identifica per 3 components de la versió)
exemples (les cometes només fan falta si hi ha espais o bé
           símbols de redirecció de l'intèrpret de comandes {<,>}):
cabal install QuickCheck --constraint=BiblioDeLaQualDepen==2.3.4.*
  • a partir de cabal-install v.1.16 cabal admet paral·lelisme en l'instaŀlació
cabal install -j "wx == 0.13.*"

Conflictes de dependències[modifica | modifica el codi]

Tantmateix cal tenir en compte que l'ús simultani de biblioteques que facin servir diferents versions d'un mateix paquet és molt problemàtic.[202] Cal evitar absolutament la comanda cabal upgrade que ens abocaria al problema esmentat.[202]

Entorn aïllat (sandboxed) de desenvolupament amb cabal-dev[modifica | modifica el codi]

cabal-dev crea un dipòsit de biblioteques específic del projecte en el subdirectori ./cabal-dev

els executables queden al directori ./cabal-dev/bin
es pot reutilitzar un mateix dipòsit, creant el subdirectori ./cabal-dev com enllaç a un altre directori cabal-dev, per exemple:
ln -s $HOME/webs/yesod/cabal-dev cabal-dev

cabal-dev com a comanda, passa el dir. dels dipòsits a d'altres subcomandes:

cabal-dev ghc-pkg list

cabal-dev admet afegir al projecte altres subprojectes amb la comanda add-source

Resolució multi-projecte amb cabal-meta[modifica | modifica el codi]

cabal-meta permet la resolució conjunta de dependències, en la instal·lació conjunta de diversos subprojectes i paquets (del hackage o de directoris locals o bé remots (git)).

Un fitxer sources.txt conté la llista de subprojectes,[203] on cada línia pot contenir:

  • cas de començar per '.' o bé '/': directori local (seguit d'opcions) amb un altre node sources.txt o bé un subprojecte cabal -- les opcions són flags per al cabal
  • cas de prefix "git:", "https", "http": gitLocation gitBranca? opció*, és a dir, projecte recuperable amb git (sistema de control de versions distribuït)(les opcions són flags per al cabal)
  • altrament: paquet-del-hackage opció*

ús: cabal-meta --dev install (especificant --dev segueix les directrius de cabal-dev en dipòsits i instal·lació d'executables.

Creació del fitxer de projecte .cabal[modifica | modifica el codi]

El fitxer de projecte .cabal està relacionat un a un amb la carpeta que el conté.

cabal init
interroga l'usuari a la cònsola de comandes i genera un esquelet de projecte que caldrà editar posteriorment per afegir-hi els noms dels fonts i els paquets dels quals depèn.
leksah 
Entorn gràfic d'usuari (IDE) que conté un formulari d'edició del fitxer de projecte.

Un cop creat, la comanda cabal sense nom de paquet treballa sobre el fitxer de projecte del directori de treball:

cabal clean       -- neteja tots els fitxers generats per la compilació
  -- ''cabal-dev'' treballa sobre un rebost de biblioteques específic del projecte
  --         caldrà incorporar la subcarpeta cabal-dev/bin a la var. PATH
cabal-dev install     -- configura, compila, relliga i instal·la al rebost del projecte
cabal install -- configura, compila, relliga i instal·la al rebost de l'entorn d'usuari

Vegeu també "Com escriure (i empaquetar) un programa en Haskell".[204] També "Actualitzant un paquet a noves versions de GHC i biblioteques".[205]

Eines relacionades amb el gestor de projectes[modifica | modifica el codi]

cabal-dev
[206] versió de cabal que crea un dipòsit de biblioteques específic del projecte que es desenvolupa, per evitar conflictes amb els paquets de biblioteques de l'usuari. Vegeu ampliació a GHC#Eines.
cabal-meta
[203] instal·la (amb cabal-dev cas de --dev), en una sola comanda (resolució conjunta de dependències) diversos paquets (del hackage o de directoris locals o bé remots (git)) especificats en un o més fitxers sources.txt encadenats en arbre. Vegeu ampliació a GHC#Eines.
cabal-ghci
[207] engega ghci preparant-lo per a proves del projecte, amb camins i extensions de llenguatge obtinguts del fitxer de projecte .cabal
cabal-progdeps
[208] llista les dependències en un directori de projecte
yackage
[209] servidor de rebost "hackage" local per a desenvolupament

The Haskell Platform[modifica | modifica el codi]

The Haskell Platform vol ser un instal·lador empaquetant el compilador GHC (només a Windows, a Unix cal descarregar-lo prèviament), una biblioteca i un paquet d'eines incorporant paquets de rutines[210] del rebost Hackage.[199] amb encaix verificat respecte la versió de la biblioteca Base[97] inclosa. (Preguntes freqüents)[211]

Entorns de desenvolupament i altres eines[modifica | modifica el codi]

hlint
Aquí[212] analitzador de codi amb suggerències de millora. Avisa de construccions redundants i proposa alternatives al codi.
Leksah
Aquí.[213] Entorn gràfic de desenvolupament (IDE) basat en GTK, útil per estudiar un paquet. Genera fitxers de projecte en format .cabal. Permet obtenir info. a partir del menú de context, dels elements que marqueu d'un programa. Instruccions d'instal·lació aquí[214] Permet la depuració de programes (Truc: cal definir-hi un Workspace i un Package perquè s'activi la depuració).
eclipseFP
Aquí.[215] Endollable per a l'entorn de desenvolupament (IDE) Eclipse
Visual Haskell 2005
Entorn de Microsoft Visual Studio per al Haskell. Hi va haver un desenvolupament per al Visual Studio 2005 inacabat segons les fonts.[216] Malgrat que el compilador GHC el desenvolupa Microsoft Research a Cambridge (Regne Unit), no hi ha eines de suport específiques del Microsoft Windows.
ThreadScope
Analitzador de paral·lelisme al GHC. Vegeu GHC#Eines.
hpc (haskell program coverage)
Anàlisi de cobertura del codi. Genera traces d'execució i, després de diverses execucions, permet mostrar en un informe html, amb marcatge de colors, quines parts del codi no s'han executat mai i quines expressions booleanes són sempre certes o sempre falses. Vegeu GHC#Eines.
hp2any
perfil d'ús de memòria en temps real i diferit. Vegeu GHC#Eines.
hgettext
eina que permet extreure cadenes de text del codi font per preparar la internacionalització amb traduccions múltiples.[217]

Implementacions[modifica | modifica el codi]

Vegeu [218]

GHC ("Glasgow Haskell Compilation System")
Compilador[219] i intèrpret (GHCi)[220] que va més enllà del Haskell98.

Incorpora "concurrència","[221]paral·lelisme","[222]Memoria Transaccional per Software"[223][224] i opcions d'optimització. Pot generar codi nadiu, codi C, o codi optimitzat mitjançant LLVM.[225]

A partir de la versió 6.10 incorpora "Data Parallel Haskell"[226][227] 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.

Hugs
Intèrpret.[228] Implementació quasi completa del Haskell 98. Incorpora registres extensibles anomenats TRex.[229]
nhc98
Compilador[230] per a màquines de 32 bits. Intèrpret "Hi". Muntador "Hmake".
UHC / EHC
Utrecht Haskell Compiler / Essential Haskell Compiler[231] amb rerefons de compilació per a llenguatge C, Java, LLVM i CLR.[232]
YHC 
York Haskell Compiler[233] amb rerefons de compilació a Javascript[234] i traçador de crides Hat[235]
JHC
Jhc Haskell Compiler[236] Compilador de John Meacham.[237] (Opinió sobre JHC)[238]
LHC
LHC Haskell Compiler[239] Projecte derivat del JHC.

Extensions de noms de fitxer[modifica | modifica el codi]

Vegeu enllaç[240]

.hs
Haskell source
.lhs (Literate haskell source)
font documentat segons Programació literària immergint el codi dins la documentació:
  • o bé dins un doc. de text, prefixant les línies de codi amb '>' (estil rastre d'ocell)
  • o bé dins un doc. LaTeX situant el codi entre \begin{code} i \end{code} Vegeu lhs2tex
.cabal
descripció de paquet Cabal
.x
doc. d'entrada per al generador d'analitzadors de lèxic Alex (de l'estil del lex) (genera *.hs)
.lx
doc. Alex literari (a l'estil del .lhs)
.y
doc. d'entrada per al generador d'analitzadors gramaticals Happy (de l'estil del yacc) (genera *.hs)
.ly
doc. Happy literari (a l'estil del .lhs)
.gh
preprocessador Generic Haskell
.gc
doc. d'entrada al GreenCard (descripció enllaç amb codi C) adaptat al FFI (Foreign Function Interface) normatiu per a Haskell2010[8]
.chs
doc. d'entrada al convertidor c2hs
.hsc
doc. d'entrada al convertidor hsc2hs

Plataformes, aspectes[modifica | modifica el codi]

plataformes virtuals[modifica | modifica el codi]

sobre Xen
sobre LLVM (Low Level Virtual Machine)
  • GHC pot compilar via LLVM per aprofitar-ne les optimitzacions.[244] Comproveu-hi les "Plataformes suportades"[245]
sobre JVM
  • Frege: llenguatge quasi Haskell adaptat als tipus de dades i entorn del Java que funciona sobre el JDK de Java-7.[246][247][248]
  • LambdaVM: Rerefons JVM per al compilador GHC[249]
  • UHC Jazy: Rerefons JVM per al compilador UHC. (implementació parcial).[250]
sobre JavaScript
  • Yrc2Js: Rerefons a Javascript del compilador YHC.[251]
  • UHC: Rerefons (en desenvolupament) de l' "Utrecht Haskell Compiler" [252]
  • Elm: llenguatge molt similar al Haskell, de programació reactiva funcional que compila a Javascript
sobre CLR

Altres plataformes[modifica | modifica el codi]

A sistemes d'Apple (Mac OS, iOS)

Dialectes del Haskell[modifica | modifica el codi]

DDC (The Disciplined Disciple Compiler)
(cat: El Compilador del Deixeble Disciplinat): versió d' avaluació estricta, contràriament a Haskell (en desenvolupament).[256]
OOHaskell
biblioteca d' orientació a objectes d'Oleg Kyseliov i Ralf Lämmel (versió del 2005 no actualitzada).[257]
O'Haskell
versió orientada a objectes[258] de Johan Nordlander. Treball del 2001 evolucionat a Timber
Timber
evolució de O'Haskell, per a la programació reactiva de sistemes encastats.[259] (del mateix autor Johan Nordlander). Limitacions i problemàtiques[260]
Hume
llenguatge funcional estricte per a sistemes encastats de recursos limitats, amb concurrència per pas de missatges i característiques CSP on la sintaxi del nivell d'expressions és la del Haskell.
Incorpora limitació de costos de recursos per procés, en temps, memòria dinàmica i mida de la pila amb les corresponents excepcions.
CAL
aproximació a Haskell sobre la JVM integrat dins Open Quark de Business Objects,[261][262][263] companyia francesa absorbida[264] pel gegant alemany del programari SAP AG
Jaskell
llenguatge sobre la Java VM, funcional i d'avaluació tardana, força diferent en sintaxi.[265]
Frege
llenguatge quasi Haskell d'avaluació tardana sobre la JVM, amb particularitats del Java (literals Java, mètodes als tipus de dades, ...) sobre Java-7[246][247]
Elm
llenguatge quasi Haskell estricte de programació reactiva funcional per generar interfícies gràfiques d'usuari que compila a JavaScript, HTML i CSS

Relacionat[modifica | modifica el codi]

Referències[modifica | modifica el codi]

  1. [Parallel Haskell: Projecte de dos anys per promocionar-ne l'ús en el món real](anglès)
  2. Haskell - Concurrència i paral·lelisme(anglès)
  3. Haskell98 Online Report(anglès)
  4. GHC - Notes de l'edició(anglès) GHC 7.0+ pren per defecte l'estàndard de llenguatge Haskell2010
  5. Haskell prima - Propostes per a l'evolució del llenguatge (anglès)
  6. Extensions del Haskell i compiladors que les implementen(anglès)
  7. Haskell2010 Online Report(anglès)
  8. 8,0 8,1 Anunciant Haskell 2010(anglès) Acord final sobre Haskell 2010
  9. Característiques del Haskell 2010 (anglès)
  10. Anunci del nou procés Haskell Prima, i del Haskell 2010 (anglès)
  11. Propostes per a Haskell 2014(anglès)
  12. Extensible Records(anglès)
  13. Extensió TRex de l'intèrpret Hugs
  14. 14,0 14,1 Obtenir el compilador GHC
  15. GAlternatives - GUI per al selector d'alternatives a Linux(anglès)
  16. Comanda runghc (anglès)
  17. GHCi - guia d'usuari de l'intèrpret(anglès)
  18. GHCi - Declaracions de tipus i classes a l'intèrpret
  19. 19,0 19,1 19,2 Haskell.org - Lazy vs. Non-strict (anglès) Avaluació tardana i avaluació no-estricta
  20. 20,0 20,1 Haskell is a strict language
  21. 21,0 21,1 HaskellWiki - Thunk(anglès)
  22. Generalising Monads to Arrows(anglès)
  23. Estructura lèxica del Haskell98 (anglès)
  24. Extensió UnicodeSyntax(anglès)
  25. H2010 - Identificadors i variables(anglès)
  26. Sintaxi H98(anglès) consym reserva el rol de constructor als símbols que comencen per ':'
  27. Referència de sintaxi de Haskell2010(anglès)
  28. Haddock - Com documentar(anglès)
  29. Invocant Haddock - opcions(anglès)
  30. IEC.DLC - Sagnat
  31. 31,0 31,1 31,2 Rank-2 types, ($), and the monomorphism restriction (anglès)
  32. 32,0 32,1 Fixity declarations(anglès) Precedències i associativitat dels operadors
  33. Guia de classes estàndards - Instàncies derivades (anglès)
  34. Instàncies derivades - especificació (anglès)
  35. Prelude del H98 (Predefinits)(anglès) La majoria de les classes instanciables amb la clàusula deriving són aquí
  36. 36,0 36,1 Data.Eq(anglès)
  37. 37,0 37,1 37,2 37,3 Data.Ord(anglès)
  38. 38,0 38,1 38,2 Prelude#Enum(anglès)
  39. 39,0 39,1 39,2 Data.Ix - classe dels índexs (anglès)
  40. 40,0 40,1 40,2 Prelude#Bounded(anglès)
  41. 41,0 41,1 Text.Show(anglès)
  42. 42,0 42,1 Text.Read(anglès)
  43. Numeric(anglès)
  44. 44,0 44,1 Prelude#Num(anglès)
  45. 45,0 45,1 Data.Bits(anglès)
  46. 46,0 46,1 Prelude#Real(anglès)
  47. 47,0 47,1 Prelude#Integral(anglès)
  48. 48,0 48,1 Prelude#Fractional(anglès)
  49. 49,0 49,1 Prelude#RealFrac(anglès)
  50. 50,0 50,1 Prelude#RealFloat(anglès)
  51. 51,0 51,1 Prelude#Floating(anglès)
  52. 52,0 52,1 52,2 Data.Ratio(anglès)
  53. 53,0 53,1 Data.Fixed - reals de coma fixa(anglès)
  54. GHC - La clàusula deriving admet més classes(anglès)
  55. Data.Bool(anglès)
  56. 56,0 56,1 Data.Char(anglès)
  57. Data.Int(anglès)
  58. Data.Word(anglès)
  59. Data.Complex(anglès)
  60. Ambiguous Types, and Defaults for Overloaded Numeric Operations(anglès)
  61. Overloaded string literals(anglès)
  62. Data.Word
  63. Instàncies de la classe Real
  64. Extensions de GHC - Record Puns(anglès) simplificació de sintaxi en l'encaix dels camps dels registres
  65. Tuples a Haskell98(anglès)
  66. Data.Tuple
  67. proposta NoDatatypeContexts(anglès)
  68. 68,0 68,1 Sinònims de tipus liberals(anglès)
  69. 69,0 69,1 Llistes - Nil i Cons(anglès)
  70. Data.List(anglès)
  71. How to pick your string library in Haskell(anglès) Com escollir la biblioteca de cadenes de caràcters en Haskell
  72. GHC Guia d'usuari - senyal de compilació: -fwarn-incomplete-patterns(anglès) deshabilitada per defecte
  73. Pattern type signatures (anglès)
  74. Pattern guards(anglès)
  75. HaskellWiki - Let vs Where
  76. GHC - Implicit parameters(anglès)
  77. Operador pipeline (entubar) de Fsharp(anglès)
  78. Aplicació parcial
  79. Secció d'un operador infix (anglès)
  80. Throw / Catch, restriccions (anglès)
  81. Control.OldException(anglès)
  82. Control.Exception(anglès)
  83. La funció evaluate(anglès)
  84. Crides d'error en Ent./Sort.(anglès)
  85. Assercions(anglès)
  86. Undefined: Bottom (No finalització)
  87. Depuració al Haskell(anglès)
  88. Debug.Trace - Imprimir traces des del codi funcional(anglès)
  89. Debug.Trace.traceStack
  90. Tipus existencials (anglès)
  91. tipus existencials en Haskell(anglès)
  92. Haskell GHC - Perquè existencial(anglès)
  93. Quantificació existencial
  94. Tipus existencial a EHC/UHC amb la paraula reservada exists(anglès)
  95. Tipus existencial al JHC amb la paraula reservada exists(anglès)
  96. 96,0 96,1 Tipus existencial (anglès)
  97. 97,0 97,1 Biblioteca Base de GHC - vegeu mòduls marcats Base(anglès)
  98. Opcions de GHC per al preprocessador(anglès)
  99. IORef's - referències mudables dins la mònada IO
  100. Top level mutable state (anglès) Estat de nivell global d'un programa
  101. Data.IORef atomicModifyIORef(anglès) L'ús d' atomicModifyIORef per protegir més d'una IORef està desaconsellat
  102. La mònada ST (anglès) permet fer computacions amb actualitzacions in situ
  103. La mònada ST - referència(anglès)
  104. STRef's - referències a valors mudables dins la mònada ST
  105. Control.Category (anglès)
  106. 106,0 106,1 Control.Arrow (anglès)
  107. Introducció planera a "Haskell XML Toolbox" (anglès)
  108. Fletxes (Arrows) a GHC
  109. Fletxes (Arrows) a l'intèrpret Hugs
  110. 110,0 110,1 Iteratee I/O(anglès)
  111. Deterministic allocation and freeing of scarce resources(anglès) Allotjament determinista i alliberament de recursos escassos.
  112. Introducció als conduits(anglès)
  113. Viccionari anglès - El sufix -ee(anglès)
  114. Importació d'instàncies
  115. Noms de mòdul jeràrquics(anglès)
  116. Running a compiled program (anglès) Paràmetres per al Run Time System
  117. Guia d'Opcions de línia d'ordres en Haskell(anglès)
  118. API System.Console.GetOpt amb exemples(anglès)
  119. Neil Mitchell - Línia d'ordres, exemples d'arguments(anglès)
  120. Standard Prelude - funcions i tipus predefinits(anglès)
  121. Prelude del GHC(anglès)
  122. API's de les biblioteques bàsiques del Haskell(anglès)
  123. Interfície dels vectors immutables(anglès)
  124. Interfície dels vectors mudables(anglès)
  125. Vectors de tractament paral·lel (paral·lelisme de dades)(anglès)
  126. Vectors en memòria amb accés de nivell baix(anglès)
  127. Vectors d'elements encapsulats(anglès)
  128. Vectors d'elements Unboxed (No encapsulats)(anglès)
  129. HaskellWiki - DiffArrays(anglès)
  130. Hackage - DiffArrays - Vectors per diferència(anglès)
  131. Vectors en memòria global (mònada IO)(anglès)
  132. Vectors en memòria local (mònada ST)(anglès)
  133. Guia sobre Array i Ix (anglès)
  134. Altres tipus de vectors a GHC (anglès)
  135. Extensions comunes a GHC i Hugs (anglès)
  136. Classes multiparàmetre i dependències funcionals(anglès)
  137. Fun with Type Functions(anglès)
  138. Famílies de tipus - Tipus associats(anglès)
  139. Mòduls de primera al Haskell (anglès) Parametrització al Haskell
  140. Monomorphism_restriction(anglès)
  141. Polimorfisme
  142. Rank-N types(anglès)
  143. Switching off the Monomorphism restriction(anglès)
  144. Let Generalisation in GHC 7.2(anglès) Monomorfisme als lligams locals
  145. Tipus de Dades Algebraics Generalitzats (GADTs)
  146. Haskellwiki - newtype
  147. Extensió GeneralizedNewtypeDeriving (anglès)
  148. Keyword arguments in Haskell(anglès)
  149. DrIFT (anglès)
  150. Data.Derive (anglès)
  151. Manual de Data.Derive
  152. HaskellWiki - Kind (anglès)
  153. Explicitly-kinded quantification(anglès)
  154. 154,0 154,1 Promoció de tipus(anglès)
  155. El kind Constraint
  156. Equality constraints(anglès)
  157. Constraint Kinds for GHC(anglès)
  158. GHC - Famílies de tipus
  159. HaskellWiki - Type Families(anglès)
  160. 7.4.1 - release notes(anglès)
  161. Forma normal Beta
  162. GHC Prelude - operació seq(anglès)
  163. FOLDOC Weak Head Normal Form
  164. reference.com - Weak Head Normal From
  165. HaskellWiki - Weak Head Normal Form
  166. Univ. de Girona - Introducció a la prog. funcional
  167. 167,0 167,1 El paquet deepseq(anglès)
  168. H2010 Language report - Strictness Flags(anglès)
  169. Eficiència i rendiment en tipus de dades (anglès)
  170. BangPatterns - Avaluació estricta dels paràmetres (anglès)
  171. RealWorldHaskell - Optimitzacions(anglès)
  172. Haskellwiki - Tail recursion
  173. Tail recursion modulo cons
  174. Limitations of strictness analysis (anglès)
  175. biblioteca "text" de tires com a vectors de caràcters de 16 bits(anglès)
  176. HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
  177. Variables globals(anglès)
  178. La mònada Reader
  179. HaskellWiki - Top level mutable state
  180. Una solució al problema de les configuracions.
  181. paquet Seal-module
  182. Implicit configurations
  183. Hack - In computer science(anglès)
  184. 184,0 184,1 System.IO.Unsafe (anglès)
  185. IO inside: The dark side of the IO Monad(anglès)
  186. HaskellWiki - Stack overflow
  187. Retainer profiling
  188. El paquet deepseq-th(anglès)
  189. HaskellWiki - Memory leak(anglès)
  190. .Edward Z. Yang - Space leak zoo(anglès)
  191. Profiling memory usage(anglès)
  192. l'extensió de llenguatge UndecidableInstances(anglès)
  193. OOP vs type classes
  194. Regionalització de recursos(anglès)
  195. Paquets amb Mónades per a la regionalització
  196. Cabal - Com instal·lar un paquet Cabal (anglès)
  197. Com crear un paquet Cabal
  198. Package versioning policy(anglès) Política de versionament dels paquets
  199. 199,0 199,1 Hackage - Rebost oficial d'aplicacions i biblioteques empaquetades amb Cabal (anglès)
  200. Cabal - Preguntes freqüents(anglès)
  201. Afegint paquets a una instal·lació Hugs (anglès)
  202. 202,0 202,1 Cabal FAQ - Conflictes de dependències(anglès)
  203. 203,0 203,1 cabal-meta (anglès)
  204. Com escriure (i empaquetar) un programa en Haskell(anglès)
  205. Actualitzant paquets a noves versions de GHC i biblioteques(anglès)
  206. cabal-dev (anglès)
  207. cabal-ghci (anglès)
  208. cabal-progdeps (anglès)
  209. yackage (anglès)
  210. Docum de la Haskell Platform(anglès)
  211. The Haskell Platform FAQ (anglès)
  212. Paquet hlint que genera el programa del mateix nom(anglès)
  213. Leksah - Entorn gràfic de desenvolupament (IDE) per a Haskell (anglès)
  214. Instal·lació de l'entorn de desenvolupament Leksah(anglès)
  215. Endollable per a l'entorn de desenvolupament Eclipse (anglès)
  216. Fòrum StackOverflow - Visual Haskell(anglès)
  217. HaskellWiki - Internacionalització dels programes en Haskell(anglès)
  218. Implementacions de Haskell (anglès)
  219. Compilador GHC "Compilador Haskell de Glasgow" (anglès)
  220. Intèrpret del Glorious Compilador Haskell de Glasgow (anglès)
  221. Concurrència con Haskell(castellà)
  222. Glasgow Parallel Haskell(anglès)
  223. Un Tast de Haskell - Memoria Transaccional per Software (anglès)
  224. Haskell Wiki - Memoria Transaccional per Software(anglès)
  225. Rerefons del GHC (anglès)
  226. HaskellWiki - Data Parallel Haskell(anglès)
  227. Nested Data Parallellism in Haskell(anglès)
  228. Hugs intèrpret de Haskell (anglès)
  229. Registres extensibles TRex de l'intèrpret Hugs (anglès)
  230. Compilador nhc98 (anglès)
  231. Utrecht Haskell Compiler (anglès)
  232. UHC/EHC rerefons de compilació (anglès)
  233. York Haskell Compiler(anglès)
  234. Generació de codi Javascript des d'YHC(anglès)
  235. Traçador de crides Hat(anglès)
  236. Jhc Haskell Compiler (anglès)
  237. Web de John Meacham creador del compilador JHC(anglès)
  238. Opinió sobre JHC
  239. LHC Haskell Compiler(anglès)
  240. Preprocessadors de Haskell(anglès)
  241. wiki de HaLVM: The Haskell Lightweight Virtual Machine(anglès)
  242. Run Haskell on Xen - No Operating System Required!(anglès)
  243. Adam Wick - The Haskell Lightweight VM
  244. The Compilador Haskell de Glasgow and LLVM(anglès)
  245. Rerefons LLVM de GHC(anglès)
  246. 246,0 246,1 El llenguatge Frege(anglès)
  247. 247,0 247,1 Wiki del projecte Frege(anglès)
  248. Hola Món amb Frege
  249. LambdaVM rerefons de GHC a codi Java
  250. UHC Jazy - rerefons a la màq. virtual Java(anglès)
  251. YHC/Javascript (anglès)
  252. Rerefons Javascript del compilador UHC(anglès)
  253. rerefons CLR a UHC(anglès)
  254. Running Haskell on the CLR(anglès)
  255. Anunciant GHC per a l'IOS (anglès)
  256. El Compilador del Deixeble Disciplinat DDC(anglès)
  257. Llenguatge OOHaskell(anglès)
  258. Llenguatge O'Haskell(anglès)
  259. Llenguatge Timber(anglès)
  260. Timber - Limitacions i problemàtiques(anglès)
  261. Open Quark(anglès)
  262. OpenQuark (llenguatge CAL sobre JVM)(anglès)
  263. Viquipèdia anglesa - Quark Framework (implement. de Haskell per a la JVM)(anglès)
  264. Bussiness Objects a la wikip. francesa (francès)
  265. Jaskell(anglès)

Enllaços externs[modifica | modifica el codi]

A Wikimedia Commons hi ha contingut multimèdia relatiu a: Haskell Modifica l'enllaç a Wikidata