Haskell

Per a altres significats, vegeu «Haskell (desambiguació)».
Infotaula de llenguatge de programacióHaskell
Tipusllenguatge de programació purament funcional, llenguatge de programació no estricte, llenguatge de programació modular, llenguatge interpretat, llenguatge de regles fora de joc, Llenguatge de programació compilat i llenguatge de programació Modifica el valor a Wikidata
Data de creació1990; fa 34 anys (1990)
DissenyLennart Augustsson, Warren Burton, Kevin Hammond, Paul Hudak, John Hughes, Thomas Johnsson, Simon Peyton Jones, John Launchbury, Erik Meijer, Alastair Reid i Philip Wadler Modifica el valor a Wikidata
DesenvolupadorPaul Hudak, Lennart Augustsson, John Hughes, Simon Peyton Jones, Erik Meijer i Philip Wadler Modifica el valor a Wikidata
EpònimHaskell Curry Modifica el valor a Wikidata
Paradigma de programacióprogramació funcional estandarditzat de semàntica no estricta i avaluació tardana
Darrera versió estableHaskell 2010[1]
Majors implementacionsGHC, Hugs, NHC, JHC, Yhc, UHC
DialectesHelium, Gofer
Influenciat perClean,[2] FP,[2] Gofer,[2] Hope and Hope+,[2] Id,[2] ISWIM,[2] KRC,[2] Lisp,[2] Miranda,[2] ML and ML Estàndard,[2] Orwell, SASL,[2] SISAL,[2] Scheme[2]
Ha influenciatAgda,[3] Bluespec,[4] C++11/Concepts,[5] C#/LINQ,[6][7][8][9] Cayenne,[6] Clean,[6] Clojure,[10] CoffeeScript,[11] Curry,[6] Escher,[12] F#,[13] Frege,[14] Hack,[15] Idris,[16] Isabelle,[6] Java/Generics,[6] LiveScript,[17] Mercury,[6] Perl 6,[18] Python,[6][19] Scala,[6][20] Swift,[21] Timber,[22] Visual Basic 9.0[6][7]
Sistema operatiumultiplataforma
Extensió dels fitxers.hs, .lhs
Etiqueta d'Stack ExchangeEtiqueta Modifica el valor a Wikidata
Pàgina webhaskell.org

Haskell és un llenguatge de programació funcional estandarditzat de semàntica no estricta i avaluació tardana de les expressions (ang: lazy evaluation) en el moment que se'n demana el valor i pren el nom del matemàtic Haskell Curry.

Es diu que és un llenguatge funcional pur. El cert és que admet variables d'estat però permet encapsular-ne els canvis (context ST), o bé circumscriure'n els efectes col·laterals al nivell superficial (context IO).

Haskell basa el polimorfisme en el requeriment d'implementació d'interfícies pels tipus dels paràmetres. Les interfícies amb un paràmetre de tipus t defineixen una partició de l'espai dels tipus en classes segons si les implementen o no, i per això s'anomenen classes de tipus.

-- classe de tipus (la interfície)
class Eq t where
 (==) :: t -> t -> Bool -- iguals
 (/=) :: t -> t -> Bool -- desiguals
 -- implementació per defecte
 x == y = not (x /= y)
 x /= y = not (x == y)
 -- caldrà especificar només una de les operacions en definir la implementació

data Bool = False | True -- definició del tipus Bool

-- instància (la implementació) de la classe Eq per al tipus Bool
instance Eq Bool where
 (==) False False = True
 (==) True True = True
 (==) _ _ = False

-- definició del tipus (Llista a) = Nil | Cons a (Llista a)
-- els símbols '[' i ']' designen una llista
data [a] = [] -- el constructor Nil es denota amb "[]"
 | a : [a] -- el constructor Cons es denota amb ':' en posició infix
-- * per sintaxi, si un símbol comença per ':', va infix
-- * tot identificador de funció es pot posar en infix si s'envolta de cometes revesses

-- exemple d'ús: "Eq t =>" es llegeix: per aquells tipus t tals que (Eq t)
ésMembre :: Eq t => t -> [t] -> Bool -- 
ésMembre x [] = False 
ésMembre x (cap : cua) = x == cap 
 || x `ésMembre` cua -- notació infix amb cometes revesses
  • 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).

data Color = Verd | Blau | Lila deriving (Eq, Show) -- el compilador deriva instàncies de les classes esmentades, obtenint la posició i el nom dels valors

Els literals no tenen tipus associat sinó que poden pertànyer a aquells tipus que implementin una determinada classe:

