Es diu que és un llenguatge funcional pur. El cert és que admet variables d'estat però permet encapsular-ne els canvis (context ST), o bé circumscriure'n els efectes col·laterals al nivell superficial (context IO).
Haskell basa el polimorfisme en el requeriment d'implementació d'interfícies pels tipus dels paràmetres. Les interfícies amb un paràmetre de tipus t defineixen una partició de l'espai dels tipus en classes segons si les implementen o no, i per això s'anomenen classes de tipus.
-- classe de tipus (la interfície)classEqtwhere(==)::t->t->Bool-- iguals(/=)::t->t->Bool-- desiguals-- implementació per defectex==y=not(x/=y)x/=y=not(x==y)-- caldrà especificar només una de les operacions en definir la implementaciódataBool=False|True-- definició del tipus Bool-- instància (la implementació) de la classe Eq per al tipus BoolinstanceEqBoolwhere(==)FalseFalse=True(==)TrueTrue=True(==)__=False-- definició del tipus (Llista a) = Nil | Cons a (Llista a)-- els símbols '[' i ']' designen una llistadata[a]=[]-- el constructor Nil es denota amb "[]"|a:[a]-- el constructor Cons es denota amb ':' en posició infix-- * per sintaxi, si un símbol comença per ':', va infix-- * tot identificador de funció es pot posar en infix si s'envolta de cometes revesses-- exemple d'ús: "Eq t =>" es llegeix: per aquells tipus t tals que (Eq t)ésMembre::Eqt=>t->[t]->Bool-- ésMembrex[]=FalseésMembrex(cap:cua)=x==cap||x`ésMembre`cua-- notació infix amb cometes revesses
les classes de tipus són com un mòdul genèric, amb el tipus com a paràmetre o índex, que defineix la signatura de les operacions on intervé el tipus indexat.[23]
Derivació automàtica d'instàncies (implementacions d'interfícies): També podem demanar al compilador que, per a classes de tipus bàsiques, derivi una instància partint de la representació interna del tipus (clàusula deriving en la definició de tipus estructurals (clàusula data).
dataColor=Verd|Blau|Liladeriving(Eq,Show)-- el compilador deriva instàncies de les classes esmentades, obtenint la posició i el nom dels valors
Els literals no tenen tipus associat sinó que poden pertànyer a aquells tipus que implementin una determinada classe:
$ghci-- a l'intèrpretPrelude>:t11::Numa=>a-- 1 pot assignar-se a vars. d'aquells tipus que implementin la classe Num (que correspon a l'estructura algebraica d'anell)Prelude>:t1.51.5::Fractionala=>a-- 1.5 pot assignar-se a vars. d'aquells tipus que implementin la classe Fractional (que correspon a l'estructura algebraica de cos)-- la clàusula "default" permet l'assignació de tipus tradicional als literals, esmentant una seqüència de tipus a provardefault(Int,Double)
{-# LANGUAGE OverloadedStrings, OverloadedLists #-}importData.Semigroup((<>))importData.Text(Text)-- text com a vectors de UTF-16importData.Set(Set)importData.Map(Map)tiraDeText=("abc"::Text)<>"def"llista=([1..3]::[Int])<>[4..6]cjt=([1..3]::SetInt)<>[4..6]dicc=([(1,"a"),(2,"b")]::MapIntString)<>[(3,"c")]
Tipus derivats: Corresponent a la derivació de classes de la P.O.O. Haskell possibilita la derivació de tipus amb la declaració newtype, heretant del tipus base les implementacions d'aquelles classes de tipus que esmentem a la clàusula deriving. Caldrà l'extensió de llenguatge GeneralizedNewtypeDeriving.[24]
Les implementacions no tenen nom. Un tipus no pot tenir diverses implementacions d'una classe de tipus, per ex. diverses ordenacions o diversos monoides per un mateix tipus. Caldrà crear-ne un tipus derivat amb newtype per cadascuna de les implementacions.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}-- amplia les instàncies heretables del Haskell98-- tipus derivat per implementar un Monoide per a la suma, parametritzat pel tipus basenewtypeSuma=Sum{getSum::a}deriving(Eq,Ord,Read,Show,Bounded,Generic,Generic1,Num)-- classes de les instàncies a heretar-- el constructor obté, del tipus base, el tipus derivat-- l'accessor obté, del tipus derivat, el tipus baseinstanceNuma=>Monoid(Suma)where-- per aquells `a` que implementin Num (un anell algebraic)mempty=Sum0-- element neutre del MonoideSumx`mappend`Sumy=Sum(x+y)-- operació associativa
Les col·leccions heterogènies d'altres llenguatges, aquí es tipifiquen per les operacions requerides, mitjançant tipus existencials (aquells tipus quins components implementin les classes de tipus requerides per al seu tractament).
{-# LANGUAGE ExistentialQuantification #-}llistaHomo::[Int]llistaHomo=[1,3,4]-- llista homogènia-- tipus "existencial": amb un component de tipus variable que compleixi una restricciódataObjPresentable=foralla.(Showa)=>Obja-- per aquells tipus 'a' tals que "(Show a)"presentaObj::ObjPresentable->StringpresentaObj(Objx)="Obj "++showx-- llista d'elements amb components heterogenis, -- als quals podrem aplicar les operacions de les classes especificades a la restricció llistaHetero::[ObjPresentable]llistaHetero=[Obj1,Obj"abc"]
El control de les operacions sobre els elements dels contenidors es pot fer de diverses maneres:
Mapeig: Si el contenidor implementa un Functor podrem estalviar múltiples recorreguts dels contenidors en aplicar diversos morfismes als elements, perquè per les lleis del Functor l'aplicació de la composició serà equivalent a la composició de les aplicacions individuals de cadascun dels morfismes. Els conjunts no són Functor
Si no hi ha efectes laterals
Els catamorfismes o plegaments són operacions que redueixen una col·lecció de valors a un de sol, altrament dit, una gramàtica A* -> A, a la classe Data.Foldable.[25]
Inversament, els anamorfismes[26] son operacions de desplegament que partint d'un valor anomenat llavor genera una col·lecció A -> A*. No tenen una classe específica. Es descriuen als mòduls de cada col·lecció.
Un Hylomorfisme és l'aplicació consecutiva d'un anamorfisme seguida d'un catamorfisme. Per exemple el càlcul del factorial desplega una seqüència de crides que és plegada en un valor final.
Un Metamorfisme és l'aplicació consecutiva d'un catamorfisme seguida d'un anamorfisme.
Un Paramorfisme és l'extensió del concepte de catamorfisme. Modela la recursió sobre un tipus de dades inductiu.
Un Apomorfisme és l'extensió del concepte d'anamorfisme, modelant la corecursió sobre un tipus coinductiu.
Quan hi ha efectes laterals cal controlar la seqüència de les operacions
la classe Traversable facilita l'avaluació seqüencial dels elements d'un contenidor d'accions o bé del mapeig dels elements amb una funció d'efectes.[27]
L'avaluació d'accions es pot fer de tres maneres possibles
Per combinació de resultats (les accions es podrien paral·lelitzar):
Els Functors aplicatius permeten combinar els resultats d'un seguit d'accions sense restringir-ne la temporalitat i componen les accions aplicant un resultat combinador de la primera, al resultat de l'acció següent. L'obtenció d'un resultat combinador es pot fer mitjançant l'aplicació d'un combinador de més d'un paràmetre, amb `fmap` a la primera acció, o també, elevant el combinador com a valor amb `pure` al tipus de l'efecte.
Per encadenament de resultats (serialització temporal):
Les Mònades componen les accions per encadenament del resultat doncs la segona acció pren forma de funció (amb efectes laterals) sobre el resultat de la precedent Hi ha una sintaxi específica (els blocs do) per descriure la composició de manera imperativa.
Les Fletxes generalitzen les funcions d'efectes laterals de les mònades, i componen les accions com un encadenament de resultats d'efectes funció d'una entrada que aplicarem a un valor inicial. Hi ha una sintaxi específica (els blocs proc) per descriure'n la composició.
Mònades amb efectes laterals funcionals
La mònada Reader possibilita l'encapsulament d'una funció d'un entorn que podem consultar (ask) i executar localment (amb local) una acció subordinada passant-li l'entorn modificat. L'entorn l'haurem de proporcionar inicialment en executar l'acció.[28]
La mònada Writer possibilita l'encapsulament d'una sortida incrementable com a Monoide que es pot consultar (listen), actualitzar (tell) i incrementar (censor).[29]
La mònada State possibilita l'encapsulament d'un estat que podrem consultar (get) i actualitzar (put).[30]
afegint paràmetres de tipus a les classes de tipus,
o bé, cas de tipus dependents, definint-los com a tipus associats a la classe
o bé mitjançant #Famílies de tipus (especificació a nivell global d'un tipus dependent quan es vol comú a diferents classes, per exemple el tipus de l'Element per a diferents classes de contenidors).
Haskell destaca en la facilitat per al paral·lelisme, per aprofitar la potència dels processadors multicor.[32][33]
A finals dels anys 1980 es va constituir un comitè amb l'objectiu de recollir en un llenguatge les característiques dels múltiples llenguatges funcionals de l'època, Miranda, ML i altres.
La primera versió va sortir el 1990. La versió més estesa actualment és la que correspon a l'informe Haskell 98.[34] Tanmateix el compilador GHC incorpora l'estàndard Haskell2010 per defecte a partir de la versió 7.0[35]
A principi de 2006 va començar el procés per definir-ne una nova revisió amb el nom de Haskell' ("Haskell prima", ang:"Haskell prime").[36] Diversos compiladors incorporen extensions del llenguatge que per a fer-les servir caldrà precedir el codi font amb la pragma{-# LANGUAGE extensió1, extensió2, ... #-} o bé el senyal corresp. de compilació (-Xextensió per al GHC). El wiki de "Haskell Prima" esmenta per cada extensió els compiladors que la implementen.[37]
Haskell 2010:[38] Actualment ha finalitzat el procés de discussió de les incorporacions a la nova versió de l'estàndard,[39][40] així com un nou procés d'evolució amb revisions (bi-)anuals del llenguatge.[41]
Haskell té un desavantatge important en la dificultat de depuració, que obliga a un esforç especial en la prevenció de fallades:
El model d'execució de Haskell fa que no hi hagi traça de pila de crides. Però se n'ha desenvolupat una de simulada per quan es compila per l'ajustatge (profiling). Per aquest cas, cal disposar de compilacions per l'ajustatge de totes les biblioteques relligades.[43][44][45]
Les petades per crides a error de les funcions parcials (proveu head []) donen, en compilació de producció, informació molt escassa: ni situació de l'error (a partir de GHC 8.0 error ja dona la posició, excepte al paquet base on s'ha mantingut la versió antiga, reanomenada errorWithoutStackTrace), ni la de la crida que incompleix la precondició. Per això es recomana no utilitzar-les en funcions parcials i convertir-les en totals amb resultat opcional caçant el cas no previst en l'anàlisi del retorn. Recentment des de GHC 7.10.2 hi ha la possibilitat d'obtenir el punt de crida d'origen mitjançant paràmetres implícits especials (Vegeu exemple). Alternativa més segura: evitar les funcions parcials (La biblioteca Safe ofereix alternatives a les funcions parcials predefinides del Prelude; el mòdul Data.List.NonEmpty ofereix llistes no buides com a tipus NonEmpty per aplicar de manera segura les funcions que petarien sobre llistes buides, per ex.: head).[46]
Els errors en funcions parcials de col·leccions no imprimeixen els valors causants, perquè la textualització dels elements (Show elem) no s'exigeix.
L'ús de traces per depurar es revela un maldecap per la irregularitat en l'ordre d'impressió que segueix l'ordre d'execució i no el d'especificació, indeterminat (per tant aleatori) en codi funcional pur, subjecte a l'avaluació tardana en traces en expressions let del codi monàdic (seqüencial).
El pas de paràmetres en les crides recursives (aval. tardana per defecte) pot fer petar la pila (pila d'avaluacions pendents) mentre no s'arriba al cas simple no-recursiu, si no s'avaluen de manera estricta els paràmetres acumuladors de manera completa, en profunditat (#Modes d'avaluació d'expressions i #Avaluació estricta explícita).
S'ha criticat que no hi hagi un sistema d'extensió de registres[47] i/o especialització. A l'intèrpret Hugs se'n va desenvolupar un anomenat TRex[48] (exemple) que GHC no ha incorporat.
En realitat es pot aconseguir facilitat en l'especialització de comportament, desacoblant la funcionalitat de l'estructura amb classes de propietats (getters/setters) com es descriu a l'Encapsulament estil O.O..
La implementació recent de polimorfisme de registres amb camps específics permet especificar els accessors com a requeriment de context, mitjançant la classe HasField (GHC 8.2), estalviant definir classes getters per aconseguir el polimorfisme (exemple verificat a #Polimorfisme de registres amb camps específics - la classe HasField).[49][50][51]
Les referències funcionals, anomenades lents, permeten l'actualització funcional d'estructures complexes en els seus components com un tot, reduint la necessitat de dividir la funció en els mòduls corresponents als components afectats com faríem a la POO.
↑Syme, Don; Granicz, Adam; Cisternino, Antonio. Expert F#. Apress, 2007, p. 2. «F# also draws from Haskell particularly with regard to two advanced language features called sequence expressions and workflows.»
↑Lattner, Chris. «Chris Lattner's Homepage». Chris Lattner, 03-06-2014. [Consulta: 3 juny 2014]. «The Swift language is the product of tireless effort from a team of language experts, documentation gurus, compiler optimization ninjas, and an incredibly important internal dogfooding group who provided feedback to help refine and battle-test ideas. Of course, it also greatly benefited from the experiences hard-won by many other languages in the field, drawing ideas from Objective-C, Rust, Haskell, Ruby, Python, C#, CLU, and far too many others to list.»