Compilador Haskell de Glasgow

De Viquipèdia
Dreceres ràpides: navegació, cerca

ghc (ordre d'ordinador que respon a la sigla de "Glasgow Haskell Compiler", en català Compilador Haskell de Glasgow) és un compilador nadiu de codi lliure per al llenguatge de programació funcional Haskell, el qual va ser originalment desenvolupat a la universitat de Glasgow per Simon Peyton Jones i Simon Marlow. El desenvolupament continua actualment sota l'auspici del Departament d'Investigació de Microsoft (Microsoft Research), al Regne unit on treballa Peyton Jones. GHC ha pres humorísticament el sobrenom "Glorious Haskell Compiler".

El nom ghc segueix el costum antic d'anomenar les ordres amb un identificador curt d'una a tres lletres, on la 'c' designa compilador, la 'h' designa el llenguatge i la primera lletra, la ciutat de la universitat que el desenvolupà originàriament (com també ho fa mmc: "Compilador Mercury de Melbourne",[1] o bé yhc:"Compilador Haskell de York"[2] o bé uhc:"Compilador Haskell d'Utrecht").[3]

El compilador també està escrit en Haskell (una tècnica coneguda com a bootstrapping), però el nucli de sistema per a Haskell està escrit en C i una adaptació del C--[4][5][6] referida com a Cmm.[7] L'última versió del compilador compleix amb l'estàndard més nou del llenguatge, que és, ara per ara, el Haskell 2010.[8] GHC està disponible per a moltes plataformes, incloent Windows i la majoria de sistemes Unix (com les diferents distribucions de GNU/Linux), Mac OS X i la majoria d'arquitectures de processador.

Aquest incloïa, fins a la versió 6.10.x, la majoria d'extensions al Haskell estàndard, fins i tot la biblioteca STM, que serveix per fer transaccions atòmiques (o tot o no res) de dades compartides entre diferents fils d'execució.

A partir de GHC 6.12 les biblioteques ja no vénen amb el compilador sinó que es distribueixen en edicions diferides, en la Plataforma Haskell ("The Haskell Platform").[9][10] La corresponent a GHC 6.12.1 ja està disponible. Entre altres millores, suporta tractament d'entrada/sortida per a caràcters no anglosaxons, segons la codificació del sistema subjacent[11]

Taula de continguts

Gestió de memòria[modifica | modifica el codi]

Vegeu doc.[12] GHC compila per defecte amb memòria dinàmica il·limitada. Per establir-ne límits vegeu el senyal de compilació -M.[13] Disposa d'un recollidor de memòria brossa generacional (per defecte dues gen.), amb control d'envelliment (nombre de recol·leccions abans de passar a la generació següent) de dos passos per generació.[14]

L'allotjament es fa dins de blocs reutilitzables, partions d'un megabloc que és el tros que es reclama al sistema quan cal i que només li retornen en havent acabat.[15]

Megabloc
bloc d'un megabyte, que l'allotjador de memòria demana de cop.
Capability 
fil d'execució lligat a un del sistema, un per cada processador elemental, sobre els quals s'executen els fils d'execució lleugers del planificador del Run Time System.
Nursery (maternitat)
zona d'allotjament de dades noves (una per capability/P.E.), del recollidor de memòria brossa generacional.

A l'inici les dades s'allotgen a la maternitat (ang:nursery) (conjunt fix de blocs) i se'n disposa d'una per capability.[16] després passen a la generació 0, i següents[17] tantes com s'estableixi a les opcions de l'intèrpret d'ordres per al RunTimeSystem.

Per a la generació més vella, es pot triar el sistema de recollida de la brossa, entre el mètode de Cheney (stop and copy de dos espais) (per defecte), el mètode de compactació (senyal -c) (estalvia espai però és més lent). Les altres generacions fan servir el mètode de Cheney.[18]

  • Per forçar la recollida de brossa quan convingui: System.Mem.performGC[19]
  • Referències dèbils (ang:weak pointer) per a taules de memoïtzació[20][21] i també per a MVar's (Mutable variable) amb mkWeakMVar.[22]

Fent servir biblioteques de relligat dinàmic[modifica | modifica el codi]

A partir de GHC >= 6.12[23][24][25] Cal que GHC s'hagi compilat amb./configure --enable-shared

# relliga amb les versions compartides (no estàtiques) (bib.so o bé bib.dll segons sistema op.) del RunTimeSystem i dels paquets

ghc --make -dynamic Main.hs

Paral·lelisme[modifica | modifica el codi]

Novetats al bloc butlletí "Parallel Haskell Digest"[26]

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

Vegeu[27]

par

par:: a → b → b -- activa el càlcul del primer operand en paral·lel (que s'encua en espera d'una CPU disponible) mentre que el segon s'executa al fil d'exec. actual, retornant el resultat d'aquest darrer.[28]

pseq

pseq:: a → b → b -- avalua el primer operand en el fil d'exec. actual, de manera primerenca (estricta) i avalua el segon de manera tardana (lazy) quin resultat retorna. Vegeu "seq vs. pseq"[29][30]

opció multiprocessador

L'opció -threaded de "ghc --make" relliga el programa amb la biblio. del Run Time System multiprocessador, emprant diversos fils d'execució del sistema per possibilitar el paral·lelisme, altrament el relliga amb la de l'RTS uniprocessador.[31]

GHC conté un planificador de fils d'execució lleugers, llançats amb forkIO, que s'executen amb relació "M a N" sobre fils d'execució del sistema (1 per cada processador elemental) anomenats Capability.[32]

{-# LANGUAGE PackageImports #-}
 
 import "parallel" Control.Parallel (par, pseq)
 
 parfib :: IntInteger
 parfib 0 = 0
 parfib 1 = 1
 parfib n | n > 1 = nf2 `par` (nf1 `pseq` (nf1+nf2)) -- calc nf2 en un fil en paral·lel i nf1 al fil principal
                                            --   i seqüencialment, al fil principal,
                                            --   en acabar en retorna la suma
           where nf1 = parfib (n-1)
                 nf2 = parfib (n-2)
 
 main = print $ parfib 10
-- compilació amb -threaded per fer servir la biblio. "Run Time System multi-processador"
-- afegir -rtsopts per poder afegir paràmetres al llançador per a l'R.T.S.
ghc --make -threaded -rtsopts parfib.hs

-- execució mostrant estadístiques  "+RTS -s". Afegirem -Nx per un nombre x de processadors elementals.

-- "si el user time (temps en mode usuari) és major que l'elapsed time (temps transcorregut)
--     és que s'ha emprat més d'un processador[33]
-- proveu-ho també sense -Nx

-- per distingir les opcions d'execució específiques per al Haskell Run Time System de les del programa, 
--    cal escriure els params. per al Haskell després de +RTS 
--                        i tancar amb -RTS si volem afegir params. per al programa.

./parfib +RTS -s -N2

Estratègies[modifica | modifica el codi]

Les estratègies són funcions de coordinació de l'execució.[34][35] Vegeu també[36][37]

La mònada Eval.[34]

data Eval a = Done { runEval :: a }
 
-- aquest tipus genera els operadors inversos (constructor i accessor) dels registres d'un sol component:
-- Done    :: a → Eval a
-- runEval :: Eval a → a
 
-- instanciem la classe mònada sobre el tipus ''Eval a'' per seqüenciar les operacions
instance Monad Eval where
  return x = Done x
  Done x >>= f = f x
 
-- elevem ''par'' i ''pseq'' al tipus de la mònada amb les funcions rpar i rseq
rseq, rpar :: a → Eval a
rseq x = x `pseq` return x
rpar x = x `par` return x
 
-- reescribint nfib
nfib :: IntInt
nfib n | n <= 1    = 1
       | otherwise = runEval $ do
                         x <- rpar (nfib (n-1))
                         y <- rseq (nfib (n-2))
                         return (x + y)
-----
-- ara, al tipus de ''rpar'' i ''rseq'' l'anomenarem "estratègia"
 
type Strategy a = a → Eval a
 
-- avalua amb l'estratègia 
withStrategy :: Strategy a → a → a
withStrategy strat e = runEval (strat e)
 
-- using és withStrategy amb els param. canviats, per a ésser emprat com a op. infix
using == flip withStrategy
 
-- composició seqüencial d'estratègies
dot :: Strategy a → Strategy a → Strategy a
-- strat2 `dot` strat1 == strat2. withStrategy strat1

Estratègies típiques:[35]

r0  -- estratègia buida (no avalua res), elem. neutre de la combinació ''dot''
rseq  -- avalua a WHNF (Weak Head Normal Form)
rdeepseq -- avalua a Forma Normal
rpar -- encua l'avaluació per l'execució en paral·lel (''spark'')
----
evalList :: Strategy a → Strategy [a]   -- estratègia per aplicar a una llista l'estratègia del primer arg.
parList :: Strategy a → Strategy [a]   -- estratègia per aplicar a una llista, paral·lelitzant, l'estratègia del primer arg.
  • parMap estrat: combinador típic, aplica map f i avalua la llista resultant amb l'estratègia parList estrat
 !no s'ha d'utilitzar amb rpar, ja que activaria l'exec. paraleŀla doblement per cada elem.
parMap estrat f = withStrategy (parList estrat). map f

Càlcul típic amb estratègies.

{-# LANGUAGE PackageImports #-}
import "parallel" Control.Parallel.Strategies (parMap, rpar, rseq)
 
-- parMap :: Strategy b → (a → b) → [a] → [b]
-- parMap strat f = (`using` parList strat). map f
 
càlcul x = 2 * sqrt x
 
mapejaEnParaŀlel = parMap rseq    -- avalua en paral·lel a WHNF (per rseq)
 
resultatsDelCàlculEnParaŀlel = mapejaEnParal·lel càlcul (llista 2)
  where llista n = [n..1100000] :: [Double]
 
main = print $ sum resultatsDelCàlculEnParaŀlel

compilació i exec.

# cal compilar amb "-rtsopts" per poder afegir opcions del RunTimeSystem en temps d'execució
ghc --make -threaded -rtsopts prova.hs
 
#   opcions: estadístiques: -s, 
#            utilitza tots els nuclis de CPU: -N, 
#            pila de 100MB: -K100m
./prova +RTS -K100m -s -N

Exemple: #Reducció en paral·lel mitjançant estratègies.

Futurs[modifica | modifica el codi]

Encadenament de càlculs asíncrons - la mònada Par[modifica | modifica el codi]

Vegeu-ho a Haskell concurrent#Futurs -- Encadenament de càlculs asíncrons - la mònada Par

Paral·lelisme en operacions IO - El functor aplicatiu Concurrently[modifica | modifica el codi]

Vegeu-ho a Haskell concurrent#Operacions d'Entrada/Sortida asíncrones i simultànies

Paral·lelisme de dades[modifica | modifica el codi]

Regular parallel arrays[modifica | modifica el codi]

Un primer nivell de tractament paral·lel s'ofereix mitjançant el paquet Repa ("Regular parallel arrays")[38][39] de tractament en paral·lel de vectors pluridimensionals. Exemple més avall.

Data Parallel Haskell[modifica | modifica el codi]

GHC implementa el, més complex, paral·lelisme de dades niuat, trad. de "Nested Data Parallelism"[42][43][44] basat en treballs de Guy E. Blelloch.[45]

Per a proves cal instal·lar el paquet dph-examples.

cabal install dph-examples
 
#si la versió de GHC és la 7.4.x i dóna error en compilar el paquet ''bmp'', 
#    caldrà provar forçant la versió anterior a la que peta.
cabal install dph-examples "--constraint= bmp < 1.2.3.1"

Cal fixar-se en la versió instal·lada (diferent segons la versió de GHC) i descarregar-ne les fonts per investigar.

# per obtenir-ne la versió instaŀlada
ghc-pkg list | grep dph-examples

Per desenvolupar-hi cal tenir en compte que...

  • Cal separar les operacions per mòduls segons si són o no vectoritzades
  • A la interfície el tipus ha de ser (PArray t)
  • El tipus vector ([: t :]) és NO-polimòrfic i cal especificar el tipus de l'element, com ara [: Double :], les operacions sobre els tipus primitius no estan sobrecarregades i per cada tipus cal importar-ne el mòdul d'operacions corresponent (per ex.: Data.Array.Parallel.Prelude.Double).

Vegeu exemple més avall.

  • Pròximament: "SIMD Support to Data Parallel Haskell"[46][41]

Paral·lelisme a la GPU[modifica | modifica el codi]

  • Biblioteca Accelerate amb suport per a rerefons CUDA,[47] OpenCL (en preparació)[48] i RePa[49] (en preparació, per a CPU), i genera nuclis (d'iteracions en paral·lel) per a GPU partint d'un llenguatge incrustat (EDSL) en Haskell (treball en curs)[50]
  • Biblio. OpenCLWrappers facilita l'ús de lligams amb biblioteques externes que implementin l'estàndard OpenCL,[51] article: OpenCL from Haskell[52]
  • Simultanejant càrregues de treball en CPU i GPU. Vegeu "How to write hybrid CPU/GPU programs with Haskell".[53] El paquet meta-par ofereix una versió de la mònada Par (encadenament de càlculs asíncrons) per compartir recursos de hardware, amb un planificador de tasques SMP.[54]

Concurrència[modifica | modifica el codi]

  • MVar's - Mutable Variable
  • forkIO / forkOS
  • Software Transactional Memory
  • Futurs -- Encadenament de càlculs asíncrons - la mònada Par
  • Operacions d'Entrada/Sortida asíncrones i simultànies
  • Cloud Haskell
  • Biblioteca Actor
  • Communicating Haskell Processes (CSP)

Vegeu Haskell concurrent.

Metaprogramació - Template Haskell[modifica | modifica el codi]

Extensió de GHC que permet l'anàlisi i generació de codi o bé Haskell o bé a expressions del tipus algebraic de l'arbre d'operacions (AST) d'un programa, com els generats internament pel compilador de Haskell.[55]

Avaluació en temps de compilació[modifica | modifica el codi]

El prefix $ a un terme o bé a una expressió entre parèntesis, $terme o bé $(expr), s'anomena crida splice (cat.: unir dues parts) i vol dir que cal avaluar l'expressió en temps de compilació. Exemple: #Punts de control al codi amb Loch-TH

Quasi-Quotations[modifica | modifica el codi]

Permeten incrustar texts convertibles a codi, escrits seguint gramàtiques diferents, generant expressions interoperables amb les del mòdul que l'allotja.

Per exemple a la plataforma web Yesod[56] el quasiQuoter shamlet permet incrustar plantilles HTML amb expressions que inclouen símbols vàlids en l'àmbit en què s'incrusta.

Definició literària: Dispositiu lingüístic que facilita la inclusió d'expressions lingüístiques amb variables de substitució facilitant una adequació rigorosa a les regles del llenguatge.[57]

Sintaxi
Les acotacions QuasiQuotation utilitzen una sintaxi denominada Claus d'Oxford [|... |] o bé [quasiQuoter|... |] on el nom del prefix indica el tipus que aporta el traductor del text acotat per les claus.[58]
Context
Una acotació QuasiQuotation pot descriure, segons el lloc on s'utilitza, una expressió, o bé un patró, o bé un tipus, o bé una llista de declaracions a nivell de mòdul
Tipus
Un tipus quasiQuoter es defineix com un registre amb traductors opcionals per cadascun dels 4 possibles contextos en què es pretengui utilitzar assignant als altres el valor undefined.[59]
La seva definició ha d'aportar funcions per la traducció del text a expressions del llenguatge Template Haskell que incorpora combinadors sobre el tipus de dades algebraic de l'arbre d'operacions (AST) de la representació que el compilador, internament obté del programa.[60][61][62]
data QuasiQuoter = QuasiQuoter { 
                       quoteExp  :: String → Q Exp,  -- funció per obtenir-ne una expressió en lleng. TemplateHaskell
                       quotePat  :: String → Q Pat,  -- funció per obtenir-ne un patró en lleng. TH.
                       quoteType :: String → Q Type, -- funció per obtenir-ne un tipus en lleng. TH.
                       quoteDec  :: String → Q [Dec] -- funció per obtenir-ne una llista de declaracions en lleng. TH.
                       }

Exemple senzill: document tot seguit[modifica | modifica el codi]

Traductor per a incloure en expressions, textos multilínia (estil hereDoc o document-tot-seguit), com a literals, amb la funció stringE de T.H. que prefixa els literals String en la representació de l'"arbre sintàctic (AST)" del codi que en fa el compilador.

  • els contextos que no interessen es deixen undefined.
  • l'ús i la definició han d'estar en mòduls separats.
{-# LANGUAGE TemplateHaskell #-}
module ElMeuDocTotSeguit where
import Prelude (undefined)
import Language.Haskell.TH (stringE)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
 
docTotSeguit = QuasiQuoter stringE undefined undefined undefined
-- ús
{-# LANGUAGE QuasiQuotes #-}
module Main where
import ElMeuDocTotSeguit (docTotSeguit)
 
-- format a GHC 7 : 
elMeuTextPluriLínia = [docTotSeguit|text
continuació
|]
 
-- format a GHC 6.12 prefixant el qq. amb $: [$hereDoc|text|]

Exemple: Incrustació de HTML amb expressions avaluables[modifica | modifica el codi]

De les biblioteques del Yesod.[56]

El quasiQuoter shamlet tradueix, a expressions del Template Haskell, un llenguatge de marques afegint automàticament les marques de tancament segons el sagnat.

Les claus #{...} inclouen expressions avaluables quin tipus resultant ha de ser traduïble (implementar la classe Text.Blaze.ToMarkup). Les claus ^{...} inclouen expressions del mateix tipus de la plantilla.

{-# LANGUAGE PackageImports, QuasiQuotes #-}
module Plantilla (html) where
 
-- cabal install hamlet -- el paquet blaze-html queda instal·lat per dependència
 
import "hamlet" Text.Hamlet (Html, shamlet)
import "blaze-html" Text.Blaze.Renderer.String (renderHtml)
 
html ::String
html = renderHtml pàgina
 
nom = "Biel"
títol = "Títol de la pàgina"
 
data TAmic = Amic {amicNom::String, amicEdat:: Int}
elsAmics = [Amic "Joan" 30, Amic "Montse" 40]
 
-- Acotació ''QuasiQuotation'' per al cos de la pàgina
-- amb #{expr} inclou l'avaluació de l'expressió per a tipus que implementin la classe Text.Blaze.ToMarkup
 
cosDeLaPàg :: [TAmic] → Html
cosDeLaPàg amics = [shamlet|
  <p>Hola el meu nom és #{nom}
  $if null amics
     <p>Ho sento, no hi ha amics.
  $else
     <p>Els meus amics són #{length amics}:
     <ul>
       $forall amic <- amics
         <li>#{amicNom amic} que té #{amicEdat amic} anys
|]
 
-- Acotació ''QuasiQuotation'' principal
-- amb ^{expr} inclou una crida a un altre QQ del mateix tipus de resultat.
 
pàgina :: Html
pàgina = [shamlet|
         $doctype 5
         <html>
           <head>
             <title>#{títol}
           <body>^{cosDeLaPàg elsAmics}
|]

Facilitats per l'optimització de fusió[modifica | modifica el codi]

En aplicar una composició sobre una estructura, per ex., (reducció. filtre p'. map f. filtre p).

Malgrat que, en ser un llenguatge d'avaluació tardana, no hi ha la problemàtica de la desforestació, hi ha propostes per fusionar-ne les iteracions.

L'ús de la pragma RULES permet que el compilador, en fase de pre-procés, refaci (reescrigui) operacions de composició.[63]

Stream fusion[modifica | modifica el codi]

Fusió de bucles en la composició d'operacions sobre estructures mitjançant la conversió de l'estructura en un corrent de dades (ang: Stream)[64] i especificant l'operació per cada element del corrent.

El corrent incorpora la possibilitat de tenir elements sense dades, com a conseqüència del filtratge.

Les funcions com ara els filtres s'han d'implementar començant per l'operació de conversió stream sobre l'estructura i acabant per la inversa unstream. En la composició de funcions, l'aparició juxtaposada de les conversions unstream al final d'una funció amb la inversa stream de l'inici de la següent és eliminada pel compilador mitjançant pragmes RULES aportades per la implementació.[65][66]

Vegeu biblioteca Stream-fusion al rebost Hackage[67]

Biblio ràpida[modifica | modifica el codi]

Vegeu l'API del GHC[68] i la de la plataforma[10] Haskell.

contenidors[modifica | modifica el codi]

Vegeu ref.[69]

Recordatori d'algunes operacions
S'esmenten les ops. ometent el paràm. de l'estructura sobre la qual s'aplica. Si va primer, s'esmenta en forma d'aplicació parcial infix (`op` p2 p3..). Ocasionalment s'especifica el tipus del retorn. Respecte als tipus de les funcions, consulteu les refs.

notes[modifica | modifica el codi]

llistes amb optimització de fusió[modifica | modifica el codi]
  • el paquet stream-fusion[67] aporta llistes amb funcions fusionables pel mètode stream fusion.
conjunts / taules associatives[modifica | modifica el codi]
  • Set ε[70] i Map κ ε[71] del paquet "containers" implementen conjunts i diccionaris basats en arbres de cerca binària balancejats amb cost de cerca O(log2 n).
  • IntSet[72] i IntMap ε[73] també de "containers" implementen Set i Map sobre el domini Int amb implementació basada en arbres de prefix (Tries) sobre la seqüència de bits amb cost de cerca, com a màxim el nombre de bits del tipus Int: O(min(n,W)).
  • el paquet hashmap[74] aporta una implementació de conjunts[75] i diccionaris[76] basada en IntSet i IntMap de containers, que requereix una aplicació del domini de les claus al tipus Int (funció resum (de hash en anglès) classe Hashable)[77] i desant al conjunt/diccionari o bé un sol element/parell o bé, en cas de col·lisions, un cistell dels elements/parells quines claus col·lideixen, implementat com a Set/Map del paquet containers. El paquet hashmap ha quedat desaconsellat en favor del paquet unordered-containers.
  • el paquet unordered-containers[78] implementa conjunts[79] i diccionaris[80] per taules de dispersió (HashSet i HashMap) quines claus han d'implementar una funció resum (classe Hashable) però no cal que implementin Ord (ordenable).
seqüències de dos caps, amb accés més ràpid que les llistes[modifica | modifica el codi]
  • Seq ε de Data.Sequence[81] implementa seqüències finites amb dos caps (ang:double ended queue) i accés constant O(1) en afegir i recuperar per ambdós costats. viewl proporciona la vista per l'esquerra i viewr la de la dreta. També ofereix cost d'accés aleatori inferior a les llistes: O(log(min(i,n-i))) per a l'accés indexat; O(log(min(n1,n2))) per la concatenació, i O(1) per la consulta de llargària.
vectors pluridimensionals[modifica | modifica el codi]
  • Array shape ε del paquet Repa[38][82] aporta vectors pluridimensionals d'elements no-encapsulats (unboxed) (allotjats directament)
    • Els elements del Repa han d'implementar
class (Show a, Unbox a) => Elt a
  zero :: a
  one :: a
 ...
    • Els tipus dels índexs del Repa prenen una forma semblant a una "pila de sencers", i implementen la classe Shape:
type DIM0 = Z     -- zero dimensions: escalar
type DIM1 = DIM0 :. Int   -- una dimensió, ex. Z :. 3 
type DIM2 = DIM1 :. Int   -- dues dimensions, ex. Z :. 3 :. 3
...
  • Data.Array.Repa utilitza la terminologia dels llenguatges on l'avaluació tardana és explícita, anomenant vectors diferits (ang:delayed) les aplicacions no-avaluades, per exemple amb map, forçant-ne l'avaluació amb la funció force (com a l'OCaml) que avalua, paral·lelitzant els càlculs.

A ghci:

Prelude>:m +Data.Array.Repa
Prelude Data.Array.Repa> let v22 = fromList (Z :. 2 :. 2) [1,2,3,4] :: Array DIM2 Int
Prelude Data.Array.Repa> let consultaV22 i j = v22 ! (Z :. i :. j)
Prelude Data.Array.Repa> consultaV22 1 0
3
Prelude Data.Array.Repa> extent v22
(Z :. 2) :. 2
Prelude Data.Array.Repa> size $ extent v22
4
Prelude Data.Array.Repa> let w22 = force $ Data.Array.Repa.map (\x → x * 2) v22 
Prelude Data.Array.Repa> w22             -- Data.Array.Repa.force avalua els mapejos en paral·lel
Array (Z :. 2 :. 2) [2,4,6,8]
vectors d'alt rendiment[modifica | modifica el codi]
  • Vector ε i MVector σ ε del paquet Vector[83] aporten vectors immutables i mudables basats en famílies de tipus que permeten implementacions més flexibles, i, a banda, funcions fusionables (stream fusion per la composició).[84][85] Per a vectors de tipus primitius el mòdul Data.Vector.Unboxed aporta vectors immutables i mudables d'elements no encapsulats.[86]
  • hi ha paquets relacionats al Hackage amb funcionalitats diverses
    • vector-algorithms: algoritmes sobre vectors mudables MVector
    • vector-binary-instances: serialització
    • vector-read-instances: instància de Read
    • vector-instances: instàncies de diverses classes
    • ...
altres biblioteques rellevants[modifica | modifica el codi]
  • el paquet dlist[88] aporta llistes per diferència que redueixen el cost de concatenació en cas d'aniuament d'expressions Vegeu ref.[89]
  • el paquet heap[90] aporta seqüències amb prioritat Heap policy item on policy pren valors o bé {Min|Max}Policy per a ítems on la prioritat és el mateix valor (amb àlies del tipus com a {Max|Min}Heap a) o bé Fst{Min|Max}Policy per a ítems parells (prioritat, valor) (amb àlies com a {Max|Min}PrioHeap prioritat valor). L'arbre del Heap porta el tipus HeapT que pot ser buit (constructor Empty) i la classe HeapItem aporta les funcions de conversió entre el parell (Prio policy item, Val policy item) i l'element item.

generació[modifica | modifica el codi]

opcionals - generació
paquet tipus context sense valor
o bé error
amb valor de llista o funció generadora
base Maybe ε[91] Nothing Just x listToMaybe llista
Either tipError ε[92] Left error Right x
seqüències - generació
paquet tipus context buit amb un elem. de llista o funció generadora
base [ε][93] [ ] [x] ll. per comprensió
repeat x
replicate n x
iterate (f::ε→ε) x
cycle llista
unfoldr (f::acc→Maybe(ε,acc)) inicial
containers Seq ε[81] empty singleton x fromList llista
bytestring ByteString[94]
-- UArray Word8
empty singleton x pack llistaDeBytes
text
-- ∈ H. Platform[10]
Text[95]
-- ByteString codif. UTF16
indexat com [Word16][96]
empty singleton ch pack string
classes interfície
base Monoid[97] mempty

Data.List.cycle requereix llista NO-buida.

vectors - generació
paquet tipus context buit amb un elem. de llista o funció generadora
-- associació = (índex, valor)
array Array ι ε[98]
-- immutable amb
allotj. indirecte dels elements
(Ix ι) listArray (iMin, iMax) llista
array (iMin, iMax) llistaD'Associacions
UArray ι ε[99]
-- immutable amb
allotj. directe
-- via interfície IArray
vector
(tipus Índex = Int)
Vector ε[100]
-- immutable amb
allotj. directe
empty singleton x fromList llista
replicate n x
generate llarg (f :: Índex → ε)
MVector σ ε[101]
-- mudable amb
allotj. directe
Vector.thaw ivector -- descongela immutable
replicate n x
replicateM n acció
clone mvector
repa multidim.
Array sh ε[82]
(Shape sh,
Elt ε)
singleton x
  :: Array DIM0 ε
fromList shape llista
classes interfícies
array IArray α ε[102]
-- immutables
  -- α ∈ {Array, UArray}
-- cal esmentar el tipus desitjat
listArray (iMin, iMax) llista :: α ι ε
array (iMin, iMax) llistaD'Associacions :: α ι ε
-- des de MArray
MArray.freeze mArray
MArray α ε m[103]
-- mudables
  -- mònada IO, α ∈ {IOArray, IOUArray}
  -- mònada ST, α ∈ {STArray, STUArray}
(Monad m) -- cal esmentar el tipus desitjat
newListArray (iMin, iMax) llista :: m (α ι ε)
newArray (iMin, iMax) x_inicial:: m (α ι ε)
-- des de IArray
thaw iArray
estructures - generació
paquet tipus context buit amb un elem. de llista o funció generadora
-- associació = (clau, valor)
de llista ordenada
-- més ràpid
containers IntSet[72] empty singleton x fromList llista fromAscList llista
IntMap ε[73] empty singleton clau x fromList llistaD'Associacions fromAscList llistaD'Associacions
containers Set ε[70] (Ord ε) empty singleton x fromList llista fromAscList llista
Map κ ε[71] (Ord κ) empty singleton clau x fromList llistaD'Associacions fromAscList llistaD'Associacions
unordered-containers HashSet ε[79] (Hashable ε, Eq ε) empty singleton x fromList llista
HashMap κ ε[80] (Hashable κ, Eq κ) empty singleton clau x fromList llistaD'Associacions
bytestring-trie Trie ε[87] empty singleton bytestringClau x fromList llistaD'Associacions
containers Tree ε[104] Node x [ ] unfoldTree f llavor
heap[90] {Min|Max}Heap ε Ord ε empty singleton x fromList fromDescList
fromAscList
{Min|Max}PrioHeap prio val Ord prio empty singleton (pri,v) -- de llista de (prio, val)
fromList
-- de llista de (prio, val)
fromDescList
fromAscList

consulta[modifica | modifica el codi]

opcionals - consulta
(en vermell si parcialment definides, criden a error)
tipus context és
buit?
mida pertinença obtenir elem components altres
Maybe ε[91] isNothing fromEnum . isJust (==). Just -- encaix
Nothing | Just x
maybeToList
fromJust
fromMaybe default
Either tipError ε[92] -- encaix
Left error | Right x
  • fromJust de Maybe NO buida
seqüències - consulta
(en vermell si parcialment definides, criden a error)
tipus context és
buit?
mida pertinença obtenir elem components altres
[ε][93] null length elem x find predicat:: Maybe ε
-- encaix
[ ] | (x : xs)
head
tail
subsequences
permutations
init
last
Seq ε[81] null length (=/ Nothing). (elemIndex{L|R} x) -- encaix de viewl seq
EmptyL | (x :< xseq)
-- encaix de viewr seq
EmptyR | (x :> xseq)
-- via Foldable
ByteString[94]
-- UArray Word8
null length elem x find predicat
-- encaix de uncons byteStr
Nothing | Just (x, xs)
:: Maybe (Word8, ByteString)
unpack
:: [Word8]
head
tail
init
last
Text[95]
-- ByteString codif. UTF16
indexat com [Word16][96]
null length elem ch find predicat
-- encaix de uncons text
Nothing | Just (ch, txt)
:: Maybe (Char, Text)
unpack
:: [Char]
head
tail
init
last
classes interfícies
Foldable contenidor[105] elem x find predicat toList
  • head, tail, init, last requereixen llistes NO buides
vectors - consulta
tipus context és
buit?
mida pertinença obtenir elem components altres
Array ι ε[98] (Ix ι) bounds -- límits de l'índex
indices
elems
assocs -- parells (índex, valor)
Vector ε[100] (Eq ε) null length elem x find predicat toList
head
tail
init
last
MVector σ ε[101] null length Vector.freeze --cap a Vector
multidim. repa
Array sh ε[82]
(Shape sh,
Elt ε)
size . extent toList
toScalar -- cas de shape DIM0
extent
  ::(Shape sh)
classes interfícies
IArray α ε[102]
-- immutables
bounds
indices
elems
assocs
MArray α ε m[103]
-- mudables
(Monad m) getBounds
getElems
getAssocs
estructures - consulta
tipus context és
buit?
mida pertinença obtenir elem components altres
Set ε[70]
IntSet[72]
null size elems
(Ord ε) member x -- cerca aproximació
lookup{LT,GT,LE,GE} x
toAscList
HashSet ε[79] (Hashable ε, Eq ε) null size member x toList
Map κ ε[71]
IntMap ε[73]
null size keys
keysSet
elems
assocs
(Ord κ) member clau (! clau)
lookup clau -- :: Maybe ε
-- cerca aproximació
lookup{LT,GT,LE,GE} clau
toAscList
HashMap κ ε[80] (Hashable κ, Eq κ) null size member clau (! clau)
lookup clau -- :: Maybe ε
lookupDefault default clau -- :: ε
keys
elems
toList
Trie ε[87] null size member bytestringClau lookup bstrClau keys
elems
toList
submap bstrClau
Tree ε[104] rootLabel
subForest
-- llista en pre-ordre
flatten
-- elems per nivells
levels
{Min|Max}Heap ε[90] Ord ε null size -- encaix de view heap
Nothing | Just (x, heap)
:: Maybe (ε, {Min|Max}Heap ε)
toList
toDescList
toAscList
{Min|Max}PrioHeap prio val[90] Ord prio null size -- encaix de view heap
Nothing | Just ((pri, v), heap)
:: Maybe ((prio, val), {Min|Max}PrioHeap prio val)
-- llista de (prio, val)
toList
toDescList
toAscList

Els errors generats per funcions definides parcialment, com ara head, que requereix llista no-buida, tenen una depuració complicada (la crida a error no informa de la situació de la crida que incompleix la pre-condició (excepte si es compila per l'ajustatge, ang:profiling), ni normalment tampoc del valor causant (si la implementació de Show pel paràmetre no es requereix), només dispara una excepció genèrica ErrorCall amb el missatge).

actualització / transformació[modifica | modifica el codi]

opcionals - actualitza/transforma
tipus aplicació (map)
als elems.
altres
Maybe ε[91] -- via Functor maybe default (f::ε→b)
Either tipError ε[92] -- via Functor either (f::tipError→b) (g::ε→b)
classes interfícies
Functor[106] fmap (f::a → b)
seqüències - actualitza/transforma
transforma
tipus context afegeix elimina aplicació (map)
als elems.
altres transforma'n
una llista
[ε][93] (x :) delete x map (f:: ε → b)
mapAccum{L|R} (f::acc→x→ (acc, y)) acumIni
-- amb imatge opcional
Maybe.mapMaybe (f::ε→Maybe b)
nub --elimina duplicats
reverse
intersperse x
transpose
intercalate llista
Ord ε insert x sort
Seq ε[81] (x <|) -- a l'esquerra
(|> x) -- a la dreta
mapWithIndex (f:: Int → ε → b) reverse
ByteString[94] cons x -- al davant
snoc x -- al darrera
map (f:: Word8 → Word8) reverse
intersperse byte
transpose
intercalate bstr
Text[95] cons ch
snoc ch
map (f:: Char → Char) reverse
intersperse ch
-- específics
replace cerca  subst
toUpper
toLower
toCaseFold
justifyLeft llarg ch
justifyRight llarg ch
center llarg ch
-- retalla espais
strip
stripLeft
stripRight
transpose
intercalate txt
classes interfícies
Functor[106] fmap (f::a → b)
  • Els tipus monomòrfics (ByteString, Text i també IntSet) no implementen Functor, que requereix tipus amb kind * -> * (aritat del tipus >= 1)
vectors - actualitza/transforma
transforma
tipus afegeix actualitza aplicació (map) altres
Array ι ε[98] (// llistaD'Associacions) -- via Functor
Vector ε[100] cons x -- pel davant
snoc x -- pel darrere
map (f:: a → b)
-- amb l'índex
imap (f:: Int → a → b)
reverse
MVector σ ε[101] (`set` x)
clear -- desvincula refs.
-- copy dst src
(`copy` mvectorOrigen)
(`move` mvectorOrigen)
(`grow` n)
multidim. repa
Array sh ε[82]
map (f:: a → b) reshape shape
transpose
classes interfícies
IArray α ε[102] (// llistaD'Associacions) amap (f::a→b)
MArray α ε m[103] mapArray (f::a → b)
estructures - actualitza/transforma
transforma
tipus context afegeix elimina actualitza aplicació (map)
als elems.
Set ε[70]
IntSet[72]
(Ord ε) insert x delete x insert x -- subst. l'existent map (f:: a → b)
-- cas que f conservi l'ordre, més ràpid amb
mapMonotonic (f:: a → b)
HashSet ε[79] (Hashable ε, Eq ε) insert x delete x insert x -- subst. l'existent map (f:: a → b)
Map κ ε[71]
IntMap ε[73]
(Ord κ) insert clau x delete clau adjust (f::a→a) clau
update (f::a→Maybe a) clau
alter (f::Maybe a → Maybe a) clau
map (f:: a → b)
mapWithKey (f:: κ → a → b)
mapAccum (f:: acc → a → (acc, b)) acumIni
-- mapeig a les claus amb
mapKeys (f::k1 -> k2)
-- si f conserva l'ordre, més ràpid amb
mapKeysMonotònic (f::k1 -> k2)
-- en les coŀlisions de f, combina valors
mapKeysWith (en_coŀlisions::a->a->a) (f::k1 -> k2)
HashMap κ ε[80] (Hashable κ, Eq κ) insert clau x delete clau adjust (f::a→a) clau map (f:: a → b)
mapWithKey (f:: κ → a → b)
Trie ε[87] insert bytestringClau delete bstrClau adjust f bstrClau mapBy (f:: ByteString → a → Maybe b)
filterMap (f:: a → Maybe b)
Tree ε[104] -- via Functor
{Min|Max}Heap ε[90] (Ord ε) insert x
{Min|Max}PrioHeap prio val[90] (Ord prio) insert (pri, v) -- via Functor
  • Els tipus (Set a) i (HashSet a) no implementen Functor: Perquè el resultat de l'aplicació amb fmap de (f::a->b) sigui un Set o bé un HashSet cal que els tipus origen i imatge compleixin els requeriments de context dels elements de Set (Ord a) o bé HashSet (Hashable a, Eq a), no inclosos a la definició de Functor.

Nota per a les actualitzacions als diccionaris (pròpiament taules associatives) Data.Map i Data.IntMap:

  • adjust (f::a→a) clau dicc : si clau no pertany a dicc, retorna el dicc. original
  • update (f::a→Maybe a) clau dicc : si f retorna Nothing, update elimina la clau; cas de Just y actualitza amb y
  • alter (f::Maybe a → Maybe a) clau dicc : si clau no existeix, la insereix amb el valor (f Nothing); altrament com update

accés indexat[modifica | modifica el codi]

seqüències - accés indexat
en color si generen error, en marró si és
previsible: domini acotat i continu: length O(1) i índex ∈ [0,length)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
:: Maybe Int
cerca índexs
::[Int]
elimina actualitza
[ε][93] (!! índx) elemIndex x
findIndex predicat
elemIndices x
findIndices predicat
Seq ε[81] (`índex` índx) elemIndexL x -- per l'esquerra
findIndexL predicat
elemIndexR x --per la dreta
findIndexR predicat
elemIndices{L|R} x
findIndices{L|R} predicat
adjust f índx
update índx x
ByteString[94] (`índex` índx) elemIndex x -- pel davant
elemIndexEnd x -- pel darrera
findIndex predicat
elemIndices x
findIndices predicat
Text[95] (`índex` índx) findIndex predicat
vectors - accés indexat
en color si generen error, en marró si és
previsible: domini acotat i continu: length O(1) i índex ∈ [0,length)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
:: Maybe Int
cerca índexs elimina actualitza
Array ι ε[98] (! índx)
Vector ε[100] (! índx)
(!? índx) -- :: Maybe ε
elemIndex x
findIndex predicat
-- retornen :: Vector Int
elemIndices x
findIndices predicat
MVector σ ε[101] (`read` indx) (`write` indx x)
(`swap` i j)
multidim. repa
Array sh ε[82]
(Shape sh,
Elt ε)
(! índxMultiDim)
(`índex` índxMultiDim)
(`safeIndex` índxMultiDim)
classes interfícies
IArray α ε[102] (! índx)
MArray α ε m[103] (Monad m) (`readArray` indx) (`writeArray` indx x)
estructures - accés indexat (índex basat en zero sobre seq. ordenada)
en color si generen error, en marró si és
previsible: domini acotat i continu: size O(1) i índex ∈ [0,size)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
elimina actualitza
Set ε[70] (Ord ε) elemAt índx
retorna :: ε
findIndex valor -- :: Int
lookupIndex valor -- :: Maybe Int
deleteAt índx
Map κ ε[71] (Ord κ) elemAt índx
retorna :: (κ,ε)
findIndex clau -- :: Int
lookupIndex clau -- :: Maybe Int
deleteAt índx updateAt f índx

combinació[modifica | modifica el codi]

combina (monoides i zips)
tipus context compon compon-ne
una llista
combina per posició
(zips)
zip = zipWith (,)
desacobla llista de tuples
(unzips)
Maybe ε[91] -- via Monoid -- via Monoid
[ε][93] (++) concat (`zip` llista)
(`zip{3..7}` llista...)
zipWith (f::a→b→c) llista1 llista2
zipWith{3..7} f llista...
unzip
unzip{3..7}
Seq ε[81] (><) (`zip` seq)
(`zip{3..4}` seq...)
zipWith (f::a→b→c) seq1 seq2
zipWith{3..4} f seq...
ByteString[94] append concat (`zip` byteStr)
zipWith (f::Word8→Word8→a) byteStr1 byteStr2
unzip
Text[95] append concat (`zip` txt)
zipWith (f::Char→Char→Char) txt1 txt2
Set ε[70]
IntSet[72]
(Ord ε) union unions
HashSet ε[79] (Hashable ε, Eq ε) union unions
Map κ ε[71]
IntMap ε[73]
(Ord κ) union
unionWith (f::ε -> ε -> ε)
unionWithKey (f::κ -> ε -> ε -> ε)
unions
unionsWith (f::ε -> ε -> ε)
HashMap κ ε[80] (Hashable κ, Eq κ) union
unionWith (f::ε -> ε -> ε)
unions
Trie ε[87] union{L|R}
mergeBy (f::ε -> ε -> Maybe ε)
-- via Monoid
Tree ε[104]
{Min|Max}Heap ε[90] (Ord ε) union unions
{Min|Max}PrioHeap prio val[90] (Ord prio) union unions
Array ι ε[98]
Vector ε[100] (++) concat (`zip` vect)
(`zip{3..6}` vect)
zipWith (f::a→b→c) vect1 vect2
zipWith{3..6} f vect...
-- amb l'índex a la funció
izipWith (f::Int→a→b→c) vect1 vect2
izipWith{3..6} f vect...
unzip
unzip{3..6}
multidim. repa
Array sh ε[82]
(++)
append
classes interfícies
Monoid[97] mappend mconcat
IArray α ε[102]
MArray α ε m[103]
combina com a conjunt o bé multiconjunt
tipus context diferència unió uneix-ne
una llista
intersecció està contingut contingut
estrictament
obtenir
distints
[ε][93] (\\) -- multi-cjt union -- no multi intersect -- multi (`isPrefixOf` llista)
(`isSuffixOf` llista)
(`isInfixOf` llista)
nub
Seq ε[81]
ByteString[94] (`isPrefixOf` byteStr)
(`isSuffixOf` byteStr)
(`isInfixOf` byteStr)
Text[95] (`isPrefixOf` txt)
(`isSuffixOf` txt)
(`isInfixOf` txt)
Set ε[70]
IntSet[72]
(Ord ε) (\\)
difference
union unions intersection isSubsetOf isProperSubsetOf
HashSet ε[79] (Hashable ε, Eq ε) difference union unions intersection
Map κ ε[71]
IntMap ε[73]
(Ord κ) (\\)
difference
union unions intersection isSubmapOf isProperSubmapOf
HashMap κ ε[71] (Hashable κ, Eq κ) difference union
unionWith (f::ε -> ε -> ε)
unions intersection
intersectionWith (f::ε -> ε -> ε)
Trie ε[87] unionL
unionR
{Min|Max}Heap ε[90] (Ord ε) union unions
{Min|Max}PrioHeap prio val[90] (Ord prio) union unions

reducció[modifica | modifica el codi]

plegats
(en color si parcialment definides, criden a error)
tipus reducció successió de plegats parcials
per l'esquerra per la dreta per l'esquerra per la dreta
[ε][93] foldl (f::a→ε→a) inicial
foldl' f ini -- estricte
foldl1 (g::ε→ε→ε) -- sobre el primer elem.
foldr f ini
foldr1 g
scanl f ini
scanl1 g
scanr f ini
scanr1 g
classes
Foldable t[105] foldl f ini
foldl' f ini -- estricte
foldl1 g
foldr f ini
foldr' f ini -- estricte
foldr1 g
  • foldl1, foldr1, scanl1, scanr1: sobre seqüències NO buides
opcionals - plegats especials
tipus llista de
contenidors
retornant [a] retornant ([a], [b])
Maybe ε[91] catMaybes
Either tipError ε[92] lefts
rights
-- (lefts, rights):
partitionEithers
plegats especials
tipus sobre
booleans
s/. predicat s/. ordre numèrics contenidor llista de
contenidors
contenidor
de llistes
índex del
menor/major
ε ~ Bool (Ord ε / κ) (Num ε) (Ord ε)
[ε][93] and
or
all predicat
any predicat
minimum
maximum
sum
product
concatMap (f::a→[b]) concat
ByteString[94] all predicat
any predicat
minimum
maximum
concatMap (f::Word8→ByteString) concat
Text[95] all predicat
any predicat
minimum
maximum
concatMap (f::Char→Text) concat
Vector ε[100] and
or
all predicat
any predicat
minimum
maximum
sum
product
concatMap (f::a → Vector b) concat minIndex
maxIndex
Set ε[70] -- via Foldable -- via Foldable findMin
findMax
-- via Foldable -- via Foldable -- via Monoid
IntSet[72] findMin
findMax
-- via Monoid
HashSet ε[79] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
Map κ ε[71]
IntMap ε[73]
-- via Foldable -- via Foldable -- min/max de la clau
findMin
findMax
-- via Foldable -- via Foldable -- via Monoid
HashMap κ ε[80] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
{Min|Max}PrioHeap prio val[90] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
classes
(Monoid m)
Foldable t[105] and
or
all predicat
any predicat
minimum
maximum
sum
product
concatMap (f::a→m b) concat
Monoid t[97] mconcat
  • minimum, maximum sobre seqüències NO buides
  • minIndex, maxIndex sobre vectors NO buits
  • findMin, findMax sobre conjunts i diccionaris No buits
  • els tipus monomòrfics (ByteString, Text, IntSet) no implementen Foldable que requereix tipus amb Kind (* -> *) (aritat del tipus >= 1)

partició[modifica | modifica el codi]

seqüències - partició
tipus a l'índex s/.
predicat
en trams d'elements
consecutius equivalents
per ocurrències
d'un separador
en trams de
llargada fixa
:: (t,t) :: (t,t) group = groupBy (==)
[ε][93] take n
drop n
splitAt índx filter predicat
takeWhile predicat
dropWhile predicat
partition predicat
span predicat
break predicat
group
groupBy (equiv::a→a→Bool)
Seq ε[81] take n
drop n
splitAt índx filter predicat
takeWhile{L|R} predicat
dropWhile{L|R} predicat
partition predicat
span{l|r} predicat
break{l|r} predicat
ByteString[94] take n
drop n
splitAt índx filter predicat
takeWhile predicat
dropWhile predicat
partition predicat
span predicat
break predicat
group
groupBy (equiv::Word8→Word8→Bool)
split separador
splitWith predicatDelSeparador
Text[95] take n
drop n
splitAt índx filter predicat
takeWhile predicat
dropWhile predicat
partition predicat
span predicat
break predicat
group
groupBy (equiv::Char→Char→Bool)
splitOn textSeparador
split predicatDelSeparador
chunksOf n
  • (span{l|r} predicat) parteix com (takeWhile{L|R} predicat, dropWhile{L|R} predicat)
  • (break{l|r} predicat) és equivalent a (span{l|r} (not. predicat))[107]
vectors - partició
tipus a l'índex s/.
predicat
en funció d'un rang
-- array de parells [(i, arr ! f i) | i <- [iMin..iMax] ]
(Ix i, Ix j)
Array ι ε[98] ixmap (iMin, iMax) (f::i → j)
Vector ε[100] take n
drop n
slice índx n
splitAt índx filter predicat
takeWhile predicat
dropWhile predicat
partition predicat
span predicat
break predicat
MVector σ ε[101] take n
drop n
slice índx n
splitAt índx
classes
IArray α ε[102] ixmap (iMin, iMax) (f::i → j)
MArray α ε m[103] mapIndices (iMin, iMax) (f::i → j)
estructures - partició
tipus context a l'índex en inferiors
i superiors
(estrictament)
s/.
predicat
pel prefix
Set ε[70]
IntSet[72]
(Ord ε) split pivot partition predicat
filter predicat
HashSet ε[79] (Hashable ε, Eq ε) filter predicat
Map κ ε[71]
IntMap ε[73]
(Ord κ) split clauPivot partition predicat
filter predicat
HashMap κ ε[80] (Hashable κ, Eq κ) filter predicat
filterWithKey (f::clau->predicat)
Trie ε[87] submap bytestrPrefixClau
Tree ε[104]
{Min|Max}Heap ε[90] (Ord ε) take n
drop n
splitAt n
filter predicat
partition predicat
takeWhile predicat
dropWhile predicat
span predicat
break predicat
{Min|Max}PrioHeap prio val[90] (Ord prio) take n
drop n
splitAt n
filter predicat
partition predicat
takeWhile predicat
dropWhile predicat
span predicat
break predicat

implementació de classes[modifica | modifica el codi]

classes de la biblioteca GHC que les coŀleccions implementen:

  • control seqüencial: Applicative, Monad
  • composició no-seqüencial: Monoid
  • composició seqüencial: Alternative, MonadPlus
  • mapejat i reducció no-seqüencials: Functor, Foldable
  • mapejat i reducció seqüencials: Traversable
  • comparació de les coŀleccions: Eq, Ord
  • textualitzacio de les coŀleccions: Show, Read
  • travessament genèric de l'estructura de les coŀleccions: Data
  • avaluació a forma normal de les coŀleccions: NFData ( contracció de Normal Form Data)
  • serialització de les coŀleccions: Binary
excepcions i crítiques[modifica | modifica el codi]
  • els tipus monomòrfics (ByteString,[94] Text[95] i IntSet[72] no poden implementar les classes Functor i Foldable, Applicative, Monad, Alternative, MonadPlus, Traversable (quines funcions estan definides per a tipus amb un paràmetre). En intentar-ho, dóna l'error: Kind mis-match (l'aritat del tipus no és l'esperada).
  • no implementen Functor, per requerir restriccions de context no contemplades:
    • el conjunt (Set ε) dels arbres balancejats. El resultat de l'aplicació del Functor ha de ser un arbre balancejat, per tant cal que el domini de la imatge implementi Ordenable.
    • el conjunt (HashSet ε). Perquè el resultat del Functor sigui un HashSet, cal que el domini de la imatge de la funció de mapping implementi els requeriments dels elements de HashSet (Hashable ε, Eq ε).
alternatives
    • El paquet mono-traversable[108] aporta classes per a contenidors, sense l'exclusió dels tipus monomòrfics,
també inclou la majoria de les funcions en classes per estalviar haver-les de distingir amb el prefix del mòdul.
implementacions[modifica | modifica el codi]
implementa classes d'avaluació seqüencial dels elements
tipus Applicative
(combinador
de resultats)
Alternative
(applicative
amb monoide)
Monad
(encadenament
de resultats)
MonadPlus
(mònada
amb monoide)
Traversable[109]
(que pot obtenir
coŀleció de resultats
de l'avaluació seqüencial
dels elements si són accions
o dels elements mapejats a accions)
Maybe ε[91]
Either tipError ε[92] No No des de ghc 7.8
[ε][93]
Seq ε[81] des de ghc 7.8
Array ι ε[98] No No No No No
Vector ε[100]
Set ε[70] No No No No No
HashSet ε[79] No No No No No
Map κ ε[71]
IntMap ε[73]
No No No No
HashMap κ ε[80] No No No No
Trie ε[87] No No
Tree ε[104] No No
  • Al GHC 7.10 està previst que Applicative esdevingui superclasse de Monad, i que Alternative ho sigui de MonadPlus.[110] Preveient això a GHC 7.8 es generen avisos per als tipus que implementen MonadPlus i no ho fan amb Alternative.[110]
  • Alternative i MonadPlus inclouen implementacions que obeeixen regles diferents. Hi ha una proposta per a separar cadascuna d'elles en dues classes.[111][112]
    • La regla Left Catch: Maybe l'obeeix, però no la implem. de la llista. Vegeu implementacions al mòdul Control.Monad.[113]
  mplus (return x) k = return x
  (<|>) (pure x) k = pure x 
 
-- Maybe l'obeeix
instance MonadPlus Maybe where
   mzero = Nothing
 
   Nothing `mplus` ys  = ys
   xs      `mplus` _ys = xs
 
mplus (return x) k == Just x   -- == return x
 
-- la llista no: 
instance MonadPlus [] where
   mzero = []
   mplus = (++)
 
mplus (return x) k == [x] ++ k   -- /= return x
implementa classes
tipus Monoid[97]
(composable:
mempty, mappend)
Functor[106]
(mapejable:
fmap)
Foldable[105]
(reduïble:
foldl, foldr)
Binary[114]
(serialitzable:
put, get)
NFData[115]
(avaluable a
Forma Normal:
rnf, deepseq)
Data[116]
(que se'n pot
recórrer l'estructura
genèricament:
gfoldl, gmapX)
Maybe ε[91] (Monoid ε)
compon els elements
(Binary ε) (NFData ε) (Data ε)
Either tipError ε[92] No des de ghc 7.8 (Binary tipError,
Binary ε)
(NFData tipError,
NFData ε)
(Data tipError,
Data ε)
[ε][93] mappend = (++) (Binary ε) (NFData ε) (Data ε)
Seq ε[81] mappend = (><) (Binary ε) (NFData ε) (Data ε)
ByteString[94] mappend = append N/A:
l'aritat del tipus
no concorda
(Kind mis-match)
N/A:
l'aritat del tipus
no concorda
Text[95] mappend = append N/A:
l'aritat del tipus
no concorda
(Kind mis-match)
N/A:
l'aritat del tipus
no concorda
(-- implementable
via "binary-generic")
Array ι ε[98] No No (Binary ι,
Binary ε)
(NFData ι,
NFData ε)
No
Vector ε[100] mappend = (++) (Binary ε)
-- instàncies al paquet
vector-binary-instances
(NFData ε) (Data ε)
Set ε[70] (Ord ε)
mappend = union
No[117] (Binary ε) (NFData ε) (Data ε,
Ord ε)
HashSet ε[79]
mappend = union
No (-- implementable
via "binary-generic")
(NFData ε) (Data ε)
IntSet[72] mappend = union N/A:
l'aritat del tipus
no concorda
(Kind mis-match)
N/A:
l'aritat del tipus
no concorda
Map κ ε[71]
IntMap ε[73]
(Ord κ)
mappend = union
cas de claus coincidents
es pren el valor
del primer operand
(Binary ε) (NFData κ,
NFData ε)
(Data κ,
Data ε,
Ord κ)
HashMap κ ε[80]
mappend = union
(-- implementable
via "binary-generic")
(NFData κ,
NFData ε)
(Data κ,
Data ε)
Trie ε[87] (Monoid ε)
mappend = mergeBy...
cas de claus coincidents
en compon els valors
(Binary ε) No No
Tree ε[104] No (Binary ε) (NFData ε) (Data ε)
{Min|Max}Heap ε[90]
mappend = union
No No No No No
{Min|Max}PrioHeap prio val[90]
mappend = union
No No No

Altres classes habituals:

  • Eq t: Igualable, contenidors iguals comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista.
  • Ord t: Ordenable, compara contenidors comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista ascendent.
  • Show t: Textualitzable a String (els caràcters ASCII imprimibles (0x20 a 0x7F) tal com són, els metacaràcters (com ara '\t',..) amb el prefix '\', altres caràcters en representació numèrica) En ésser utilitzat per a la serialització, requereix representació unívoca. Per a una representació humanament llegible de caràcters no anglosaxons, cal fer servir l'entrada/sortida del paquet Text.
  • Read t: Llegeix el que mostra (Show t).
implementa classes
tipus Eq[118]
(Igualable)
Ord[119]
(Ordenable)
Show[120]
(Textualitzable)
--només ASCII
altrament codis numèrics
Read[121]
(Llegible)
--només ASCII
altrament codis numèrics
--monomòrfics
ByteString[94]
Text[95]
IntSet[72]
--tipus d'aritat 1
Maybe ε[91]
[ε][93]
Seq ε[81]
Vector ε[100]
Set ε[70]
IntMap ε[73]
(Eq ε) (Ord ε) (Show ε) (Read ε)
--tipus d'aritat 2
Map κ ε[71]
Either κ ε[92]
(Eq κ, Eq ε) (Ord κ, Ord ε) (Show κ, Show ε) (Read κ, Read ε)
--tipus amb particularitats
(Ix ι) => Array ι ε[98] (Eq ε) (Ord ε) (Show ι, Show ε) No
Tree ε[104] (Eq ε) Aquests arbres
no presuposen ordre
(Show ε) (Read ε)
(Hashable ε, Eq ε) => HashSet ε[79] No (Show ε) (Read ε)
(Hashable κ, Eq κ) => HashMap κ ε[80] (Eq ε) No (Show κ, Show ε) (Read κ, Read ε)
(Ord ε) => {Min|Max}Heap ε[90]
(Ord prio) => {Min|Max}PrioHeap prio val[90]
Implementació de Binary (serialitzable) amb binary-generic[modifica | modifica el codi]

Cal que el tipus implementi la classe Data[116] del mòdul Data.Data que possibilita el recorregut genèric de l'estructura del tipus. Vegeu el paquet binary-generic[122]

{-# LANGUAGE PackageImports #-}
import Data.Data (Data) -- Data possibilita el travessament genèric de l'estructura
import Data.Binary (Binary(..))  -- la classe Binary i els seus mètodes (..)
import "binary-generic" Data.Binary.Generic (getGeneric, putGeneric)
 
-- Text de Data.Text ja implementa Data
 
import Data.Text (Text) 
import Data.HashSet (HashSet)
import Data.Hashable (Hashable)
 
instance Binary Text where
  get = getGeneric
  put = putGeneric        -- versió tàcita (sense paràm. formals reduïbles)
 
instance (Data a, Hashable a, Eq a) => Binary (HashSet a) where
  get = getGeneric
  put = putGeneric

prova:

cabal install binary-generic
ghc -c prova.hs
Sobrecàrrega de literals String[modifica | modifica el codi]

Així com els literals numèrics obtenen el tipus de la signatura de les operacions en que intervenen, la classe IsString fa possible això mateix per als literals String admetent-ne l'ús en posicions d'altres tipus que n'implementin la conversió, en el mètode fromString de la classe IsString.[123] Cal especificar l'extensió de llenguatge OverloadedStrings.[124]

{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString.Char8 as BS
 
-- "abc" convertit automàticament al tipus esperat
-- sempre que implementi el mètode ''fromString'' de IsString
main = BS.putStrLn "abc"
implementa classes
tipus IsString[123]
[Char][93]
ByteString[125] Data.ByteString.Char8
(ByteString)
Text[95]

A partir de GHC 7.8, els literals String, si hi ha l'extensió OverloadedStrings, ja no tenen tipus String sino que el tipus es descriu de manera existencial com un tipus d'aquells que implementen IsString

(IsString t) => t

de manera similar als literals numèrics, que si són enters es descriu com un tipus d'aquells que implementen la classe Num

(Num t) => t
Sobrecàrrega de literals Llista[modifica | modifica el codi]
  • Des de GHC 7.8. OverloadedLists: Els literals llista poden passar com a literals d'altres tipus que implementin (IsList t) aportant-hi la conversió.[126] Exemple:
-- provat amb GHC versió == 7.7.20140111
{-# LANGUAGE OverloadedLists, TypeFamilies #-}
 
import GHC.Exts (IsList(..))
 
import Data.Hashable
import Data.HashSet as HS
import Data.HashMap.Lazy as HM
 
-- per a (HashSet a)
instance (Hashable a, Eq a) => IsList (HashSet a) where
 
  -- Item coŀlecció: tipus de l'element del literal llista relatiu al tipus de la classe
  type Item (HashSet a) = a
  fromList = HS.fromList
  toList = HS.toList
 
provaSet = ["Joan", "Pere"] :: HashSet String
 
-- per a (HashMap k a)
instance (Hashable k, Eq k) => IsList (HashMap k a) where
 
  -- Item coŀlecció: tipus de l'element del literal llista relatiu al tipus de la classe
  type Item (HashMap k a) = (k, a)      
  fromList = HM.fromList
  toList = HM.toList
 
provaMap = [("Joan",2),("Pere",3)] :: HashMap String Int

Preprocessadors[modifica | modifica el codi]

Compilació condicional amb codi CPP[modifica | modifica el codi]

Inclusió de codi de preprocessador de C (CPP).[127] Compilació condicional.[128]

Cal esmentar el senyal de compilació -cpp o bé la pragma d'extensió de llenguatge {-# LANGUAGE CPP #-}.

El gestor de projectes Cabal genera amb cabal build el fitxer de macros dist/build/autogen/cabal_macros.h definint per cada biblioteca l'identificador de versió i una macro.

/* package base-4.6.0.1 */
#define VERSION_base "4.6.0.1"
#define MIN_VERSION_base(major1,major2,minor) (\
  (major1) <  4 || \
  (major1) == 4 && (major2) <  6 || \
  (major1) == 4 && (major2) == 6 && (minor) <= 0)

Això permet especificar compilació condicional segons la versió de biblioteca.

{-# LANGUAGE CPP, PackageImports #-}
 
-- pragmes condicionats a la versió de GHC
 
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707 && __GLASGOW_HASKELL__ < 710
{-# OPTIONS_GHC -fno-warn-amp #-}
#endif
 
-- codi segons versió de biblioteca
 
#ifdef __GLASGOW_HASKELL__         -- cas de compilador GHC
 
  #if MIN_VERSION_biblioteca(4,0,0)
   ... codi que compila si la versió és igual o superior a l'esmentada
  #else
   ... cas de versions més antigues, per exemple: ús de E/S UTF8 en GHC < 6.12
    import Prelude hiding (putStr, putStrLn, getLine) -- amaguem les funcions predefinides no UTF8 que volem UTF8
    import "utf8-string" System.IO.UTF8 (putStr, putStrLn, getLine) 
  #endif
 
#elif defined(__UHC__)              -- cas de compilador UHC
...
 
#else
 
  #error "no ens fem responsables d'altres compiladors"
#endif
  • Recomanat: Mòduls de compatibilitat per als casos de trencament de l'API[129]

altres[modifica | modifica el codi]

El gestor de projectes Cabal cerca per a cada mòdul de la llista de components, les extensions dels documents dels preprocessadors més habituals i en genera el mòdul Haskell corresponent sense que calgui indicar-ho al fitxer de projecte.

  • cas de document de lèxic (extensions {.x,.lx}) el preprocessador Alex genera el mòdul haskell del mateix nom.
  • cas de document de gramàtica (extensions {.y,.ly}) el preprocessador Happy genera el mòdul haskell del mateix nom.

Forats en expressions per consultar-ne el tipus (ang: Type Holes)[modifica | modifica el codi]

És una nova característica de GHC 7.8 que permet consultar el tipus que pertoca a una posició de paràmetre actual, designada amb el guió baix '_', abans d'escriure-hi una expressió.[130]

$> ghci                                                                                                                                                                      
GHCi, version 7.7.20140111:...
Prelude> :set -XTypeHoles
Prelude> :{
Prelude| data Free f a
Prelude|   = Pure a
Prelude|   | Free (f (Free f a))
Prelude|  
Prelude| instance Functor f => Monad (Free f) where
Prelude|   return a     = Pure a
Prelude|   Pure a >>= f = f a
Prelude|   Free f >>= g = Free _
Prelude| :}
 
<interactive>:32:23:
    Found hole ‛_’ with type: f (Free f b)
    Where: ‛f’ is a rigid type variable bound by
               the instance declaration at <interactive>:29:10
           ‛b’ is a rigid type variable bound by
               the type signature for
                 (>>=) :: Free f a -> (a -> Free f b) -> Free f b
               at <interactive>:31:10
    Relevant bindings include
      g :: a -> Free f b (bound at <interactive>:32:14)
      f :: f (Free f a) (bound at <interactive>:32:8)
      (>>=) :: Free f a -> (a -> Free f b) -> Free f b
        (bound at <interactive>:31:3)
    In the first argument of ‛Free’, namely ‛_’
    In the expression: Free _
    In an equation for ‛>>=’: (Free f) >>= g = Free _
Prelude>

Depuració[modifica | modifica el codi]

Pistes per la depuració aquí.[131]

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.

Per exemple, en cas de manca en la consulta en un diccionari Data.Map (dicc ! clau), dóna l'error "Map.find: element not in the map". Cap pista (a GHC) de la situació de la crida a error, ni de la rutina de procedència, ni de la clau causant de l'error. (no s'exigeix que les claus dels Map siguin textualitzables (instàncies de Show)).

A Haskell no hi ha la pila per a crides dels llenguatges estrictes sinó que la pila és només d'expressions pendents d'ésser avaluades per a diferents patrons.[132] Per tant tampoc tindrem el bolcat de crides per situar-se.

Solució parcial: A partir de GHC 7.4.1, per quan es compila amb l'opció de l'ajustatge de rendiment (ang:profiling), (ghc -prof), s'ha habilitat una reescriptura del codi intern per la simulació d'una pila de crides estricta, per obtenir-ne un bolcat en cas d'excepció.[133][134] Cal afegir +RTS -xc al llançament del programa.[135]

L'ús de l'ajustatge (ang:profiling) requereix relligar el programa amb una versió particular de perfilat de la biblio del RunTimeSystem.[136] Per fer proves amb perfilat cal disposar d'una compilació amb -prof de totes les biblioteques que relliguem.[137]
Per evitar l'error de manca de biblioteca de perfilat en les dependències, cal generar versions amb perfilat de les biblioteques que s'instaŀlin. Per això, per al desenvolupament en aïllament (sandbox), convé afegir l'opció de perfilat per defecte (library-profiling: True) a l'arxiu de configuració de cabal específic del projecte (cabal.config) en la mateixa carpeta.

Tanmateix si sabeu per on poden anar els trets, es pot anar acorralant amb paranys (catch) i afegir traces als llocs sospitosos.

aproximació amb paranys (catch)[modifica | modifica el codi]

Desavantatge: Les clàusules catch no es poden utilitzar dins el codi funcional, sinó només en codi de la mònada IO.[138]

Cal fer servir el catch de Control.Exception. El catch del Prelude està desaconsellat (ang:deprecated) (eliminat a GHC v. 7.6.1) perquè no caçava les excepcions del codi funcional.

{-# LANGUAGE ScopedTypeVariables #-}
 
import Prelude hiding (catch)  -- el catch del Prelude està desaconsellat (''deprecated'')
import Control.Exception (catch, ErrorCall, SomeException)
 
default (Int, Double) -- seq. de desambiguació dels literals numèrics
 
cap (x:_xs) = x
cap [] = error "l'has espifiada, calçat per trobar qui m'ha cridat!!"
 
erroni n = cap $ drop n [1,2,3]
 
aproxAmbParanys n = (print $ erroni n)
       `catch` (\(err :: ErrorCall)putStrLn $ "caçada op. no definida per al valor: " ++ show err)
       `catch` (\(err :: SomeException)putStrLn $ "caçada: " ++ show err)
 
main = aproxAmbParanys 5

Vegeu també #Punts de control al codi amb Loch-TH.

aproximació amb traces[modifica | modifica el codi]

Desavantatges:

  • en el codi funcional l'ordre d'impressió de múltiples traces és aleatori perquè l'ordre d'execució de subexpressions és indeterminat.
  • altrament cal tenir en compte l'avaluació tardana.
import Debug.Trace (trace, traceStack) -- traceStack (des de GHC v.7.4.1)
 
default (Int, Double) -- seq. de desambiguació dels literals numèrics
 
cap (x:_xs) = x
cap [] = error "l'has espifiada, calçat per trobar qui m'ha cridat!!"
 
erroni n = cap $ drop n [1,2,3]
 
-- aproximació amb traces:
 
-- trace imprimeix el missatge via unsafePerformIO
-- traceStack, a més, imprimeix una "pila de crides" artificial (no n'hi ha al Haskell)
--       sempre que es compili amb opcions de perfilat (ghc --make -prof -fprof-auto-calls prova.hs)
 
aproxAmbTraces n = resultat'
        where 
              resultat = erroni n
              resultat' = trace msg resultat  -- o bé amb ''traceStack'' si GHC >= 7.4
              msg = "el paràmetre era " ++ show n
 
main = aproxAmbTraces 5
$ ghc --make -prof -fprof-auto-calls prova.hs
...
$./prova
el paràmetre era 5
Stack trace:
  Main.exemple.resultat' (prova.hs:13:27-49)
  Main.main (prova.hs:16:17-25)
  Main.main (prova.hs:16:9-25)
  Main.main (prova.hs:(16,8)-(17,102))
  Main.main (prova.hs:(16,8)-(18,77))
  Main.CAF (<entire-module>)
prova: l'has espifiada, calçat per trobar qui m'ha cridat!!

aproximació amb depuració a l'intèrpret ghci[modifica | modifica el codi]

Amb GHCi debugger,[139] Vegeu l'article "No more exceptions: debugging Haskell code with GHCi".[140] establint punts d'aturada (break-points)

  • cal habilitar el senyal break-on-exception
  • l'excepció ErrorCall, generada per la crida a la funció error, provoca l'aturada
  • amb :back retrocedeix al break-point anterior
  • GHCi permet magatzemar en un fitxer "./.ghci"[141] les ordres a executar en arrencar ghci, facilitant desar una llista de ordres de càrrega de mòduls i declaració de break-points per a un ús reiteratiu. Requereix permís d'escriptura exclusiu del propietari al fitxer i al directori.
(a UNIX caldrà desactivar el permís d'escriptura de grup del fitxer i del directori: chmod g-w ".ghci" && chmod g-w $PWD)
default (Int, Double) -- seq. de desambiguació dels literals numèrics
 
cap (x:_xs) = x
cap [] = error "l'has espifiada, calçat per trobar qui m'ha cridat!!"
 
erroni n = cap $ drop n [1,2,3]
 
aproxAmbExecucióControlada n = erroni n
 
-- ordres GHCi: 
--   :set -fbreak-on-exception 
--   :break <ident-global|num-linia>   -- crea un breakpoint a l'identificador o bé num. de línia
--   :back    -- retrocedeix a breakpoint anterior
--   :continue     -- endavant sense aturar-se
--   :step         -- avança fins al proper breakpoint
--   :steplocal    -- avança fins al proper breakpt que pertanyi a la mateixa funció a nivell de declaracions
--   :stepmodule   -- avança fins al proper breakpt que pertanyi al mateix mòdul.
--   :show breaks  -- llista breakpoints
--   :list           -- llista el codi al voltant del breakpt 
--
-- consulta de variables:
--   :show bindings  -- llista identificadors visibles (variables i retorn consultables)
--   :print <ident>  -- consulta ident. avaluats, visibles en l'àmbit
--   :force <ident>  -- cas de no estar avaluat, força'n l'avaluació i imprimeix-lo
 
main = aproxAmbExecucióControlada 5
ghci prova.hs
Ok, modules loaded: Main.
*Main> :set -fbreak-on-exception
*Main> :break aproxAmbExecucióControlada
Breakpoint 0 activated at prova.hs:(30,1)-(32,35)

*Main> aproxAmbExecucióControlada 5
Stopped at prova.hs:(30,1)-(32,35)
_result :: Int = _
[prova.hs:(30,1)-(32,35)] *Main> :continue
Stopped at <exception thrown>
_exception :: e = _
[<exception thrown>] *Main> :back
Logged breakpoint at prova.hs:(30,1)-(32,35)

Traslladant la petada de les funcions parcials a la rutina que fa la crida[modifica | modifica el codi]

La crida a la funció error genera una excepció genèrica ErrorCall que cal evitar.

Opcions:

  1. situar la crida a la funció parcial en un case havent descartat el cas no definit en alternatives precedents.
  2. avaluar la precondició en el mòdul que fa la crida.
{-# LANGUAGE CPP #-}
import Control.Exception
 
-- macro CPP amb precondició i crida
 
#define SAFE_HEAD(list) (\
  assert (not $ null list) (head list))
 
default (Int)  -- desambiguació dels literals numèrics
 
llista = [1,2,3] 
 
main = do
       let cap = SAFE_HEAD(llista)       -- amb precondició i crida
       print cap

3. Substitució per una funció total equivalent, avaluant-ne un sol patró. En el cas de patró no previst, ens donarà la situació de l'encaix incomplet.

-- funció total equivalent a la parcial head
headMaybe :: [a] -> Maybe a
headMaybe (x:_) = Just x
headMaybe [] = Nothing
 
default (Int)  -- desambiguació dels literals numèrics
 
llista = [1,2,3] 
 
main = do
       -- en cas de llista buida inesperada ens donarà situació de l'encaix fallit
       let Just cap = headMaybe llista   
 
       print cap

La biblioteca Safe millora les funcions parcials del Prelude[modifica | modifica el codi]

La biblioteca Safe[142] ofereix diverses versions de les rutines predefinides al Prelude que poden petar.

  • sufix Def per a versions amb valor per defecte per al cas no definit.
  • sufix May per a versions amb resultat opcional (Maybe)
  • sufix Note per a versions amb missatge específic per al cas d'error.
  • sufix Safe: cas de no definició, retorna l'original. (tailSafe [] = [] ; initSafe [] = [])

Exemple: at: funció substitutiva de la indexació de llistes (!!), definida parcialment per a valors no negatius de l'índex, menors que la llargada:

at :: [a] -> Int -> a   -- com (!!) però amb millor missatge d'error
 
atDef :: a -> [a] -> Int -> a    -- amb valor per defecte
 
atMay :: [a] -> Int -> Maybe a   -- amb resultat opcional
 
atNote :: String -> [a] -> Int -> a   -- amb missatge per al cas d'error
Afegint localització i missatge per al cas no definit[modifica | modifica el codi]

Amb les biblioteques safe i file-location:[143]

{-# LANGUAGE TemplateHaskell, PackageImports #-}
import "file-location" FileLocation (err')
import "safe" Safe (atNote)
import Text.Printf (printf)
 
default (Int)
 
obtenirElCinquè :: [a] -> a
obtenirElCinquè llista = atNote cas_d'error llista 4
  where 
    cas_d'error = $err' msg   -- com error (excep. ErrorCall) incorporant la situació en temps de compilació.
 
    -- aquí ''printf'' equival al ''sprintf'' del lleng. C xq el tipus requerit és String i no (IO ())
    msg = printf "T'has passat, la llista només té %d elements.\n" (length llista)  
 
main = print $ obtenirElCinquè [1,2,3]

resultat:

$ runhaskell prova.hs
prova: main:Main prova.hs:11:19 T'has passat, la llista només té 3 elements.

Evitant les funcions parcials[modifica | modifica el codi]

El més convenient és evitar les funcions parcialment definides,

  • o bé substituir-les per funcions totals, analitzant el resultat per patrons
  • o bé definint un tipus específic per al subdomini vàlid, amb generadors que en validin els valors.

Opcions de compilació per evitar fallades[modifica | modifica el codi]

Vegeu ref.[144]

  • -fwarn-incomplete-patterns : avisa quan l'encaix pot fallar
  • -fwarn-incomplete-uni-patterns : avisa quan l'encaix en una lambda és incomplet
  • -fwarn-incomplete-record-updates: avisa quan les actualitzacions de registres són incompletes
  • -firrefutable-tuples: fer l'encaix de tuples irrefutable

Efecte fallada en una seqüència de computacions[modifica | modifica el codi]

fallada mònadica per elements absorbents[modifica | modifica el codi]

Els elements absorbents per l'esquerra de l'encadenament en una mònada fan inútil l'avaluació de la computació subseqüent, doncs el resultat és el mateix valor, de manera que l'encadenament progressa només per als elements no absorbents en (>>=).

Això facilita l'encadenament de computacions que poden fallar, sense haver de consultar la correcció del resultat a cada pas.

tipus de la mònada element absorbent en (>>=)
Maybe a Nothing
Either err a Left err
[a] [ ]
classe
MonadPlus m mzero
fallada monàdica en el codi funcional[modifica | modifica el codi]

Com acabem d'esmentar, l'element absorbent en l'encadenament en la mònada Maybe (o bé Either) evita l'avaluació de les computacions posteriors.

-- funció total
 
headMaybe :: [a]Maybe a
headMaybe (x : _) = return x  -- resultat exitós, equival a (Just x)
headMaybe [] = Nothing        -- element absorbent de la mònada Maybe
 
doblar :: Num a => a → Maybe a
doblar x = return $ 2 * x
 
doblarElCap :: Num a => [a]Maybe a
doblarElCap llista = headMaybe llista >>= doblar
 
-- doblarElCap [] == Nothing
-- doblarElCap [1,2,3] == Just 2
amb un tipus específic per al subdomini de casos definits[modifica | modifica el codi]

I una funció de validació dels casos definits:

newtype TLlistaNoBuida a = LlistaNoBuida { obtenirLlista :: [a]} deriving (Eq, Ord, Show)
 
-- Constructor:                LlistaNoBuida :: [a] -> TLlistaNoBuida a
-- Inversa del constructor:    obtenirLlista :: TLlistaNoBuida a -> [a]
 
validaLlistaNoBuida :: [a] -> Maybe (TLlistaNoBuida a)
validaLlistaNoBuida xs @ (_:_) = Just $ LlistaNoBuida xs
validaLlistaNoBuida _ = Nothing
 
head :: TLlistaNoBuida a -> a
head = Prelude.head. obtenirLlista
 
-----------------------------------
-- operant en la mònada Maybe: 
-- un resultat Nothing (element absorbent en (>>=)) fa que l'encadenament no progressi
--
obtenirLaLlistaNoBuidaMenor :: Ord a => [a] -> [a] -> Maybe (TLlistaNoBuida a) 
obtenirLaLlistaNoBuidaMenor l1 l2 = do
         nb1 <- validaLlistaNoBuida l1
         nb2 <- validaLlistaNoBuida l2
         if nb1 < nb2 then return nb1 else return nb2
 
default (Int) -- tipus per defecte dels literals
 
main = do
         case obtenirLaLlistaNoBuidaMenor [1,2,3] [2,3,4] of
           Just llistaNoBuida -> (putStrLn. show) llistaNoBuida
           Nothing -> putStrLn "alguna de les llistes era buida"
fallada monàdica en els efectes col·laterals - el transformadors EitherT i MaybeT[modifica | modifica el codi]

La fallada en la mònada IO es produeix llançant una excepció IOError.[145]

En el cas de la mònada ST, el comportament no està definit. La implementació per defecte crida la rutina error disparant una excepció ErrorCall.[146]

Per afegir-hi la funcionalitat de l'element absorbent en (>>=) cal aplicar-hi un transformador de mònades que la incorpori.

El transformador de mònades EitherT,[147] de manera similar al MaybeT, afegeix la possibilitat d'evitar càlculs posteriors a la fallada, a una mònada, afegint-hi a més a més, informació de l'error. Aquí l'aplicarem a la mònada IO, resultant la mònada (EitherT tipError IO).

{-# LANGUAGE PackageImports #-}
import System.IO
import "either" Control.Monad.Trans.Either (EitherT (..))
import "transformers" Control.Monad.Trans.Class (lift)
 
-- el tipus EitherT, definit a Control.Monad.Trans.Either
-- newtype EitherT err mònada a = EitherT { runEitherT :: mònada (Either err a) }
 
-- errors de l'aplicació
data Err = ErrLectura | ErrForaMarges Int deriving (Eq)
 
instance Show Err where
  show ErrLectura = "error de lectura"
  show (ErrForaMarges x) = "fora marges: " ++ show x
 
-- funció total -- en comptes d'excepció retorna fallada amb l'error
llegirSencer :: StringEither Err Int 
llegirSencer s = case reads s of
    [(x, "")]return x
    _         → Left ErrLectura     -- elements absorbents de la mònada Either
 
x.$ f = f x      -- aplicació cap enrere
 
deEither_a_EitherTIO :: Either err a → EitherT err IO a
deEither_a_EitherTIO = EitherT. return
 
llegeixSencerDEntrada :: EitherT Err IO Int
llegeixSencerDEntrada = do
                        s <- lift getLine
                        s.$ llegirSencer
                         .$ deEither_a_EitherTIO
 
-- apliquem encadenaments sobre el resultat de la lectura, segurs que no petarà.
 
comprovaMarges :: Int → EitherT Err IO Int
comprovaMarges x = if x >= 0 
                      then return x
                      else Left (ErrForaMarges x).$ deEither_a_EitherTIO
 
obtenirLArrelQuadrada :: Int → EitherT err IO Float
obtenirLArrelQuadrada = return. sqrt. fromIntegral              -- versió tàcita
 
main = do
        eiResult <- runEitherT $ llegeixSencerDEntrada >>= comprovaMarges >>= obtenirLArrelQuadrada
        case eiResult of
             Right v → putStrLn $ "resultat: " ++ show v
             Left err → putStrLn $ "error: " ++ show err
cabal install either
runhaskell prova.hs

Punts de control al codi amb Loch-TH[modifica | modifica el codi]

El paquet loch-th[148] aporta una solució a les assercions fallides, generant automàticament en els punts de control $check <expr>, amb template haskell, gestors d'excepcions sobre l'expressió que prefixen la situació (fitxer : línia : columnes) del $check al missatge d'error.

module Erroni where
 
import Control.Exception (assert)
 
erroni :: String
erroni = assert False "abc"
-- fitxer prova.hs
{-# LANGUAGE PackageImports, TemplateHaskell #-}
import "loch-th" Debug.Trace.LocationTH (check)
import Erroni
 
msg = $check erroni
 
main = print ($check msg)

la seva execució, en cas d'excepció, mostra les posicions dels $check travessats:

ghc --make prova.hs Erroni.hs -o prova
./prova

prova: prova.hs:8:14-19: prova.hs:6:6-11: Erroni.hs:6:9-14: Assertion failed

Loch-TH i els constructors[modifica | modifica el codi]

$check avalua amb evaluate que ho fa a WHNF. Degut a això, quan hi ha constructors pel mig cal avaluar estrictament el component que interessa. Vegeu explicació a Haskell / estrictesa

force del paquet "deepseq"[149] avalua estrictament també els components (avaluació a Forma Normal), si el tipus implementa la classe NFData del mateix paquet. El paquet deepseq-th aporta la derivació genèrica d'instàncies NFData per a tipus de dades algebraics.

{-# LANGUAGE PackageImports, ScopedTypeVariables, TemplateHaskell, BangPatterns #-}
{-# LANGUAGE UnicodeSyntax #-} -- per les fletxes Unicode
 
import Prelude hiding (catch) -- El catch del Prelude no detecta excepcions en el codi funcional
import Control.Exception (catch, SomeException)
import Control.Monad
 
import "loch-th" Debug.Trace.LocationTH (check)
import "deepseq" Control.DeepSeq (force)
 
erroni1 = $check (head []) : "abc"  -- sí que prefixa posició
erroni2 = $check (head [] : "abc")  -- no prefixa la posició (els components del constructor (:) no s'avaluen)
erroni3 = $check (head [] `consEstricteALElem` "abc")  -- amb ! a l'elem a WHNF, sí que prefixa la posició
erroni4 = $check (force (head [] : "abc")) -- amb 'force' de deepseq a Forma Normal, sí que prefixa la posició
 
consEstricteALElem !x xs = x:xs      -- ! al paràmetre, l'avalua a ''WHNF'' (vegeu ext. BangPatterns)
 
prova :: String -> String -> IO ()
prova títol cas = do
  putStr $ "cas " ++ títol ++ ": "
  putStrLn cas `catch` (\(excep :: SomeException)print excep)
 
main = do 
  forM_ [("erroni1", erroni1),("erroni2", erroni2),
         ("erroni3", erroni3),("erroni4", erroni4)] 
         $ \(títol, cas) -> prova títol cas

dóna, mostrant si s'avalua o no el codi erroni, assenyalant-ne la posició:

$ runhaskell prova
cas erroni1: prova.hs:11:11-16: Prelude.head: empty list
cas erroni2: Prelude.head: empty list
cas erroni3: prova.hs:13:11-16: Prelude.head: empty list
cas erroni4: prova.hs:14:11-16: Prelude.head: empty list

Traçabilitat[modifica | modifica el codi]

Vegeu ref.[150]

En el codi funcional[modifica | modifica el codi]

L'ordre d'impressió de diverses traces en una expressió serà aleatori!!, perquè l'ordre d'execució de subexpressions en el codi funcional és indeterminat.

  • trace: mostra un missatge mitjançant unsafePerformIO
  • traceShow: mostra el valor d'una variable textualitzable
  • traceStack (des de GHC 7.4): Cal compilar amb la biblio. de perfilat (ang:profiling) com trace afegint la pila de crides simulada de l'estudi de costos.

En el codi monàdic[modifica | modifica el codi]

  • traceIO: mostra el missatge des d'una expressió de la mònada IO, retornant unit.
  • traceM: mostra el missatge des d'una expressió mònada no IO, retornant unit.
  • traceShowM: mostra el valor d'una variable textualitzable en una mònada, retornant unit.

EventLog Tracing[modifica | modifica el codi]

És una traça per a registre d'esdeveniments per a ser llegides per eines per a l'ajustatge del rendiment (perfilat, ang:profiling).[151]

  • Cal compilar amb l'opció -eventLog.[152]
  • per a mostrar la traça textualment, cal compilar a més a més amb -debug i executar amb +RTS -v[flags].[153] Mostra les traces pel dispositiu de sortida stdout.
  • per a un ús de la traça per eines de perfilat, s'envien les traces al fitxer nom_programa.eventlog, amb codificació binària (més compacte), executant amb +RTS -l[flags].[153]

Exemple:

import Control.Monad
import Debug.Trace
 
main = forM_ [1..6] $ \cnt -> do
                traceEventIO $ "cnt: " ++ show cnt
$ ghc --make -eventlog -debug -rtsopts prova.hs
Linking prova...
$./prova +RTS -vu
...

Més problemàtiques[modifica | modifica el codi]

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.[154]

  • Amb la mònada Reader[155] 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").[156] 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[157][158]
    • Segons Oleg Kiseliov i Chung-chieh Shan[159]

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

unsafePerformIO[160] possibilita definir variables de configuració globals accessibles des de codi funcional pur, sense necessitat de passar-les com a paràmetre, trencant el principi de transparència referencial (constància del resultat d'una expressió amb les mateixes entrades) per la dependència de l'estat de la variable.

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".)[161]

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

unsafePerformIO[160] és la porta del darrere de la mònada IO.[161]

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).[160]
{-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}  -- recomanació de System.IO.Unsafe
import System.IO.Unsafe
import Data.IORef
 
-- variable global, a l'estil del llenguatge ML, però fora de la mònada IO, 
-- trenca la transparència referencial de les expressions de codi pur on s'utilitzi
 
{-# NOINLINE intRef #-} -- recomanació de System.IO.Unsafe
intRef :: IORef Int                     -- variable global fora de la mònada IO
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 avaluació diferida (''thunk'')
        writeIORef intRef 42
        print $ llegeix_100_cops ()
        writeIORef intRef 30
        print $ llegeix_100_cops ()

El problema del sobreiximent de la pila[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[132] i la secció Haskell#Avaluació estricta explícita (treballar a espai constant de la pila). També "Retainer profiling" per investigar "Perquè l'avaluació d'un objecte és retinguda"[163]

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[164] aporta funcions d'avaluació a Forma Normal que sobrepassen les limitacions de seq. El paquet deepseq-th[165] facilita la derivació d'instàncies de la classe NFData (contracció de NormalFormData), 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","[166] Space leak zoo"[167]

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

El problema infernal de les dependències de biblioteques (ang: cabal dependency hell)[modifica | modifica el codi]

El relligat requereix que hi hagi una sola versió de cadascuna de les biblioteques relligades.

El desenvolupament en aïllament (sandbox) es fa per evitar dependre de biblioteques instal·lades en l'entorn d'usuari, subjectes a alteracions per la instaŀlació d'aplicacions i també per evitar que estiguin relligades amb biblioteques instaŀlades relligades al seu torn amb versions diferents d'una mateixa biblioteca.

cabal sandbox init inicialitza un dipòsit de biblioteques exclusiu del projecte,[169] que impedeix tenir múltiples versions instal·lades d'una mateixa biblio.

Tanmateix la incorporació a posteriori de biblioteques en un projecte, pot portar a la situació que les dependències de la nova siguin incompatibles amb el conjunt de biblioteques instal·lades, i calgui eliminar el dipòsit i reintentar-ho de cap i de nou, havent de reinstal·lar-ho tot, amb la consegüent aturada d'hores que suposa la recompilació de tot plegat.

Stackage - rebost alternatiu amb biblioteques verificades[modifica | modifica el codi]

Stackage[170] és un rebost de paquets de biblioteques amb versions de compilació verificada respecte a una versió del GHC i de les biblioteques dependents.

Stackage notifica a l'autor d'una biblioteca del seu rebost en el moment que l'actualització una biblioteca dependent trenca la compatibilitat, i fins que no l'actualitzi queda exclosa del rebost, del qual se'n conserven còpies antigues disponibles com a imatges momentànies (ang.:snapshot).[171]

Stackage, contracció de "Stable, Vetted Hackage", és una iniciativa[172] dels creadors de l'entorn de desenvolupament web Yesod, auspiciada per FPComplete.[173]

Caldrà especificar al fitxer de configuració .cabal/config el nou rebost en la propietat

remote-repo: stackage:<url-del-rebost>

on la url-del-rebost cal obtenir-la de la darrera versió del stackage corresponent a la versió del GHC desitjada del llistat de la pàgina: Imatges momentànies (ang:Snapshots) de l'stackage, descarregant a continuació l'índex del nou rebost mitjançant cabal update.

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

Vegeu

  • "Més aviat: com fer que un programa compili més depressa".[174]
  • "Més ràpid: com fer que un programa corri més".[175]
  • "Més petit: com fer que un programa ocupi menys".[176]
  • "Més auster: com fer que un programa engoleixi menys memòria dinàmica".[177]

Optimitzacions d'allotjament[modifica | modifica el codi]

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

Boxing és el procés de convertir una dada que es passa per valor en un objecte que es passa per referència. Unboxing és el procés invers.

Els valors no encapsulats eviten l'allotjament en memòria dinàmica, fent que el compilador allotgi el valor del literal amb el codi.

Cal que el compilador generi les instruccions de màquina adequades, a partir d'operadors, tipus i literals que GHC especifica afegint un símbol # al darrera:

  • tipus: Int#, Float#, Double#
  • operadors: (+#), (-#), (*#), (==#)
  • literals: 4# 3.2# 'c'#

Cal, però, especificar la pragma d'extensió de llenguatge {-# LANGUAGE MagicHash #-} per què el compilador accepti la sintaxi ampliada.

Restriccions: No es poden emprar valors unboxed a les funcions polimòrfiques (perquè no tenen descriptor de tipus).[178]

De unboxed a boxed
  • el tipus de 4# és Int# ;
  • Amb el constructor I# de GHC.Exts, el tipus de (I# 4#) és Int
De boxed a unboxed per encaix
let !(I# unboxed_int) = 5::Int

Vegeu[179] i el farragós exemple següent:

{-# LANGUAGE MagicHash, BangPatterns #-}
import GHC.Exts (Int#, Int( I#)) -- importa el tipus Int# i el constructor I# del tipus Int
import GHC.Prim ((*#),(-#), (==#), (>#))
 
fact_rf :: Int# → Int# → Int#
fact_rf !acum !n
  | n ==# 0# = acum
  | n ># 0# = fact_rf (acum *# n) (n -# 1#)
 
imprimeix :: Int# → IO ()
imprimeix unboxed_int = do
   putStrLn $ "resultat " ++ show (I# unboxed_int) -- de unboxed a boxed aplicant el constructor
 
main = do
  let !(I# unboxed_int) = 5::Int   -- de boxed a unboxed per encaix (requereix estrictesa(!))
      result = fact_rf 1# unboxed_int
  imprimeix result

Desencapsulament en un tipus - La pragma UNPACK[modifica | modifica el codi]

Desempaqueta un component d'un constructor de dades en el mateix constructor evitant un nivell d'indirecció.[180]

data T = T {-# UNPACK #-} !Float
           {-# UNPACK #-} !Float

La pragma {-# OPTIONS_GHC -funbox-strict-fields #-} ho fa automàtic.

Des de ghc 7.8, la optimització UNPACK ve per defecte, per als components estrictes de mida "petita" (igual o inferior a la de la paraula de la màquina, i també per als de coma flotant).[181]

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

Les tuples unboxed[182]

(# a, b #)

són un capítol a part i s'utilitzen per retornar més d'un valor alhora, sense allotjar la tupla a la memòria dinàmica. Cal especificar l'extensió de lleng. UnboxedTuples i l'opció -fobject-code (codi nadiu, doncs a codi intermedi (bytecode) no compila).

-- fitxer tuples-no-encapsulades.hs
{-# OPTIONS_GHC -fobject-code #-}
{-# LANGUAGE UnboxedTuples #-}
 
f x y = (# x+1, y-1 #)  -- retorn amb tupla ''no encapsulada''
 
g x = case f x x of
 
    (# a, b #) → a + b  -- obrim la tupla per encaix del patró ''(#... #)''
 
main = do
     let y = g 5
     putStr "y = "
     print y

Compilació i exec.

ghc —make tuples-no-encapsulades.hs

./tuples-no-encapsulades

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

L'especialització es fa servir per millorar el rendiment en les operacions sobrecarregades, quan es fan servir molt amb un tipus concret. Cal especificar la pragma SPECIALIZE.[183]

{-# LANGUAGE BangPatterns #-}
 
factorial :: (Num a, Ord a) => a → a
factorial 0 = 1
factorial n
 | n > 0 = factorial_rf 1 n
 | otherwise = error "n no pot ser negatiu"
 where
        factorial_rf !acum !n  -- aval. estricta als paràmetres formals
              | n == 1 = acum
              | n > 1  = factorial_rf (acum * n) (n-1)
 
{-# SPECIALIZE factorial :: Int → Int #-}
{-# SPECIALIZE factorial :: Integer → Integer #-}

-- en detectar el compilador el tipus esmentat a la pragma, genera versions no sobrecarregades, més eficients, de la funció.

Safe Haskell[modifica | modifica el codi]

Vegeu ref.[184] És una extensió de llenguatge per restringir l'ús de diverses construccions, com ara unsafePerformIO[160] que permet trencar la transparència referencial de les expressions funcionals pures (constància del resultat en diferents invocacions amb les mateixes entrades).

Pretén assegurar les propietats següents:

  • seguretat dels tipus
  • transparència referencial
  • encapsulament estricte de mòduls
  • raonament modular
  • consistència semàntica.

Cal esmentar la pragma {-# LANGUAGE Safe #-}.

JavaScript com a rerefons de GHC (Haskell al navegador web)[modifica | modifica el codi]

JavaScript té el desavantatge de tenir un codi molt fràgil, però és una eina que s'hi pot comptar a tots els navegadors web.[185]

Haste[186][187][188][189] i també GhcJs[190][191] ofereixen alternatives de generació de codi Javascript partint de la sortida STG del compilador.[192]

Permeten utilitzar primitives específiques juntament amb pràcticament qualsevol codi Haskell compilable amb GHC, i generar-ne codi Javascript.

Exemple amb Haste, instaŀlació (cal versió cabal-1.18+ per la compilació aïllada):

mkdir haste && cd haste         # crea carpeta per la compilació aïllada
 
cabal sandbox init              # inicialitza un dipòsit de biblioteques exclusiu del projecte
cabal update
cabal install haste-compiler
# 
export PATH=$PWD/.cabal-sandbox/bin:$PATH     # afegeix carpeta d'executables generats
 
haste-boot      # inicialitza i compila a Javascript les biblioteques bàsiques de GHC.

Programa d'exemple[186] que mostra en un element html amb id="sortida" el contingut de la casella de text (elem. input type="text") amb id="entrada_de_text"

import Haste
 
main = do
  Just inp  <- elemById "entrada_de_text"
  Just outp <- elemById "sortida"
 
  onEvent inp OnKeyUp $ \_ -> do
      text <- getProp inp "value"
      setProp outp "innerHTML" text
# compilació, genera "prova.js"
hastec prova.hs   
 
# caldrà incloure (<script src="prova.js" type="text/javascript"></script>) 
#   al codi de capçalera de la pàg. html

Eines[modifica | modifica el codi]

hlint
Aquí[193] analitzador de codi amb suggerències de millora. Avisa de construccions redundants i proposa alternatives al codi.
cabal-dev
(ang:"Sandboxed haskell build environments") Permet desenvolupar un projecte cabal amb un dipòsit de biblioteques específic del projecte (sub-carpeta cabal-dev/), per aïllar-se d'interaccions amb les biblioteques de l'entorn d'usuari/sistema corresponents a les aplicacions instal·lades. Caldrà afegir a la var. d'entorn PATH la carpeta cabal-dev/bin.[194]
  • actualment la funcionalitat de cabal-dev ha estat incorporada al programa cabal amb el verb sandbox. cabal sandbox init inicialitza el dipòsit de biblioteques del projecte en el subdirectori .cabal-sandbox deixant els executables a .cabal-sandbox/bin.
cabal-meta (cabal multi-projecte)
.- obté la llista de subprojectes d'un fitxer sources.txt que pot referenciar altres directoris que també en continguin, per un tractament recursiu.[195]
.- instal·la (amb cabal-dev cas de --dev), en una sola ordre (resolució conjunta de dependències) diversos paquets (del hackage o bé subprojectes de directoris locals o remots (git))
.- als fitxers sources.txt cada línia pot contenir:
  • cas de començar per '.' o bé '/': directori (seguit d'opcions) que pot contenir un altre fitxer sources.txt i/o un projecte 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ó*
cabal-ghci
engega ghci preparant-lo per a proves del projecte, amb camins i extensions de llenguatge obtinguts del fitxer de projecte.cabal[196]
cabal-progdeps
llista les dependències del projecte del directori de treball partint del fitxer setup-config del subdir. dist.[197]
yackage
servidor de rebost "hackage" local per a desenvolupament.[198]
ThreadScope
Visualitzador gràfic de les traces d'esdeveniments corresponents a cada "capacitat" (fil d'exec. corresp a un processador elemental, sobre el qual s'executen els fils d'exec. lleugers).[199] Estudi "Ajustatge fi del paral·lelisme amb ThreadScope".[200]
hpc (haskell program coverage)
Anàlisi de cobertura del codi. Mostra, després de diverses execucions, amb marcatge de colors quines parts del codi no s'han executat mai i condicions sempre certes o sempre falses.[201]
  1. compilar amb l'opció -fhpc
  2. executar diverses vegades amb diferents entrades
  3. invocar: hpc markup nom_executable (genera hpc_index.html i altres html)
  4. obrir hpc_index.html amb un navegador
  5. per recomençar l'estudi, i també després de recompilar, cal eliminar el fitxer nom_executable.tix on guarda les traces d'execució.
hp2any (obtenció del perfil d'ús de memòria)
  • hp2any-graph mostra en temps-real un gràfic de l'evolució de l'ocup. de memòria.[202]
  • hp2any-manager mostra gràfics de fitxers de perfil (.hp) corresp. a execucions ja finalitzades.
més eines de desenvolupament al HaskellWiki

Vegeu ref.[203]

Exemples[modifica | modifica el codi]

Degut a un robot vikipèdic que converteix les fletxes del codi -> en →, per poder compilar els exemples cal afegir l'extensió de llenguatge UnicodeSyntax, com ara {-# LANGUAGE UnicodeSyntax #-}.

Arrencada amb opcions[modifica | modifica el codi]

Per afegir opcions a l'intèrpret d'ordres vegeu l'enllaç.[204][205][206]

-- per una lectura correcta dels caràcters no anglosaxons a l'intèrpret de comandes a Linux (''shell'')
--  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 System.Console.GetOpt
 
import Data.Maybe( fromMaybe )
import Text.Printf (printf)
 
-- tipus per a les opcions de l'intèrpret d'ordres (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 3r de la tupla)
   (flagsCorrectes, [],   [])  -> processa flagsCorrectes
   -- altrament
   (_,   _,    msgs_error)  -> error_als_args nom_prog msgs_error

Excepcions de tipus definits per l'usuari[modifica | modifica el codi]

Augmenta les possibilitats d'excepcions del Haskell98, amb tipus nous que només han d'instanciar la implementació per defecte de la classe Exception.[207]

 -- per implementar Exception, cal que prèviament implementin les classes requerides pel context
 
 class (Typeable e, Show e) => Exception e
{-# LANGUAGE DeriveDataTypeable,    -- important !!
             ScopedTypeVariables, BangPatterns #-}
 
import Prelude hiding (catch)   -- cal fer servir el ''catch'' de Control.Exception
{- Del Prelude
-- Non-I/O exceptions are not caught by this variant; to catch all
-- exceptions, use 'Control.Exception.catch' from "Control.Exception".
-- The 'catch' function is deprecated. Please use the new exceptions
-}
import Control.Exception
import Data.Typeable
 
data TExcepcionsDeLAplicacio = EParametreIlegal String | EUnaAltraExcepcio String
     deriving (Show, Typeable)   -- important !!
 
instance Exception TExcepcionsDeLAplicacio    -- instancia els mètodes per defecte de la classe ''Exception''
 
factorial :: IntMaybe Integer
factorial n | n == 0 =  Just 1
            | n > 0 = Just (fac_rf n 1)
            | otherwise = Nothing
           where  -- aval. estricta (!) als param. formals per evitar ''pila'' d'expr. pendents d'avaluar
             fac_rf !m !acum | m > 0 = fac_rf (m-1) (acum * toInteger m)
                             | m == 0 = acum
 
avalua_fac :: IntIO ()
avalua_fac n = case factorial n of
                       Just r → putStrLn ("resultat: " ++ show r)
                       Nothing → throwIO (EParametreIlegal "avalua_fac: param. fora del domini")
 
prova_fac :: IntIO ()
prova_fac n = 
             ( avalua_fac n
             )
             `catch` (\ (excep::TExcepcionsDeLAplicacio)case excep of
                           EParametreIlegal msg → putStrLn  msg
                           _print excep
                           )
             `catch` (\ (excep::SomeException)print excep
                     )
 
main = prova_fac (-1)
--

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

Incorporant transformacions a les llistes per comprensió amb la clàusula then.[208] Cas d'emular una consulta SQL agrupada (Group by) cal prefixar els camps agrupats amb la clàusula the. Cal esmentar la pragma {-# LANGUAGE TransformListComp #-}

{-# LANGUAGE TransformListComp #-}
import GHC.Exts (the, groupWith, sortWith)
 
-- el prefix _ com a _nom és per evitar els ''Warnings'' en símbols no utilitzats
 
consulta = [ (the dept, avg salari)
           | (_nom, dept, salari) <- empleats  
, then group by dept using groupWith
, then sortWith by (avg salari)
, then take 5 ]
 
empleats = [ ("Xavi", "Vendes", 80)
, ("Montse", "Vendes", 100)   
, ("Vicenç", "Programació", 40) 
, ("Biel", "Programació", 45)
, ("Anna", "Comptabilitat", 60)]
 
x.$ f = f x  -- aplic. cap enrere
 
avg :: (Fractional a) => [a] → a
avg [] = 0
avg xs = sum xs / fromIntegral (xs.$ length)
 
main = print consulta

dóna

 [("Programació",42.5),("Comptabilitat",60.0),("Vendes",90.0)]

vectors a la mònada ST[modifica | modifica el codi]

La mònada ST permet elaborar funcions amb encadenament de canvis d'estat d'àmbit local, en aquest cas, de vectors, que podran ser cridades des de codi funcional.

1.- Amb vectors per a elements primitius (Unboxed) amb índexs de tipus divers, com a (UArray tipusIndex tipusElem).

Creació com a immutable, descongela a mudable (thaw), modificació, recongelació(freeze) i llistat:

import Data.Array.IArray as IArray  -- interfície vectors immutables
import Data.Array.MArray as MArray  -- interfície vectors mudables
 
import Data.Array.Unboxed   -- vector immutable UArray d'elements d'allotjament directe
import Data.Array.ST   -- vector mudable STUArray d'elements d'allotjament directe a la mònada ST
import Control.Monad.ST  -- encapsula canvis d'estat local dins de codi funcional  
 
modificaArray :: Int → UArray Int Int → ST s (UArray Int Int)
modificaArray indx immVect = do
      mutVect <- thaw immVect :: ST s (STUArray s Int Int)  -- descongela a mudable
      x <- readArray mutVect indx
      writeArray mutVect indx (x - 1)
      freeze mutVect                                        -- congela a immutable
 
main = do
      let immVect = IArray.listArray (0,1) [0,1] :: UArray Int Int
          immVect2 = runST $ modificaArray 0 immVect
      print $ elems immVect2

2.- Amb vectors més eficients, amb tipus de l'índex fix (Int amb rang a partir de 0) i tipus del vector (Vector tipusElem) del paquet Vector.[86]

{-# LANGUAGE UnicodeSyntax #-}
 
import Data.Vector.Unboxed (Vector, MVector)
import Data.Vector.Unboxed as V
import Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST
 
modificaArray :: Int →  Vector Int -> ST s (Vector Int)
modificaArray indx immVect = do
      mutVect <- V.thaw immVect :: ST s (MVector s Int)  -- descongela a mudable
      x <- MV.read mutVect indx
      MV.write mutVect indx (x - 1)
      V.freeze mutVect                                   -- congela a immutable
 
main = do
      let immVect = V.fromList [0,1] :: Vector Int
          immVect2 = runST $ modificaArray 0 immVect
      print $ V.toList immVect2

diccionaris eficients amb HashMap[modifica | modifica el codi]

Hashable[77] aporta instàncies de funció resum per als tipus més freqüents, permetent l'ús eficient de diccionaris (HashMap) i conjunts (HashSet) basats en taules de dispersió, anomenades HashTable.

HashMap i HashSet són més ràpids que les implementacions basades en arbres, especialment quan la comparació de claus és lenta, com és el cas del tipus String.[78]

L'anterior implementació de la biblioteca HashMap (requereix claus ordenables, implementada mitjançant IntSet o bé IntMap que allotja o bé un parell (clau, valor) si hi ha una única imatge de la funció resum sobre la clau, o bé una estructura Set/Map en arbre balancejat amb els parells (clau, valor) quines imatges de la funció resum sobre la clau coŀlideixen), ha quedat desaconsellada en favor de la biblioteca unordered-containers (no requereix claus ordenables) que ve amb la Haskell Platform.

{-# LANGUAGE OverloadedStrings #-}
 
import Control.Exception (assert)
import Data.Text (Text)
import qualified Data.Text.IO as TextIO
 
import Data.HashMap.Lazy (HashMap, member, (!), lookup)  -- aquí també s'importen les instàncies de Hashable
import qualified Data.HashMap.Lazy as Map
 
-- el tipus Text que implementa Hashable, serà la clau del diccionari eficient
 
dicc :: HashMap Text Text              
dicc = Map.fromList [("primer", "el que va davant"),
                     ("segon", "el que ve després")]
 
main = do
         assert (Map.member "primer" dicc) -- segur que hi és
                 $ TextIO.putStrLn $ dicc ! "primer"  -- (!) és Map.!
 
         let mbDescrip = Map.lookup "segon" dicc
         case mbDescrip of
              Just txt → TextIO.putStrLn txt
              Nothing → TextIO.putStrLn "el segon no hi era"
# el paquet "unordered-containers" que conté Data.HashMap.Lazy ve amb la plataforma Haskell
runhaskell prova.hs

arbres[modifica | modifica el codi]

Vegeu ref.[209]

{-# LANGUAGE UnicodeSyntax #-}
import Data.Tree (Tree)
import qualified Data.Tree as Tree
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Tuple (swap)
import Data.Maybe
 
-- definició del tipus arbre a Data.Tree
-- data Tree a = Node {rootLabel :: a, subForest :: [Tree a]}
 
-- generador d'arbre
-- Tree.unfoldTree :: (b → (a, [b])) → b → Tree a
 
data Espècie = Animal | Vertebrat | Mamífer | Primat | Humà | Mico
                  deriving (Eq, Ord, Enum, Show)
 
parentiu = [(Vertebrat, Animal), (Mamífer, Vertebrat), (Primat, Mamífer),
            (Humà, Primat), (Mico, Primat)]
 
subespècies :: Map Espècie [Espècie]
subespècies = Map.fromListWith (++)     -- combina elements de claus ja existents amb (++)
                   $ fmap (elemALlista. capgira) parentiu
  where
    -- elemALlista = fmap (\x -> [x])  -- versió tàcita
    elemALlista (k, e) = (k, [e])      -- versió explícita
    capgira = swap
 
-- funció per generar l'arbre
jerarquitzador :: Espècie → (Espècie, [Espècie])
jerarquitzador x = (etiquetaNus, fills)
  where
    etiquetaNus = x 
    fills = Map.lookup x subespècies
                   .$ fromMaybe []   -- llista buida si no és al diccionari
 
elMeuArbre :: Tree Espècie
elMeuArbre = Tree.unfoldTree jerarquitzador Animal
 
x .$ f = f x     -- aplicació cap enrere (com l'op. "pipeline" del llenguatge F#)
 
imprimeixArbre :: (Show a) => Tree a → IO ()
imprimeixArbre arb = arb
       .$ fmap show      -- l'arbre és instància de Functor
       .$ Tree.drawTree  -- genera repr. String d'un arbre de Strings
       .$ putStrLn
 
main = imprimeixArbre elMeuArbre

dóna el resultat:

Animal
|
`- Vertebrat
   |
   `- Mamífer
      |
      `- Primat
         |
         +- Mico
         |
         `- Humà

Combinació de filtres[modifica | modifica el codi]

La classe Applicative permet combinar els resultats d'una seqüència de computacions.

Prenem com a tipus que l'implementa, les funcions amb entrada del mateix tipus.

-- Del codi font del mòdul Control.Applicative
 
-- implementació de Applicative de les funcions amb entrada del mateix tipus ''a''
instance Applicative ((->) a) where
    pure = const              -- generador del tipus ((->) a), que ignora l'entrada
    (<*>) f g x = f x (g x)   -- combina funcions aplicant-los-hi la mateixa entrada ''x''

Exemple de combinació.

import Control.Applicative
 
ésMúltipleDe :: Integral a -> a -> a -> Bool
ésMúltipleDe x = (== 0). (`mod` x)
 
-- notació amb les operacions canòniques ''pure'' i (<*>)
ésMúltipleDe3oDe5 :: Integral a => a -> Bool
ésMúltipleDe3oDe5 = pure (||) <*> ésMúltipleDe 3 <*> ésMúltipleDe 5
 
-- notació amb liftAn definida a Control.Applicative
ésMúltipleDe3oDe5 :: Integral a => a -> Bool
ésMúltipleDe3oDe5 = liftA2 (||) (ésMúltipleDe 3) (ésMúltipleDe 5)
 
-- notació amb els operadors binaris Applicative típics (<$>) i (<*>)
ésMúltipleDe3oDe5 = (||) <$> ésMúltipleDe 3 <*> ésMúltipleDe 5
 
-- també podem definir:
(<||>) :: Applicative f => f Bool -> f Bool -> f Bool
(<||>) = liftA2 (||)
 
-- llavors
ésMúltipleDe3oDe5 = ésMúltipleDe 3 <||> ésMúltipleDe 5

QuickSort amb Data Parallel Haskell[modifica | modifica el codi]

Vegeu #Data Parallel Haskell i ref.[43]

1. Mòdul d'operacions vectoritzades

{-# LANGUAGE PackageImports, ParallelArrays #-}
{-# OPTIONS -fvectorise #-}
{-# OPTIONS -fno-spec-constr-count #-}
module QSortVect (quicksortPA) where
import "dph-lifted-vseg" Data.Array.Parallel
import "dph-lifted-vseg" Data.Array.Parallel.Prelude.Double        as D
import qualified "dph-lifted-vseg" Data.Array.Parallel.Prelude.Int as I
import qualified Prelude
 
{-# NOINLINE quicksortPA #-}
quicksortPA:: PArray Double → PArray Double
quicksortPA xs = toPArrayP  (qsortVect (fromPArrayP xs))
 
qsortVect:: [: Double :][: Double :]
qsortVect xs 
  | lengthP xs I.<= 1 = xs
  | otherwise =
     let pivot  = xs !: 0
         menors = [: x | x <- xs, x D.< pivot :]
         majors = [: x | x <- xs, x D.> pivot :]
         iguals = [: x | x <- xs, x D.== pivot :]  
 
         -- mapeja en paraŀlel qsortVect als subvectors (Nested Data Parallellism) !!
         vectors = mapP qsortVect [: menors, majors :]    
     in 
       (vectors !: 0)  +:+  iguals  +:+  (vectors !: 1)

2. Mòdul d'operacions NO-vectoritzades

{- Main.hs -}
{-# LANGUAGE PackageImports #-}
import System.Environment
import Text.Printf
 
import "dph-lifted-vseg" Data.Array.Parallel.PArray (fromList, toList)
import QSortVect (quicksortPA)
 
llistaDeProva :: Int[Double]
llistaDeProva n = map toDouble $ [n,(n-1)..1] 
 
toDouble n = realToFrac n :: Double
 
ordena n = toList ( quicksortPA ( fromList $ llistaDeProva n))
 
main = do
        args <- getArgs
        nomProg <- getProgName
        case args of
             [arg]do
                     let num = read arg :: Int
                     print $ take 5 $ ordena $ abs num
 
             _ → printf "afegiu nombre d'elements\nús: %s 999\n" nomProg
  • Compila i executa
prompt$ ghc --make -threaded -rtsopts -with-rtsopts=-N -Odph Main.hs QSortVect.hs -o main -package dph-lifted-vseg
 
prompt$ time./main 100000
[1.0,2.0,3.0,4.0,5.0]
 
real    0m4.065s
user    0m5.552s
sys     0m1.480s

Reducció en paral·lel mitjançant estratègies[modifica | modifica el codi]

{-# LANGUAGE PackageImports #-}
import "parallel" Control.Parallel.Strategies
import "HUnit" Test.HUnit
import Control.Exception (assert)
 
-- de la definició: type Strategy a = a -> Eval a
 
estratSumaSegment :: Num a => Strategy [a]
-- equival a 
-- estratSumaSegment :: Num a => [a] -> Eval [a]
 
estratSumaSegment llista = return [sum llista] 
 
-- parteix llista en segments de n elements
segments :: Int -> [a] -> [[a]]
segments _ [] = [[]]
segments n xs = if null zs then [ys]
                  else ys : segments n zs   -- recursivitat final diferida (''mòdulo cons'')
  where (ys, zs) = splitAt n xs    -- parteix a l'índex n
 
sumaEnParaŀlel :: Num a => Int -> [a] -> a
sumaEnParaŀlel llargadaSegment llista = assert (llargadaSegment > 1) -- precondició
                   $ sum $ concat totalsSegments
  where
      totalsSegments = segments llargadaSegment llista 
                         `using` parList (rseq `dot` estratSumaSegment) -- rseq força avaluació dins el fil paraŀlel
 
prova llista = TestCase (assertEqual msg esperat calculat)
  where
      calculat = sumaEnParaŀlel 1000 llista
      esperat = sum llista
      msg = "sumaEnParaŀlel en segments de 1000"
 
tests = TestList [TestLabel "test1" $ prova [1..10000]]
 
main = runTestTT tests

Interfícies gràfiques[modifica | modifica el codi]

Hola món amb GTK[modifica | modifica el codi]

Web del projecte GTK per a Haskell aquí.[210]

Cal que el sistema tingui instal·lades versions de desenvolupament (acabades en -dev) de les biblioteques gràfiques

cabal install gtk2hs-buildtools gtk
import Graphics.UI.Gtk
 
gestorEnClicarBotó :: IO ()
gestorEnClicarBotó = putStrLn "Has clicat el botó Hola món"
 
main :: IO ()
main = do
  initGUI
  finestra <- windowNew
  botó <- buttonNew
 
  set finestra [ containerBorderWidth := 10,
               containerChild := button ]
 
  set botó [ buttonLabel := "Hola món" ]
 
  onClicked botó gestorEnClicarBotó
  onDestroy finestra mainQuit
 
  widgetShowAll finestra
  mainGUI

Si apareix el missatge "Undefined symbol ___gxx_personality_v0 on link" cal afegir la biblioteca stdc++ al relligat.[211]

ghc --make prova.hs -lstdc++
./prova

Hola món amb WxHaskell[modifica | modifica el codi]

WxHaskell[212] és una implementació de l'entorn gràfic Wx[213] que és multi-plataforma.

Cal haver instal·lat prèviament les biblioteques wx de desenvolupament per al sistema subjacent.

# cas de cabal v1.18+, senyal -jN per a muntatge multi-procés
 
# per a wx 2.8
cabal install "wx < 0.90"
 
# per a wx 2.9
cabal install "wx == 0.90.*"
 
# per a wx 3.x
cabal install "wx >= 0.91"
module Main where
import Graphics.UI.WX
 
gestorEnClicarBotó :: IO ()
gestorEnClicarBotó = putStrLn "Has clicat el botó Hola món"
 
hello :: IO ()
hello = do 
       frame1 <- frame [text := "Exemple"]
       botó <- button frame1 [text := "Hola món", on command := gestorEnClicarBotó]
       set frame1 [layout := widget botó]
 
main :: IO ()
main = start hello

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

Vegeu-ho a Fletxa (programació funcional)#Exemple - Extreure informació d'un document XML

Operacions als tipus - Tipus dependents de valors[modifica | modifica el codi]

Vegeu ref.[214][215]

Fins ara hi ha hagut algunes iniciatives partint dels nombres de Peano obtenint sèries curtes de tipus mono-valor anomenats en anglès singleton types[216] o definicions inductives més sofisticades.[217]

  • Remarcable: El paquet dimensional-tf incorpora als tipus les dimensions de les unitats de la física.[218]
{-# LANGUAGE PackageImports #-}
 
import Prelude hiding ((*),(^),(/),(+),(-))
import "dimensional-tf" Numeric.Units.Dimensional.TF
import "dimensional-tf" Numeric.Units.Dimensional.TF.SIUnits
import "dimensional-tf" Numeric.Units.Dimensional.TF.Quantities
 
-- el tipus Pos2 té un únic valor ''pos2'' (+2)
-- negatius amb prefix ''Neg'' als tipus o bé ''neg'' per als valors
 
import "numtype-tf" Numeric.NumType.TF (pos2, pos3, neg2, Pos2, Pos3, Neg2)
 
-- les expressions són del tipus :: Quantity Dimensions a
-- on Dimensions és un vector de potències de cada unitat implementat com a tipus producte.
--   Dimensions es pot expressar en funció de vectors unitaris (DLength, DTime,...)
 
--   type DLength = Dim Pos1 Zero Zero Zero Zero Zero Zero
--   type DArea = Dim Pos2 Zero Zero Zero Zero Zero Zero
 
--   i també de l'aplicació de les famílies de tipus {Mul, Div, Pow} 
--   corresp. a producte, divisió i exponenciació, definides a Numeric.Units.Dimensional.TF.
 
--   type DArea = Pow DLength Pos2
--   type Area = Quantity DArea
 
llargada = 4 *~ kilo meter :: Quantity DLength Float    -- sinònim :: Length Float
area = 10 *~ meter ^ pos2 :: Quantity (Pow DLength Pos2) Float   -- sinònim :: Area Float
 
-- les anotacions de tipus permeten que el compilador validi les operacions
 
volum ::  Quantity (Pow DLength Pos3) Float    -- dimLlargada elevada a la potència Pos3
volum = llargada * area   
 
temps = 10 *~ minute :: Quantity DTime Float    -- sinònim :: Time Float
 
tempsAlQuadrat = 10 *~ second ^ pos2 :: Quantity (Pow DTime Pos2) Float 
 
-- :: (Acceleration Float) també es pot expressar com segueix:
 
acceleració :: Quantity (Div DLength (Pow DTime Pos2)) Float 
 
-- acceleració :: Quantity (Mul DLength (Pow DTime Neg2)) Float  -- alternativa
 
acceleració = llargada / tempsAlQuadrat  
 
main = do
  putStrLn $ "volum: " ++ show volum
  putStrLn $ "acceleració: " ++ show acceleració

passa la comprovació de tipus i dóna:

volum: 40000.0 m^3
acceleració: 400.0 m s^-2                -- 4000 m / 10 s^2

La proposta TypeNats[219] ("Type level natural numbers"), parcialment implementada a GHC 7.6.1, permetrà una implementació més eficient (requereix l'extensió DataKinds).[220]

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

import qualified Numeric
import qualified Char
 
precisióSencers = "Int.MAX: " ++ (map Char.toUpper hexIntMax)
  where
    hexIntMax = Numeric.showHex límitSuperiorSencers "\n"
    límitSuperiorSencers = maxBound::Int
 
precisióComaFlotant :: (RealFloat a) => a → String
precisióComaFlotant nombre = mostraPrecisióSignificand ++ "; " ++ mostraRangExponent
  where
    mostraPrecisióSignificand = "significand: " ++ show (floatDigits nombre) ++ " dígits"
    mostraRangExponent = "exponent (min, max): " ++ show (floatRange nombre)
 
main = do
         putStrLn precisióSencers
         putStrLn $ "Double: " ++ precisióComaFlotant (1.0::Double)
         putStrLn $ "Float:  " ++ precisióComaFlotant (1.0::Float)

En un x86-64 amb GHC:

Int.MAX: 7FFFFFFFFFFFFFFF

Double: significand: 53 dígits;  exponent (min, max): (-1021,1024)
Float:  significand: 24 dígits;  exponent (min, max): (-125,128)

Els sencers ofereixen, a GHC, el rang complet corresp. a la paraula de la màquina (malgrat que l'especificació de Haskell només exigeix un mínim [-2^29.. 2^29-1]),[221] i no fan com altres compiladors (OCaml#tipus bàsics, SML#Precisió dels tipus bàsics) que sacrifiquen un bit per optimitzar el tractament intern conjunt de sencers i de punters.

En coma flotant, sembla que els valors dels exponents oferts per floatRange[222] donen un valor esbiaixat respecte a la intuïció. L'intèrpret GHCI dóna correctament

Prelude> 2^^128::Float
Infinity -- sobreiximent, sembla que 128 no hauria d'estar inclòs al rang

Prelude> isDenormalized (2^^(-126)::Float) -- tipus de representació segons IEEE 754
False -- sembla que (-126) hauria d'estar inclòs al rang

Però el truc és que l'exponent no correspon a la notació científica, sinó a l'especificació per a coma flotant del "Language Independent Arithmetic standard".[223]

  • La notació estandarditzada per a coma flotant de 1 és (en base 2): 0.1base 2 * 2^1

A ghci:

Prelude> exponent (2^0::Float)
1           -- !! en base 2: 1 == 0.1 (pel bit amagat de la mantissa) * 2^1
-- significand: signe * (bit amagat precedint la repr. mantissa IEEE 754 / 2^nombre_de_bits)
Prelude> significand (2^0::Float)  
0.5       -- signe * 1 / (2^1) -- en ésser potència de dos, la repr. de la mantissa són tot zeros
Prelude> let r = -1 :: Float
# es manté l'equació següent, fins i tot per a valors de r negatius
Prelude> r == (significand r) * (2 ^^ (exponent r))
True

Relacionat: Hi ha tres operadors d'exponenciació (^), (^^), (**) segons que el domini de l'exponent sigui natural, sencer, o real en coma flotant.

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

Encara que freqüentment és possible evitar aquesta situació quan es dissenya l'aplicació, si es tracta d'una modif. i el disseny no convé alterar-lo, caldrà trencar les importacions circulars, seguint les instruccions(),[224] creant un subconjunt del mòdul com a Mòdul.hs-boot, i referir-s'hi des dels altres mòduls afegint la pragma SOURCE a la clàusula import.

No caldrà fer-ne esment en el fitxer de projecte.cabal. El gestor de projectes cabal/cabal-dev, busca totes les extensions de fitxer per cada mòdul, trobarà l'extensió ".hs-boot" i hi aplicarà el (pre)procés adequat.

Vegeu també[modifica | modifica el codi]

Referències[modifica | modifica el codi]

  1. mmc - Compilador Mercury de Melbourne(anglès)
  2. web del Compilador Haskell de York(anglès)
  3. web del Compilador Haskell d'Utrecht(anglès)
  4. Diagrama de blocs del ghc que incorpora C--(anglès)
  5. web del C--(anglès)
  6. Univ. Tufts de Massachussets - Manual del C--(anglès)
  7. El llenguatge Cmm(anglès)
  8. Estàndard actual del llenguatge Haskell
  9. La "Plataforma Haskell"(anglès)
  10. 10,0 10,1 10,2 API de la Platforma Haskell(anglès)
  11. Anunci de la GHC 6.12.1(anglès)
  12. Documentació del compilador GHC
  13. Opcions del RunTimeSystem per controlar la mem. dinàmica i el recollidor de memòria brossa(anglès)
  14. RTS - Envelliment(anglès)
  15. RTS - Allotjament en Blocs
  16. RTS - El planificador(anglès)
  17. El recollidor de brossa(anglès)
  18. RTS - El recollidor de brossa.
  19. mòdul System.Mem(anglès)
  20. Simon Peyton Jones, et. al. - Stretching the storage manager(anglès)
  21. mòdul System.Mem.Weak(anglès)
  22. Control.Concurrent.MVar.mkWeakMVar(anglès)
  23. Ús de biblioteques d'enllaç dinàmic (anglès)
  24. Suport d'enllaç dinàmic a les diferents plataformes (anglès)
  25. Building and using Win32 DLLs(anglès)
  26. Parallel Haskell Digest(anglès)
  27. primitives de paral·lelisme(anglès)
  28. Haskell Paral·lel
  29. GHC seq vs. pseq(anglès) Comparació de les primitives seq i pseq
  30. GHC - primitiva pseq(anglès)
  31. GHC - Utilitzant multiprocés simètric SMP
  32. El planificador del GHC - fils d'exec. del sistema i fils del GHC (anglès)
  33. GHC - Hints (cat:Pistes) for using SMP parallelism (anglès)
  34. 34,0 34,1 Glasgow Parallel Haskell (anglès) Estratègies de paral·lelisme
  35. 35,0 35,1 Control.Parallel.Strategies(anglès)
  36. Seq no more: Better Strategies for Parallel Haskell(anglès) Prou de seq (seqüencial) - millors estratègies per al Haskell paral·lel
  37. haskellWiki - paral·lelisme(anglès)
  38. 38,0 38,1 HaskellWiki - A Repa Tutorial(anglès)
  39. REgular PArallel arrays(anglès)
  40. SIMD Support for the vector library (anglès)
  41. 41,0 41,1 GHC SIMD (anglès)
  42. Haskell - Paral·lelisme de dades
  43. 43,0 43,1 Nested Data Parallelism - presentació (diapos)
  44. Paquet dph-examples
  45. Guy E. Blelloch - Programming Parallel Algorithms - Nested Data Parallellism
  46. SIMD Support to Data Parallel Haskell(anglès)
  47. biblioteca accelerate-cuda(anglès)
  48. El paquet accelerate-opencl(anglès)
  49. El paquet accelerate-repa(anglès)
  50. Biblioteca Accelerate per al paral·lelisme a les GPU(anglès)
  51. paquet OpenCLWrappers(anglès)
  52. OpenCL from Haskell(anglès)
  53. How to write hybrid CPU/GPU programs with Haskell(anglès)
  54. El paquet meta-par(anglès)
  55. Template Haskell(anglès)
  56. 56,0 56,1 Yesod - plantilles(anglès)
  57. Quasi-Quotation a la viqui anglesa(anglès)
  58. Sintaxi dels quasiQuoters(anglès)
  59. QuasiQuotation
  60. Tipus algebraics del TemplateHaskell (anglès)
  61. Haskellwiki - Quasi-Quotation(anglès)
  62. Constructor QuasiQuoter al paquet template-haskell(anglès)
  63. Fusionant successius Map: Fent que Haskell sigui un 225% més ràpid (anglès)
  64. From lists to streams to nothing at all(anglès)
  65. Loop fusion (anglès)
  66. Stream fusion for Haskell Arrays (anglès)
  67. 67,0 67,1 paquet Stream-fusion(anglès)
  68. API del GHC(anglès)
  69. API Contenidors(anglès)
  70. 70,00 70,01 70,02 70,03 70,04 70,05 70,06 70,07 70,08 70,09 70,10 70,11 Data.Set
  71. 71,00 71,01 71,02 71,03 71,04 71,05 71,06 71,07 71,08 71,09 71,10 71,11 71,12 Data.Map
  72. 72,00 72,01 72,02 72,03 72,04 72,05 72,06 72,07 72,08 72,09 72,10 Data.IntSet
  73. 73,00 73,01 73,02 73,03 73,04 73,05 73,06 73,07 73,08 73,09 73,10 Data.IntMap
  74. paquet hashmap (anglès)
  75. Data.HashSet del paquet hashmap(anglès)
  76. Data.HashMap del paquet hashmap(anglès)
  77. 77,0 77,1 paquet Hashable(anglès)
  78. 78,0 78,1 El paquet unordered-containers (HashMap i HashSet que no requereixen claus ordenables)
  79. 79,00 79,01 79,02 79,03 79,04 79,05 79,06 79,07 79,08 79,09 79,10 Data.HashSet del paquet unordered-containers(anglès)
  80. 80,0 80,1 80,2 80,3 80,4 80,5 80,6 80,7 80,8 80,9 Data.HashMap.Lazy del paquet unordered-containers(anglès)
  81. 81,00 81,01 81,02 81,03 81,04 81,05 81,06 81,07 81,08 81,09 81,10 Data.Sequence(anglès)
  82. 82,0 82,1 82,2 82,3 82,4 82,5 Data.Array.Repa(anglès)
  83. paquet Vector(anglès)
  84. Haskell numèric: Guia sobre Vector(anglès)
  85. Famílies de tipus (anglès)
  86. 86,0 86,1 http://hackage.haskell.org/package/vector/docs/Data-Vector-Unboxed.html
  87. 87,0 87,1 87,2 87,3 87,4 87,5 87,6 87,7 87,8 paquet bytestring-trie(anglès)
  88. Data.DList(anglès)
  89. Demistifying DList's(anglès)
  90. 90,00 90,01 90,02 90,03 90,04 90,05 90,06 90,07 90,08 90,09 90,10 90,11 90,12 90,13 90,14 90,15 90,16 Data.Heap del paquet heap(anglès)
  91. 91,0 91,1 91,2 91,3 91,4 91,5 91,6 91,7 Data.Maybe
  92. 92,0 92,1 92,2 92,3 92,4 92,5 92,6 Data.Either
  93. 93,00 93,01 93,02 93,03 93,04 93,05 93,06 93,07 93,08 93,09 93,10 93,11 93,12 Data.List
  94. 94,00 94,01 94,02 94,03 94,04 94,05 94,06 94,07 94,08 94,09 94,10 Data.ByteString(anglès)
  95. 95,00 95,01 95,02 95,03 95,04 95,05 95,06 95,07 95,08 95,09 95,10 95,11 Data.Text(anglès)
  96. 96,0 96,1 Data.Text - Tipus intern del(anglès)
  97. 97,0 97,1 97,2 97,3 Data.Monoid(anglès)
  98. 98,0 98,1 98,2 98,3 98,4 98,5 98,6 98,7 98,8 Data.Array
  99. Data.Array.Unboxed
  100. 100,0 100,1 100,2 100,3 100,4 100,5 100,6 100,7 100,8 100,9 Data.Vector(anglès)
  101. 101,0 101,1 101,2 101,3 101,4 Data.Vector.Mutable(anglès)
  102. 102,0 102,1 102,2 102,3 102,4 102,5 Data.Array.IArray
  103. 103,0 103,1 103,2 103,3 103,4 103,5 Data.Array.MArray
  104. 104,0 104,1 104,2 104,3 104,4 104,5 104,6 104,7 Data.Tree
  105. 105,0 105,1 105,2 105,3 Data.Foldable
  106. 106,0 106,1 106,2 Data.Functor
  107. Data.Sequence.breakl(anglès)
  108. paquet mono-traversable(anglès)
  109. Data.Traversable
  110. 110,0 110,1 Functor-Applicative-Monad Proposal(anglès)
  111. HaskellWiki - MonadPlus(anglès)
  112. HaskellWiki - MonadPlus reform proposal(anglès)
  113. Codi font de Control.Monad.MonadPlus(anglès)
  114. Data.Binary(anglès) Serialització
  115. Control.DeepSeq(anglès)
  116. 116,0 116,1 Data.Data(anglès)
  117. reddit.com Why is there no Functor instance for Data.Set(anglès)
  118. Data.Eq(anglès)
  119. Data.Ord(anglès)
  120. Text.Show(anglès)
  121. Text.Read(anglès)
  122. Data.Binary.Generic(anglès)
  123. 123,0 123,1 Data.String
  124. Extensió OverloadedStrings(anglès)
  125. Data.ByteString.Char8(anglès)
  126. Novetats a GHC 7.8 - OverloadedLists(anglès)
  127. Opcions i variables predefinides per al CPP(anglès)
  128. Compilació condicional(anglès)
  129. HaskellWiki - Compatibility modules
  130. Type Holes(anglès)
  131. Depuració(anglès)
  132. 132,0 132,1 HaskellWiki - Stack overflow
  133. HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
  134. Finding the needle: Stack Traces for GHC - Tristan Allwood, Simon Peyton-Jones and Susan Eisenbach(anglès)
  135. HaskellWiki - Stack trace(anglès)
  136. GHC - RTS Configurations (anglès)
  137. GHC - Opcions de perfilat (anglès)
  138. Catching Exceptions(anglès)
  139. GHC users guide - The GHCi Debugger
  140. No more exceptions: debugging Haskell code with GHCi(anglès)
  141. El fitxer ".ghci"(anglès)
  142. La bilioteca Safe(anglès)
  143. la biblio. file-location(anglès)
  144. GHC flag reference(anglès)
  145. GHC.IO - failIO(anglès)
  146. Monad - fail (implementació per defecte en la classe Monad)(anglès)
  147. paquet either amb el transformador EitherT(anglès)
  148. El paquet loch-th aporta un mecanisme per informar de les crides a les assercions fallides(anglès)
  149. force del paquet deepseq permet avaluar els components d'una dada
  150. Mòdul Debug.Trace(anglès)
  151. EventLog Tracing(anglès)
  152. HaskellWiki EventLog(anglès)
  153. 153,0 153,1 GHC users guide - RTS EventLog(anglès)
  154. Variables globals(anglès)
  155. La mònada Reader
  156. HaskellWiki - Top level mutable state
  157. Una solució al problema de les configuracions.
  158. paquet Seal-module
  159. Implicit configurations
  160. 160,0 160,1 160,2 160,3 System.IO.Unsafe (anglès)
  161. 161,0 161,1 IO inside: The dark side of the IO Monad(anglès)
  162. Hack - In computer science(anglès)
  163. Retainer profiling
  164. La bibloteca deepseq(anglès)
  165. El paquet deepseq-th(anglès)
  166. HaskellWiki - Memory leak(anglès)
  167. .Edward Z. Yang - Space leak zoo(anglès)
  168. Profiling memory usage(anglès)
  169. An Introduction to Cabal sandboxes(anglès)
  170. HaskellWiki - Stackage(anglès)
  171. El servidor Stackage
  172. yesodweb.com - Stable, Vetted Hackage(anglès)
  173. FPComplete Haskell Center(anglès)
  174. Sooner: producing a program more quickly(anglès)
  175. Faster: producing a program that runs quicker(anglès)
  176. Smaller: producing a program that is smaller(anglès)
  177. Thriftier: producing a program that gobbles less heap space(anglès)
  178. Tipus unboxed (allotjament directe) - Restriccions
  179. Tipus amb allotjament directe (ang:unboxed) i primitius (anglès)
  180. Pragma UNPACK
  181. Notes de la versió 7.8.1
  182. Tuples unboxed.
  183. Pragmes del Haskell98(anglès)
  184. haskellWiki - Safe Haskell(anglès)
  185. HaskellWiki - The JavaScript Problem(anglès)
  186. 186,0 186,1 Haste: Running Haskell in the Browser
  187. Try Haste(anglès)
  188. Haste report - Towards a Declarative Web(anglès)
  189. GitHub - valderman/haste-compiler(anglès)
  190. GitHub - ghcjs(anglès)
  191. GHCJS, Concurrent Haskell in the Browser(anglès)
  192. GHC - Procés de generació de codi(anglès)
  193. Paquet hlint que genera el programa del mateix nom(anglès)
  194. Cabal-dev - Sandboxed development builds for Haskell(anglès)
  195. cabal-meta (anglès)
  196. cabal-ghci (anglès)
  197. cabal-progdeps (anglès)
  198. yackage (anglès)
  199. Threadscope
  200. Ajustatge fi del paral·lelisme amb ThreadScope
  201. Haskell Program Coverage(anglès) eina que mostra quin codi no s'ha executat mai i condicions sempre certes o sempre falses
  202. Hp2any - Obtenció del perfil d'ús de memòria(anglès)
  203. Eines de desenvolupament de programes(anglès)
  204. Guia d'Opcions de l'intèrpret d'ordres en Haskell(anglès)
  205. API System.Console.GetOpt amb exemples(anglès)
  206. Neil Mitchell - Intèrpret d'ordres, exemples d'arguments(anglès)
  207. The Exception type (anglès)
  208. GHC extra - Seleccions estil SQL a les llistes(anglès)
  209. Data.Tree(anglès)
  210. Gtk2hs(anglès)
  211. Per què serveix el símbol ___gxx_personality_v0(anglès)
  212. WxHaskell(anglès)
  213. wxWidgets(anglès)
  214. Haskell wiki - Dependent type (anglès)
  215. Haskell wiki - Type arithmetic(anglès)
  216. Paquet type-level-natural-number(anglès)
  217. Paquet numtype-tf(anglès)
  218. dimensional-tf(anglès)
  219. Haskell Trac - TypeNats(anglès)
  220. GHC >= 7.6.1. - Promoted literals
  221. Estàndard Haskell2010 - Signed integer types(anglès)
  222. GHC floatRange(anglès)
  223. wiki anglesa - Significand(anglès)
  224. 224,0 224,1 Mòduls mútuament recursius(anglès)

Enllaços externs[modifica | modifica el codi]

Notícies[modifica | modifica el codi]

Pròximament[modifica | modifica el codi]

Col·laboracions[modifica | modifica el codi]