$ ghci -- a l'intèrpret
Prelude> :t 1
1 :: Num a => a -- 1 pot assignar-se a vars. d'aquells tipus que implementin la classe Num (que correspon a l'estructura algebraica d'anell)
Prelude> :t 1.5
1.5 :: Fractional a => a -- 1.5 pot assignar-se a vars. d'aquells tipus que implementin la classe Fractional (que correspon a l'estructura algebraica de cos)

-- la clàusula "default" permet l'assignació de tipus tradicional als literals, esmentant una seqüència de tipus a provar
default (Int, Double)

La sobrecàrrega de literals fa possible incorporar-los a diferents estructures:

{-# LANGUAGE OverloadedStrings, OverloadedLists #-}
import Data.Semigroup((<>))
import Data.Text (Text) -- text com a vectors de UTF-16
import Data.Set (Set)
import Data.Map (Map)

tiraDeText = ("abc" :: Text) <> "def" 
llista = ([1..3] :: [Int]) <> [4..6]
cjt = ([1..3] :: Set Int) <> [4..6]
dicc = ([(1,"a"), (2, "b")] :: Map Int String) <> [(3,"c")]

Tipus derivats: Corresponent a la derivació de classes de la P.O.O. Haskell possibilita la derivació de tipus amb la declaració newtype, heretant del tipus base les implementacions d'aquelles classes de tipus que esmentem a la clàusula deriving. Caldrà l'extensió de llenguatge GeneralizedNewtypeDeriving.[24]

Les implementacions no tenen nom. Un tipus no pot tenir diverses implementacions d'una classe de tipus, per ex. diverses ordenacions o diversos monoides per un mateix tipus. Caldrà crear-ne un tipus derivat amb newtype per cadascuna de les implementacions.

{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- amplia les instàncies heretables del Haskell98

-- tipus derivat per implementar un Monoide per a la suma, parametritzat pel tipus base
newtype Sum a = Sum { getSum :: a }
 deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num) -- classes de les instàncies a heretar
 -- el constructor obté, del tipus base, el tipus derivat
 -- l'accessor obté, del tipus derivat, el tipus base

instance Num a => Monoid (Sum a) where -- per aquells `a` que implementin Num (un anell algebraic)
 mempty = Sum 0 -- element neutre del Monoide
 Sum x `mappend` Sum y = Sum (x + y) -- operació associativa

Les col·leccions heterogènies d'altres llenguatges, aquí es tipifiquen per les operacions requerides, mitjançant tipus existencials (aquells tipus quins components implementin les classes de tipus requerides per al seu tractament).

{-# LANGUAGE ExistentialQuantification #-}

llistaHomo :: [Int]
llistaHomo = [1, 3, 4] -- llista homogènia

-- tipus "existencial": amb un component de tipus variable que compleixi una restricció
data ObjPresentable = forall a. (Show a) => Obj a -- per aquells tipus 'a' tals que "(Show a)"

presentaObj :: ObjPresentable -> String
presentaObj (Obj x) = "Obj " ++ show x 

-- llista d'elements amb components heterogenis, 
-- als quals podrem aplicar les operacions de les classes especificades a la restricció 
llistaHetero :: [ObjPresentable]
llistaHetero = [Obj 1, Obj "abc"]

El control de les operacions sobre els elements dels contenidors es pot fer de diverses maneres:

  • 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]
  • Els transformadors de mònades possibiliten l'encapsulament de la funcionalitat de diverses mònades.[31]
Programació genèrica

La programació genèrica, parametritzada per tipus, es concreta o bé

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

Propostes d'evolució del llenguatge aquí.[42]

Crítiques:

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

Problemàtiques:

Vegeu secció #Problemàtiques.
  1. Marlow, Simon. «Announcing Haskell 2010», 24-11-2009. [Consulta: 12 març 2011].
  2. 2,00 2,01 2,02 2,03 2,04 2,05 2,06 2,07 2,08 2,09 2,10 2,11 2,12 Peyton Jones 2003, p. xi
  3. Norell, Ulf. «Dependently Typed Programming in Agda». Gothenburg: Chalmers University, 2008. [Consulta: 9 febrer 2012].
  4. Hudak et al., 2007, p. 12-38,43.
  5. Stroustrup, Bjarne; Sutton, Andrew. Design of Concept Libraries for C++, 2011.  «Còpia arxivada». Arxivat de l'original el 2012-02-10. [Consulta: 13 novembre 2015].
  6. 6,00 6,01 6,02 6,03 6,04 6,05 6,06 6,07 6,08 6,09 Hudak et al., 2007, p. 12-45–46.
  7. 7,0 7,1 Meijer, Erik «Confessions of a Used Programming Language Salesman: Getting the Masses Hooked on Haskell». OOPSLA 2007.
  8. Meijer, Erik. «C9 Lectures: Dr. Erik Meijer – Functional Programming Fundamentals, Chapter 1 of 13». Channel 9. Microsoft, 01-10-2009. Arxivat de l'original el 16 de juny 2012. [Consulta: 9 febrer 2012].
  9. Drobi, Sadek «Erik Meijer on LINQ». InfoQ. C4Media Inc. [QCon SF 2008], 04-03-2009 [Consulta: 9 febrer 2012].
  10. Hickey, Rich. «Clojure Bookshelf». Listmania!. Amazon.com. [Consulta: 9 febrer 2012].
  11. Heller, Martin «Turn up your nose at Dart and smell the CoffeeScript». JavaWorld. InfoWorld, 18-10-2011 [Consulta: 9 febrer 2012]. Arxivat 10 de febrer 2012 a Wayback Machine. «Còpia arxivada». Arxivat de l'original el 2012-02-10. [Consulta: 13 novembre 2015].
  12. «Declarative programming in Escher». [Consulta: 7 octubre 2015].
  13. 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 
  14. Wechsung, Ingo. «The Frege Programming Language». [Consulta: 26 febrer 2014].
  15. [enllaç sense format] http://www.wired.com/2014/03/facebook-hack/
  16. «Idris, a dependently typed language». [Consulta: 26 octubre 2014].
  17. «LiveScript Inspiration». [Consulta: 4 febrer 2014].
  18. «Glossary of Terms and Jargon». Perl Foundation Perl 6 Wiki. The Perl Foundation, 28 February. Arxivat de l'original el 21 de gener 2012. [Consulta: 9 febrer 2012].
  19. Kuchling, A. M. «Functional Programming HOWTO». Python v2.7.2 documentation. Python Software Foundation. [Consulta: 9 febrer 2012].
  20. Fogus, Michael. «MartinOdersky take(5) toList». Send More Paramedics, 06-08-2010. [Consulta: 9 febrer 2012].
  21. 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.»
  22. «Timber/History». [Consulta: 7 octubre 2015].
  23. HaskellWiki - Polimorfisme(anglès)
  24. GHC Users guide - GeneralizedNewtypeDeriving Arxivat 2018-08-21 a Wayback Machine.(anglès)
  25. La classe Foldable(anglès)
  26. Erik Meijer et al. - Functional programming with Bananas, Lenses, Envelopes and Barbed Wire(anglès)
  27. La classe Traversable(anglès)
  28. La mònada Reader(anglès)
  29. La mònada Writer(anglès)
  30. La mònada State(anglès)
  31. Monad transformers(anglès)
  32. [Parallel Haskell: Projecte de dos anys per promocionar-ne l'ús en el món real](anglès)
  33. Haskell - Concurrència i paral·lelisme(anglès)
  34. Haskell98 Online Report(anglès)
  35. GHC - Notes de l'edició(anglès) GHC 7.0+ pren per defecte l'estàndard de llenguatge Haskell2010
  36. Haskell prima - Propostes per a l'evolució del llenguatge Arxivat 2016-02-20 a Wayback Machine. (anglès)
  37. Extensions del Haskell i compiladors que les implementen Arxivat 2019-12-21 a Wayback Machine.(anglès)
  38. Haskell2010 Online Report(anglès)
  39. Anunciant Haskell 2010(anglès) Acord final sobre Haskell 2010
  40. Característiques del Haskell 2010 Arxivat 2009-11-30 a Wayback Machine. (anglès)
  41. Anunci del nou procés Haskell Prima, i del Haskell 2010 (anglès)
  42. Propostes per a Haskell 201x Arxivat 2018-01-25 a Wayback Machine.(anglès)
  43. Haskell wiki - Depuració(anglès)
  44. HaskellWiki - Stack Overflow(anglès)
  45. Simon Marlow - Why can't I get a Stack trace(anglès)
  46. El paquet safe(anglès)
  47. Extensible Records(anglès)
  48. «Extensió TRex de l'intèrpret Hugs». Arxivat de l'original el 2012-03-20. [Consulta: 12 març 2012].
  49. Well-typed - Polymorphism over record fields(anglès)
  50. OverloadedRecordFields(anglès)
  51. Implement HasField constraint solving and modify OverloadedLabels Arxivat 2018-01-25 a Wayback Machine.(anglès)