Glasgow Haskell Compiler
El GHC ("Glasgow Haskell Compiler") é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 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 C--. L'última versió del compilador compleix amb l'estàndard més nou del llenguatge, que és, ara per ara, el Haskell 2010.[1] 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").[2][3] 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[4]
Gestió de memòria
Vegeu doc.[5] GHC compila per defecte amb memòria dinàmica il·limitada. Per establir-ne límits vegeu el senyal de compilació -M.[6] 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ó.[7]
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.[8]
- 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, 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.[9] després passen a la generació 0, i següents[10] tantes com s'estableixi a les opcions de línia de comandes 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.[11]
Fent servir biblioteques de relligat dinàmic
A partir de GHC >= 6.12 [12][13][14] Cal que GHC s'hagi compilat amb ./configure --enable-shared
# relliga amb les versions compartides (no estàtiques) (.so o .dll segons sistema) del RunTimeSystem i dels paquets ghc --make -dynamic Main.hs
Paral·lelisme
Novetats al bloc butlletí "Parallel Haskell Digest"[15]
Paral·lelisme de tasques - Compilació per a processadors multicor
Vegeu [16]
- 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.[17]
- 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"[18][19]
- 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 a possibilitar el paral·lelisme, altrament el relliga amb la de l' RTS uniprocessador.[20]
GHC conté un planificador de fils d'execució lleugers, llançats amb forkIO, que reparteix l'execució als nuclis de CPU als quals assigna internament un fil d'execució del sistema llançats internament amb forkOS. la funció forkOnIO permet designar el nucli on desitgem que s'executi.[21]
<source lang="haskell"> {-# LANGUAGE PackageImports #-}
import "parallel" Control.Parallel (par, pseq)
parfib :: Int -> Integer
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</source>
-- 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 a 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[22] -- 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
Les estratègies són funcions de coordinació de l'execució.[23][24] Vegeu també[25][26]
La mònada Eval.[23] <source lang="haskell"> data Eval a = Done { runEval :: a }
instance Monad Eval where
return x = Done x Done x >>= k = k 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 :: Int -> Int nfib n | n <= 1 = 1
| otherwise = runEval $ do
x <- rpar (nfib (n-1))
y <- rseq (nfib (n-2))
return (x + y)
type Strategy a = a -> Eval a
-- avalua amb l'estratègia withStrategy :: Strategy a -> a -> a withStrategy strat e = runEval (strat e)
-- combinació d'estratègies dot :: Strategy a -> Strategy a -> Strategy a </source> Estratègies típiques:[24] <source lang="haskell"> 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 a l'execució en paraŀ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, paraŀleŀlitzant, l'estratègia del primer arg. -- combinador típic -- aplica map f i avalua la llista resultant amb l'estratègia parMap strat f = withStrategy (parList strat) . map f -- -- parMap no s'ha d'utilitzar amb rpar, ja que activaria l'exec. paraleŀla doblement per cada elem. </source> Càlcul típic amb estratègies. <source lang="haskell"> {-# 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
llista = [2..1100000] :: [Double] càlcul x = 2 * sqrt x
mapejaEnParaŀlel = parMap rseq -- avalua en paraŀlel a WHNF (per rseq)
resultatsDelCàlculEnParaŀlel = mapejaEnParaŀlel càlcul llista
main = print $ sum resultatsDelCàlculEnParaŀlel </source> compilació i exec.
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
Paral·lelisme de dades
Regular parallel arrays
Un primer nivell de tractament paral·lel s'ofereix mitjançant el paquet Repa ("Regular parallel arrays")[27] de tractament en paral·lel de vectors pluridimensionals. Exemple més avall.
Data Parallel Haskell
GHC implementa el, més complex, paral·lelisme de dades niuat, trad. de "Nested Data Parallelism"[30][31][32] basat en treballs de Guy E. Blelloch.[33]
Per a proves cal instaŀlar el paquet dph-examples. <source lang="bash"> 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" </source> Cal fixar-se en la versió instaŀlada (diferent segons la versió de GHC) i descarregar-ne els fonts per investigar. <source lang="bash">
- per obtenir-ne la versió instaŀlada
ghc-pkg list | grep dph-examples </source> Per desenvolupar-hi cal tenir en compte que ...
- Cal separar les operacions per mòduls segons si són o no vectoritzades
- A l'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.
Paral·lelisme a la GPU
- Biblioteca Accelerate amb suport per a rerafons CUDA, OpenCL (en preparació)[35] i RePa[36] (en preparació, per a CPU), i genera nuclis (d'iteracions en paraŀlel) per a GPU partint d'un llenguatge incrustat (EDSL) en Haskell (treball en curs)[37]
- Biblio. OpenCLWrappers facilita l'ús de lligams amb biblioteques externes que implementin l'estàndard OpenCL,[38] article: OpenCL from Haskell[39]
- Simultanejant càrregues de treball en CPU i GPU. Vegeu "How to write hybrid CPU/GPU programs with Haskell".[40] El paquet meta-par ofereix una versió de la mònada Par (encadenament de càlculs simultanis) per compartir recursos de hardware, amb un planificador de tasques SMP.[41]
Concurrència
- MVar's - Mutable Variable
- forkIO / forkOS
- Software Transactional Memory
- Encadenament de càlculs simultanis - la mònada Par
- Operacions d'Entrada/Sortida asíncrones i simultànies
- Cloud Haskell
- Biblioteca Actor
- Communicating Haskell Processes (CSP)
Vegeu Haskell concurrent.
Template Haskell
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 (metaprogramació).[42]
- Avaluació en temps de compilació: 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ó.
- Incrustació de codi de gramàtiques diferents: La construcció quasiQuoter permet incrustar textos amb regles gramaticals diferents (per ex.: plantilles HTML), i en temps de compilació generar-ne codi interoperable amb la resta.
Exemple: Depuració. Afegint punts de control per detectar qui ha cridat l'asserció fallida
Pistes per la depuració aquí.[43]
Problema: Manca de "bolcat de la pila de crides" en les petades, perquè no n'hi ha.[44][45] Nota: A partir de GHC 7.4.1 s'ha incorporat un sistema de pila de crides, amb bolcat en cas d'excepció, quan es compila amb opcions de perfilat.[46]
El paquet loch-th[47] aporta una sol·lució 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.
<source lang="haskell"> module Erroni where
import Control.Exception (assert)
erroni :: String erroni = assert False "abc" </source> <source lang="haskell"> -- fitxer prova.hs {-# LANGUAGE PackageImports, TemplateHaskell #-} import "loch-th" Debug.Trace.LocationTH (check) import Erroni
msg = $check erroni
main = print ($check msg) </source> la seva execució 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
$check avalua amb evaluate que ho fa a Weak Head Normal Form. 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"[48] avalua estrictament també els components, si el tipus implementa la classe NFData del mateix paquet. El paquet deepseq-th aporta la derivació d'instàncies NFData.
<source lang="haskell"> {-# LANGUAGE PackageImports, ScopedTypeVariables, TemplateHaskell, BangPatterns #-} import Prelude hiding (catch) -- El catch del Prelude no detecta excepcions en el codi funcional import Control.Exception (catch, SomeException)
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, sí que prefixa la posició erroni4 = $check (force (head [] : "abc")) -- amb 'force' de deepseq, sí que prefixa la posició
consEstricteALElem !x xs = x:xs -- ! al paràmetre, avalua a WHNF (vegeu ext. BangPatterns)
main = do
putStr "cas erroni1: " >> putStrLn erroni1 `catch` (\(excep :: SomeException) -> print excep) putStr "cas erroni2: " >> putStrLn erroni2 `catch` (\(excep :: SomeException) -> print excep) putStr "cas erroni3: " >> putStrLn erroni3 `catch` (\(excep :: SomeException) -> print excep) putStr "cas erroni4: " >> putStrLn erroni4 `catch` (\(excep :: SomeException) -> print excep)
</source> dóna, mostrant si s'avalua o no el codi erroni, assenyalant-ne la posició:
$ runhaskell prova cas erroni1: prova.hs:8:11-16: Prelude.head: empty list cas erroni2: Prelude.head: empty list cas erroni3: prova.hs:10:11-16: Prelude.head: empty list cas erroni4: prova.hs:11:11-16: Prelude.head: empty list
Quasi-Quotations
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[49] el quasiQuoter shamlet permet incrustar plantilles HTML amb expressions que inclouen símbols vàlids en l'àmbit en que 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.[50]
- Sintaxi
- Les acotacions QuasiQuotation utilitzen una sintaxi denominada Claus d'Oxford [| ... |] ó [quasiQuoter| ... |] on el nom del prefix indica el tipus que aporta el traductor del text acotat per les claus.[51]
- Tipus
- Un tipus quasiQuoter es defineix com un registre amb traductors opcionals per cadascun dels 4 possibles contextos en que es pretengui utilitzar (com a expressió, o bé patró d'encaix, o bé tipus, o bé declaracions) assignant als altres el valor undefined.[52]
- 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) del Haskell[53] per aquells contextos on es pretengui utilitzar, podent definir els altres com undefined.[54][55]
<source lang="haskell"> 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.
}
</source>
Exemple senzill: document tot seguit
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.
- els contextos que no interessen es deixen undefined.
- l'ús i la definició han d'estar en mòduls separats.
<source lang="haskell"> {-# 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 </source> <source lang="haskell"> -- ú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|] </source>
Exemple: Incrustació de HTML amb expressions avaluables
De les biblioteques del Yesod[49].
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.
<source lang="haskell"> {-# 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|
Hola el meu nom és #{nom} $if null amics
Ho sento, no hi ha amics. $else
Els meus amics són #{length amics}:
-
$forall amic <- amics
- #{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}
|]
</source>
Facilitats per l'optimització de fusió
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ó.[56]
Stream fusion
Fusió de bucles en la composició d'operacions sobre estructures mitjançant la conversió de l'estructura en un corrent de dades (ang: Stream)[57] 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 de 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ó.[58][59]
Vegeu biblioteca Stream-fusion al rebost Hackage[60]
Biblio ràpida
Vegeu l'API del GHC[61] i la de la plataforma[3] Haskell.
contenidors
Vegeu ref.[62]
- Recordatori d'algunes operacions, per veure similituds i diferències
- S'esmenten les ops. ometent el paràm. de l'estructura sobre la que s'aplica. Ocasionalment s'especifica el tipus del retorn. Respecte als tipus de les funcions, consulteu les refs.
notes
llistes amb desforestació / fusió de bucles
- el paquet stream-fusion[60] aporta llistes amb funcions fusionables pel mètode stream fusion.
conjunts / taules associatives
Set ε[63] iMap κ ε[64] del paquet "containers" implementen conjunts i diccionaris basats en arbres de cerca binària balancejats amb cost de cerca O(log2 n).IntSet[65] iIntMap ε[66] 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[67] aporta una implementació de Set i Map 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)[68] 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 bytestring-trie[69] aporta una implementació de diccionaris com a arbres de prefix (Tries) per a claus de tipus ByteString
cues de dos caps
Seq εde Data.Sequence[70] 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.
vectors pluridimensionals
Array shape εdel paquet Repa[71][72] aporta vectors pluridimensionals d'elements no-encapsulats (unboxed) (allotjats directament)- Els elements del Repa han d'implementar
<source lang="haskell"> class (Show a, Unbox a) => Elt a zero :: a one :: a ... </source>
- Els tipus dels índexos del Repa prenen una forma semblant a una "pila de sencers", i implementen la classe Shape:
<source lang="haskell"> 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 ... </source>
- 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
Vector εiMVector σ εdel paquet Vector[73] 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ó).[74][75]
- 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
- ...
- hi ha paquets relacionats al Hackage amb funcionalitats diverses
generació
seqüències - generació paquet tipus context buit amb un elem. de llista o
funció generadorabase [ε][76][ ] [x] ll. per comprensió
repeat x
replicate n x
iterate (f::a -> a) x
cycle llistacontainers Seq ε[70]empty singleton x fromList llista bytestring ByteString[77]
-- UArray Word8empty singleton x pack llistaDeBytes text
-- ∈ H. Platform[3]Text[78]
-- ByteString codif. UTF16
indexat com [Word16][79]empty singleton ch pack string vectors - generació paquet tipus context buit amb un elem. de llista o
funció generadoraarray Array ι ε[80]
-- immutablelistArray (iMin, iMax) llista
array (iMin, iMax) llistaD'Associacions -- parells (índx, x)vector Vector ε[81]
-- immutableempty singleton x fromList llista
replicate n x
generate llarg (f :: Int -> ε)MVector σ ε[82]
-- mudableVector.thaw ivector -- descongela immutable
replicate n x
replicateM n acció
clone mvectorrepa multidim. Array sh ε[72](Shape sh,
Elt ε)singleton x
:: Array DIM0 εfromList shape llista classes interfícies array IArray α ε[83]
-- immutables
--α ∈ {Array, UArray}-- cal esmentar el tipus desitjat
listArray (iMin, iMax) llista ::α ι ε
array (iMin, iMax) llistaD'Associacions ::α ι εMArray α ε m[84]
-- 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 iarrayestructures - generació paquet tipus context buit amb un elem. de llista o
funció generadoracontainers IntSet[65]empty singleton x fromList llista
fromAscList llista -- de llista ascendentIntMap ε[66]empty singleton clau x fromList llistaD'Associacions -- parells (clau, x)
fromAscList llistaD'Associacionscontainers
hashmapSet ε[63](Hashable ε)empty singleton x fromList llista (Ord ε)fromAscList llista -- de llista ascendent (només containers) Map κ ε[64](Hashable κ)empty singleton clau x fromList llistaD'Associacions -- parells (clau, x) (Ord κ)fromAscList llistaD'Associacions -- (només containers) bytestring-trie Trie ε[69]empty singleton bytestringClau x fromList llistaD'Associacions -- parells (bstrClau, x) containers Tree ε[85]Node x [ ] unfoldTree f llavor - exemple amb vectors a la mònada ST, creació com a immutable, descongela a mudable (thaw), modificació, recongelació(freeze) i llistat
<source lang="haskell"> import Data.Array.IArray as IArray import Data.Array.MArray as MArray import Data.Array.ST import Data.Array.Unboxed
import Control.Monad.ST
modificaArray :: UArray Int Int -> ST s (UArray Int Int) modificaArray immVect = do
mutVect <- thaw immVect :: ST s (STUArray s Int Int) -- descongela let indx = 0 x <- readArray mutVect indx writeArray mutVect indx (x - 1) freeze mutVect -- congelamain = do
let immVect = IArray.listArray (0,1) [0,1] :: UArray Int Int immVect2 = runST $ modificaArray immVect print $ elems immVect2</source>
consulta
seqüències - consulta tipus context és
buit?mida pertinença obtenir elem components altres [ε][76]null length elem x find predicat :: Maybe ε
-- encaix
[ ] | (x : xs)subsequences
permutationsSeq ε[70]null length -- encaix de viewl seq
EmptyL | (x :< xseq)
-- encaix de viewr seq
EmptyR | (x :> xseq)-- via FoldableByteString[77]
-- UArray Word8null length elem x find predicat
-- encaix de uncons byteStr
Nothing | Just (x, xs):: Maybe (Word8, ByteString)unpack :: [Word8]Text[78]
-- ByteString codif. UTF16
indexat com [Word16][86]null length elem ch find predicat
-- encaix de uncons text
Nothing | Just (ch, txt):: Maybe (Char, Text)unpack
:: [Char]classes interfícies Foldable contenidor[87]elem x find predicat toList vectors - consulta tipus context és
buit?mida pertinença obtenir elem components altres Array ι ε[80]bounds
indices
elems
assocsVector ε[81](Eq ε)null length elem x find predicat toList MVector σ ε[82]null length Vector.freeze --cap a Vectormultidim. repa Array sh ε[72](Shape sh,
Elt ε)size . extent toList
toScalar-- cas de shape DIM0
extent
::(Shape sh)classes interfícies IArray α ε[83]
-- immutablesbounds
indices
elems
assocsMArray α ε m[84]
-- mudables(Monad m)getBounds
getElems
getAssocs-- cap a IArray
freezeestructures - consulta tipus context és
buit?mida pertinença obtenir elem components altres Set ε[63]IntSet[65]null size elems (Ord ε)member x toAscList Map κ ε[64]IntMap ε[66]null size keys
keysSet
elems
assocs(Ord κ)member clau lookup clau toAscList Trie ε[69]null size member bytestringClau lookup bstrClau keys
elems
toListsubmap bstrClau Tree ε[85]flatten levels Els errors generats per funcions definides parcialment, com ara head, que requereix llista no-buida, tenen una depuració complicada. Per això és convenient una de les següents alternatives:
- substituir-les per versions que retornin el resultat en un Maybe forçant qui fa la crida a gestionar el cas Nothing.
- substituir-les per encaix de patrons incomplet (tindrem missatge d'error indicant posició si no és el cas)
- precedir-ne l'aplicació amb $check del paquet loch-th (el gestor d'excepcions auto-generat en el $check detecta l'error i imprimeix el missatge de situació del $check, excepte que quan hi ha constructors pel mig cal avaluar estrictament el component que interessa). Vegeu #Loch-TH i els constructors
actualització / transformació
seqüències - actualitza/transforma transforma tipus afegeix elimina actualitza aplicació
als elems. / índexosaltres transforma'n
una llista[ε][76](x :) delete x map (f:: a -> b)
mapAccum{L|R} (f:: acc -> x -> (acc, y)) acumIninub --elimina duplicats
reverse
intersperse xtranspose
intercalate llistaSeq ε[70](x <|) -- a l'esquerra
(|> x) -- a la dretamapWithIndex (f:: Int -> a -> b) reverse ByteString[77]cons x -- al davant
snoc x -- al darreramap (f:: Word8 -> Word8) reverse
intersperse bytetranspose
intercalate bstrText[78]cons ch
snoc chmap (f:: Char -> Char) reverse
intersperse ch-- específics
replace cerca subst
toUpper
toLower
toCaseFold
justifyLeft llarg ch
justifyRight llarg ch
center llarg chtranspose
intercalate txtvectors - actualitza/transforma transforma tipus afegeix elimina actualitza aplicació
als elems. / índexosaltres transforma'n
una llistaArray ι ε[80](// llistaD'Associacions) ixmap (iMin, iMax) (f::i -> j) Vector ε[81]cons x -- pel davant
snoc x -- pel darreramap (f:: a -> b)
imap (f:: Int -> a -> b)
concatMap (f :: a -> Vector b)reverse MVector σ ε[82](`set` x)
clear -- desvincula refs.
(`copy` mvectorOrigen)
(`move` mvectorOrigen)(`grow` n) multidim. repa Array sh ε[72]map (f:: a -> b) reshape shape
transposeclasses interfícies IArray α ε[83](// llistaD'Associacions) ixmap (iMin, iMax) (f::i -> j) MArray α ε m[84]mapArray (f::a -> b)
mapIndices (iMin, iMax) (f::i -> j)estructures - actualitza/transforma transforma tipus afegeix elimina actualitza aplicació
als elems. / índexosaltres (Ord ε) => Set ε[63]IntSet[65]insert x delete x insert x -- subst. l'existent map (f:: a -> b)
mapMonotonic (f:: a -> b)(Ord κ) => Map κ ε[64]IntMap ε[66]insert clau x delete clau adjust f clau
update f clau
alter f claumap (f:: a -> b)
mapWithKey (f:: κ -> a -> b)
mapAccum (f:: acc -> a -> (acc, b)) acumIniTrie ε[69] insert bytestringClau delete bstrClau adjust f bstrClau mapBy (f:: ByteString -> a -> Maybe b)
filterMap (f:: a -> Maybe b)Tree ε[85]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 l'inserta amb el valor (f Nothing) ; altrament com update
combinació
combina tipus concatena concatena'n
una llista[ε][76](++) concat Seq ε[70](><) ByteString[77]append concat Text[78]append concat (Ord ε) => Set ε[63]IntSet[65](Ord κ) => Map κ ε[64]IntMap ε[66]Trie ε[69]Tree ε[85]Array ι ε[80]Vector ε[81](++) concat multidim. repa Array sh ε[72](++)
appendclasses interfícies IArray α ε[83]MArray α ε m[84]com a conjunt tipus diferència unió uneix-ne
una llistaintersecció és sub. és sub.
estrictament[ε][76](\\) union intersect Seq ε[70]ByteString[77]Text[78](Ord ε) => Set ε[63]IntSet[65](\\)
differenceunion unions intersection isSubsetOf isProperSubsetOf (Ord κ) => Map κ ε[64]IntMap ε[66](\\)
differenceunion unions intersection isSubmapOf isProperSubmapOf Trie ε[69]unionL
unionRTree ε[85]Array ι ε[80]Vector ε[81]multidim. repa Array sh ε[72]classes interfícies IArray α ε[83]MArray α ε m[84]accés indexat
seqüències - accés indexat (en color si generen error, en marró si és previsible)tipus context elem. a
l'índex o claucerca l'índex
de l'elem.:: Maybe Intcerca indexos ::[Int]elimina actualitza [ε][76](!! índx) elemIndex x
findIndex predicatelemIndices x
findIndices predicatSeq ε[70](`índex` índx) elemIndexL x -- per l'esquerra
findIndexL predicat
elemIndexR x --per la dreta
findIndexR predicatelemIndices{L|R} x
findIndices{L|R} predicatadjust f índx
update índx xByteString[77](`índex` índx) elemIndex x -- pel davant
elemIndexEnd x -- pel darrera
findIndex predicatelemIndices x
findIndices predicatText[78](`índex` índx) findIndex predicat vectors - accés indexat (en color si generen error, en marró si és previsible (domini acotat i continu))tipus context elem. a
l'índex o claucerca l'índex
de l'elem.:: Maybe Intcerca indexos elimina actualitza Array ι ε[80](! índx) Vector ε[81](! índx) elemIndex x
findIndex predicat-- retornen :: Vector Int
elemIndices x
findIndices predicatMVector σ ε[82](`read` indx) (`write` indx x)
(`swap` i j)multidim. repa Array sh ε[72](Shape sh,
Elt ε)(! índxMultiDim)
(`índex` índxMultiDim)(`safeIndex` índxMultiDim) classes interfícies IArray α ε[83](! índx) MArray α ε m[84](Monad m)(`readArray` indx) (`writeArray` indx x) estructures - accés indexat (en color si generen error, en marró si és previsible)tipus context elem. a
l'índex o claucerca l'índex
de l'elem.:: Maybe Intcerca indexos ::[Int]elimina actualitza (Ord ε) => Set ε[63]IntSet[65]Map κ ε[64]elemAt índx retorna :: (κ,ε)deleteAt índx updateAt f índx (Ord κ)(! clau) lookupIndex clau IntMap ε[66](! clau) Tree ε[85]- versió de programació total per a l'índex a llistes: vegeu Haskell#El problema de la manca d'informació de situació en les petades
- la versió total per a (! clau) de Map és (lookup clau)
plegat
plegats especials tipus sobre
booleanss/. predicat s/. ordre numèrics contenidor llista de
contenidorscontenidor
de llistesíndex del
menor/majorε==Bool(Ord ε / κ)(Num ε)(Ord ε)[ε][76]and
orall predicat
any predicatminimum
maximumsum
productconcatMap (f::a->[b]) concat ByteString[77]all predicat
any predicatminimum
maximumconcatMap (f::Word8->ByteString) concat Text[78]all predicat
any predicatminimum
maximumconcatMap (f::Char->Text) concat Set ε[63]IntSet[65]findMin
findMaxMap κ ε[64]IntMap ε[66]-- min/max de la clau
findMin
findMaxVector ε[81]and
orall predicat
any predicatminimum
maximumsum
productconcatMap (f::a -> Vector b) concat minIndex
maxIndexclasses Foldable contenidor[87]and
orall predicat
any predicatminimum
maximumsum
productconcatMap (f::a->[b]) concat partició
seqüències - partició tipus a l'índex en inferiors
i superiorss/.
predicat:: (t,t):: (t,t)[ε][76]take n
drop nsplitAt índx filter predicat
takeWhile predicat
dropWhile predicatpartition predicat Seq ε[70]take n
drop nsplitAt índx filter predicat
takeWhile{L|R} predicat
dropWhile{L|R} predicatpartition predicat
span{l|r} predicat
break{l|r} predicatByteString[77]take n
drop nsplitAt índx filter predicat
takeWhile predicat
dropWhile predicatpartition predicat Text[78]take n
drop nsplitAt índx filter predicat
takeWhile predicat
dropWhile predicatpartition predicat vectors - partició tipus a l'índex en inferiors
i superiorss/.
predicatArray ι ε[80]Vector ε[81]splitAt índx
take n
drop n
slice índx npartition predicat
filter predicat
takeWhile predicat
dropWhile predicatMVector σ ε[82]splitAt índx
take n
drop n
slice índx nestructures - partició tipus a l'índex en inferiors
i superiors(estrictament)s/.
predicatpel prefix (Ord ε) => Set ε[63]IntSet[65]split pivot partition predicat
filter predicat(Ord κ) => Map κ ε[64]IntMap ε[66]split clauPivot partition predicat
filter predicatTrie ε[69]submap bytestrPrefixClau Tree ε[85]implementació de classes
implementa classes tipus Monoid[88] Functor[89] Foldable[87] Traversable[90] Applicative Monad MonadPlus Binary[91] (serialitzable)[ε][76]Sí Sí Sí Sí Sí Sí Sí (Binary ε)Seq ε[70]Sí Sí Sí Sí Sí Sí Sí (Binary ε)Set ε[63]IntSet[65](Ord ε)No Sí No No No No (Binary ε)Map κ ε[64]IntMap ε[66](Ord κ)Sí Sí Sí No No No (Binary κ,
Binary ε)Trie ε[69](Monoid ε)Sí Sí Sí Sí Sí No (Binary ε)Tree ε[85]No Sí Sí Sí Sí Sí No (Binary ε)Array ι ε[80]No Sí No No No No No (Binary ι,
Binary ε)ByteString[77]Sí No No No No No No Sí Text[78]Sí No No No No No No No (-- implementable
via "binary-generic")Sobrecàrrega de literals String
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.[92] Cal especificar l'extensió de llenguatge OverloadedStrings.[93]
<source lang="haskell"> {-# 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" </source>
implementa classes tipus IsString [92] ByteString[77]Data.ByteString.Char8
(ByteString)Text[78]Sí Serialització genèrica
Serialització genèrica de tipus algebraics mitjançant el paquet "binary-generic". Cal reclamar que el compilador en derivi la implementació de Data, per al recorregut genèric de l'estructura. <source lang="haskell"> {-# LANGUAGE PackageImports, DeriveDataTypeable #-}
import Data.ByteString.Lazy (ByteString) import Data.Data (Data, Typeable) import "binary" Data.Binary.Get (runGet) import "binary" Data.Binary.Put (runPut) import qualified "binary-generic" Data.Binary.Generic as DBG
-- cal reclamar la derivació d'instàncies de Typeable i Data (tractament genèric) data ElMeuTipus = ElMeuConstr String Int | ElMeuConstr2 Int String
deriving (Eq, Show, Typeable, Data)
mostres = [ElMeuConstr "abc" 10, ElMeuConstr2 10 "abc"]
encode :: Data a => a -> ByteString -- serialitza encode = runPut . DBG.putGeneric
decode :: Data a => ByteString -> a -- desserialitza decode = runGet DBG.getGeneric
-- ha de complir: decode . encode == id funciona mostra = (decode . encode) mostra == mostra
main = print $ all funciona mostres </source> Compilant:
ghc --make prova.hs -package binary -package binary-generic
Tipus dependents de valors
Vegeu ref.[94]
Naturals als tipus
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[95] o definicions inductives més sofisticades.[96]
- Remarcable: El paquet dimensional-tf incorpora als tipus les dimensions de les unitats de la física.[97]
<source lang="haskell"> {-# 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 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. -- Dimensions es pot expressar en funció de vectors unitaris (DLength, DTime, ...) -- i també de l'aplicació de les famílies de tipus {Mul, Dev, Pow} definides a Numeric.Units.Dimensional.TF.
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ó
</source> 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[98] ("Type level natural numbers"), parcialment implementada a GHC 7.6.1, permetrà una implementació més eficient (requereix l'extensió DataKinds).[99]
El problema de la manca d'informació de situació en les petades
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.
No hi ha l'equivalent del "bolcat de la pila de crides" d'altres llenguatges per situar-se.[44] Nota: A partir de GHC 7.4.1 s'ha incorporat un sistema de pila de crides, amb bolcat en cas d'excepció, quan es compila amb opcions de perfilat.[46]
Tanmateix si sabeu per on poden anar els trets, es pot anar acorralant amb paranys (catch) i afegir traces als llocs sospitosos.
<source lang="haskell"> {-# LANGUAGE ScopedTypeVariables #-}
import Prelude hiding (catch) import Control.Exception import Debug.Trace (trace, traceStack) -- traceStack (des de GHC v.7.4.1)
cap (x:_xs) = x cap [] = error "l'has espifiada, calçat per trobar-me!!"
erroni n = cap $ drop n [1::Int,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' = traceStack msg resultat -- o bé amb trace si GHC < 7.4 msg = "el paràmetre era " ++ show n
-- aproximació amb paranys (catch): 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))
-- aproximació per depuració al'intèrpret: -- amb GHCi debugger, establint break-points i el flag break-on-exception -- l'excepció ErrorCall provoca el break, amb :back retrocedeix al break-point anterior -- -- ordres GHCi: -- :set -fbreak-on-exception -- :break <ident-global|num-linia> -- :back -- retrocedeix a breakpoint anterior -- :print <ident> -- consulta ident. avaluats, visibles en l'àmbit -- :force <ident> -- força avaluació i imprimeixaproxAmbDepuració n = erroni n
main = print $ aproxAmbTraces 5 </source>
$ 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-me!!
Amb GHCi debugger. Vegeu "No more exceptions: debugging Haskell code with GHCi".[100]
ghci prova.hs Ok, modules loaded: Main. *Main> :set -fbreak-on-exception *Main> :break aproxAmbDepuració Breakpoint 0 activated at prova.hs:(30,1)-(32,35) *Main> aproxAmbDepuració 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)
Una soŀlució proposada de validació abans de fer crides a funcions definides parcialment és l'obtenció de tipus validats mitjançant els tipus "fantasma" (quins paràmetres no entren a la definició).[101]
Amb Template Haskell s'aconsegueix una aproximació per a les assercions fallides, com s'explica a Depuració. Afegint punts de control per detectar qui ha cridat l'asserció fallida.
El millor és evitar les crides a funcions parcialment definides (head es pot substituir per encaix de patrons), o consultar si tindrem èxit abans de fer la crida.
Una altra possibilitat és definir funcions totals que en comptes de cridar la rutina error, retornin el resultat en un Maybe i analitzar el resultat per patrons. En cas que l'opció Nothing no hi fos (per no prevista) donaria, en cas que es produís, l'excepció "Non-exhaustive patterns in case" informant de la situació del case incomplet.
<source lang="haskell"> totalIndex :: [a] -> Int -> Maybe a totalIndex (x:xs) indx = case compare indx 0 of
GT -> xs `totalIndex` (indx-1) -- el més probable primer EQ -> Just x LT -> NothingtotalIndex [] indx = Nothing
mostra = ['a','b','c']
un_índex_fora_del_rang = 3 :: Int
prova_índex_parcialment_definit = mostra !! un_índex_fora_del_rang
prova_índex_totalment_definit_amb_situació_de_l'error =
case mostra `totalIndex` un_índex_fora_del_rang of Just elm -> elm -- Nothing expressament no inclòs, perquè no esperàvem un índex fora de rang
</source> prova:
ghci Prelude>:load "prova-ghci.hs" [1 of 1] Compiling Main ( prova-ghci.hs, interpreted ) Ok, modules loaded: Main. *Main> prova_índex_parcialment_definit *** Exception: Prelude.(!!): index too large -- no indica situació de l'error *Main> prova_índex_totalment_definit_amb_situació_de_l'error *** Exception: prova-ghci.hs:(14,57)-(15,52): Non-exhaustive patterns in case
Eines
- hlint
- Aquí[102] 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.[103]
- cabal-meta
- instaŀla (amb cabal-dev cas de --dev), en una sola comanda (resolució conjunta de dependències) diversos paquets (del hackage o de directoris locals o bé remots (git)) especificats en un o més fitxers sources.txt adreçats en arbre[104], on cada línia pot contenir:
- cas de començar per '.' o bé '/': directori (seguit d'opcions) de projecte cabal, o bé directori que conté un altre fitxer "sources.txt" de subllista. (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[105]
- cabal-progdeps
- llista les dependències del projecte del directori de treball partint del fitxer setup-config del subdir. dist.[106]
- yackage
- servidor de rebost "hackage" local per a desenvolupament.[107]
- 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).[108] Estudi "Ajustatge fi del paral·lelisme amb ThreadScope".[109]
- 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.[110]
- compilar amb l'opció -fhpc
- executar diverses vegades amb diferents entrades
- invocar: hpc markup nom_executable (genera hpc_index.html i altres html)
- obrir hpc_index.html amb un navegador
- 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.[111]
- hp2any-manager mostra gràfics de fitxers de perfil (.hp) corresp. a execucions ja finalitzades.
- més eines de desenvolupament al HaskellWiki
Vegeu ref.[112]
Exemples
Excepcions de tipus definits per l'usuari
Augmenta les possibilitats d'excepcions del Haskell98, amb tipus nous que han d'implementar la classe Exception.[113]
class (Typeable e, Show e) => Exception e
<source lang="haskell"> {-# LANGUAGE DeriveDataTypeable #-}
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)
instance Exception TExcepcionsDeLAplicacio -- instancia els mètodes per defecte de la classe Exception
factorial :: Int -> Maybe Integer factorial n | n == 0 = Just 1
| n > 0 = Just (fac_rf n 1) | otherwise = Nothing where fac_rf m acum | m > 0 = fac_rf (m-1) (acum * toInteger m) | m == 0 = acumavalua_fac :: Int -> IO () avalua_fac n = case factorial n of
Just r -> putStrLn ("resultat: " ++ show r) Nothing -> throwIO (EParametreIlegal "avalua_fac: param. fora del domini")prova_fac :: Int -> IO () prova_fac n = catch
( avalua_fac n ) (\ excepcio -> case excepcio of EParametreIlegal msg -> putStrLn msg _ -> print excepcio )main = prova_fac (-1) --
</source>llistes per comprensió a l'estil de SQL
La clàusula the introdueix una consulta a l'estil de SELECT de l'SQL.[114] Cal esmentar la pragma {-# LANGUAGE TransformListComp #-}
<source lang="haskell"> {-# LANGUAGE TransformListComp #-} import GHC.Exts (the, groupWith, sortWith) import Data.List (sum, genericLength)
-- 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)]
-- evitant divisions per zero, en cas de llista buida avg :: (Fractional a, Eq a) => [a] -> a avg llista = avg_acum llista 0 0
where
avg_acum [] recompte suma | recompte == 0 = 0 | otherwise = suma / recompte
avg_acum (x:xs) recompte suma = avg_acum xs (recompte +1) (suma +x)
</source>
main = print consultadóna
[("Programació",42.5),("Comptabilitat",60.0),("Vendes",90.0)]arbres
Vegeu ref.[115]
<source lang="haskell"> import Data.Tree (Tree) import qualified Data.Tree as Tree import Data.Ord
data Jerarquia = Animal | Vertebrat | Mamífer | Primat | Humà
deriving (Eq, Ord, Enum, Bounded, Show)
índexArrel = fromEnum (minBound :: Jerarquia) índexDarrer = fromEnum (maxBound :: Jerarquia)
-- funció per generar l'arbre jerarquitzador :: Int -> (Jerarquia, [Int]) jerarquitzador x = (etiquetaNus, fills)
where etiquetaNus = toEnum x :: Jerarquia fills = case compare x índexDarrer of LT -> [x+1] EQ -> []elMeuArbre :: Tree Jerarquia elMeuArbre = Tree.unfoldTree jerarquitzador índexArrel
x |> f = f x -- 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 |> putStrLnmain = imprimeixArbre elMeuArbre </source> dóna el resultat:
Animal | `- Vertebrat | `- Mamífer | `- Primat | `- HumàExemple de Data Parallel Haskell
Vegeu #Data Parallel Haskell i ref.[31]
- Mòdul d'operacions vectoritzades
<source lang="haskell"> {-# 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 p = xs !: (lengthP xs `I.div` 2) ls = [: x | x <- xs, x D.< p :] gs = [: x | x <- xs, x D.> p :]-- mapeja en paraŀlel qsortVect als subvectors (Nested Data Parallellism) !! ss = mapP qsortVect [: ls, gs :] in (ss !: 0) +:+ [: x | x <- xs, x D.== p :] +:+ (ss !: 1)</source>
- Mòdul d'operacions No vectoritzades
<source lang="haskell"> {- 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</source>
- Compila i executa
<source lang="bash"> 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 </source>
Precisió dels tipus bàsics
<source lang="haskell"> 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)</source> 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 el rang complet corresp. a la paraula de la màquina 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[116] dónen un valor esbiaixat respecte 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".[117]
- La notació estandaritzada per a coma flotant de 1 és (en base 2): 0.1base 2 * 2^1
A ghci:
Prelude> exponent (2^0::Float) 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 en coma flotant.
Mòduls amb referències mútues (mútuament recursius)
- Vegeu [118]
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([118]), 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 i hi aplica el (pre)procés adequat.
Vegeu també
- Haskell concurrent
- Haskell
- Mònada (programació funcional)
- Fletxa (programació funcional)
- L'intèrpret Hugs
- Frege (llenguatge de programació) - lleng. quasi-Haskell per a la màquina virtual Java
Referències
- ↑ Estàndard actual del llenguatge Haskell
- ↑ La "Plataforma Haskell"(anglès)
- ↑ 3,0 3,1 3,2 Docum. de la Haskell Platform(anglès)
- ↑ Anunci de la GHC 6.12.1(anglès)
- ↑ Documentació del compilador GHC
- ↑ Opcions del RunTimeSystem per controlar la mem. dinàmica i el recollidor de memòria brossa(anglès)
- ↑ RTS - Envelliment(anglès)
- ↑ RTS - Allotjament en Blocs
- ↑ RTS - El planificador(anglès)
- ↑ El recollidor de brossa(anglès)
- ↑ RTS - El recollidor de brossa.
- ↑ Ús de biblioteques d'enllaç dinàmic (anglès)
- ↑ Suport d'enllaç dinàmic a les diferents plataformes (anglès)
- ↑ Building and using Win32 DLLs(anglès)
- ↑ Parallel Haskell Digest(anglès)
- ↑ primitives de paral·lelisme(anglès)
- ↑ Haskell Paral·lel
- ↑ GHC seq vs. pseq(anglès) Comparació de les primitives seq i pseq
- ↑ GHC - primitiva pseq(anglès)
- ↑ GHC - Utilitzant multiprocés simètric SMP
- ↑ El planificador del GHC - fils d'exec. del sistema i fils del GHC (anglès)
- ↑ GHC - Hints (cat:Pistes) for using SMP parallelism (anglès)
- ↑ 23,0 23,1 Glasgow Parallel Haskell (anglès) Estratègies de paral·lelisme
- ↑ 24,0 24,1 Control.Parallel.Strategies(anglès)
- ↑ Seq no more: Better Strategies for Parallel Haskell(anglès) Prou de seq (seqüencial) - millors estratègies per al Haskell paral·lel
- ↑ haskellWiki - paral·lelisme(anglès)
- ↑ REgular PArallel arrays(anglès)
- ↑ SIMD Support for the vector library (anglès)
- ↑ 29,0 29,1 GHC SIMD (anglès)
- ↑ Haskell - Paral·lelisme de dades
- ↑ 31,0 31,1 Nested Data Parallelism - presentació (diapos)
- ↑ Paquet dph-examples
- ↑ Guy E. Blelloch - Programming Parallel Algorithms - Nested Data Parallellism
- ↑ SIMD Support to Data Parallel Haskell(anglès)
- ↑ El paquet accelerate-opencl(anglès)
- ↑ El paquet accelerate-repa(anglès)
- ↑ Biblioteca Accelerate per al paral·lelisme a les GPU(anglès)
- ↑ paquet OpenCLWrappers(anglès)
- ↑ OpenCL from Haskell(anglès)
- ↑ How to write hybrid CPU/GPU programs with Haskell(anglès)
- ↑ El paquet meta-par(anglès)
- ↑ Template Haskell(anglès)
- ↑ Depuració(anglès)
- ↑ 44,0 44,1 HaskellWiki - Stack overflow
- ↑ Explicit CallStack (anglès)
- ↑ 46,0 46,1 HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
- ↑ El paquet loch-th aporta un mecanisme per informar de les crides a les assercions fallides(anglès)
- ↑ force del paquet deepseq permet avaluar els components d'una dada
- ↑ 49,0 49,1 Yesod - plantilles(anglès)
- ↑ Quasi-Quotation a la viqui anglesa(anglès)
- ↑ Sintaxi dels quasiQuoters(anglès)
- ↑ QuasiQuotation
- ↑ Tipus algebraics del TemplateHaskell (anglès)
- ↑ Haskellwiki - Quasi-Quotation(anglès)
- ↑ Constructor QuasiQuoter al paquet template-haskell(anglès)
- ↑ Fusionant successius Map: Fent que Haskell sigui un 225% més ràpid (anglès)
- ↑ From lists to streams to nothing at all(anglès)
- ↑ Loop fusion (anglès)
- ↑ Stream fusion for Haskell Arrays (anglès)
- ↑ 60,0 60,1 paquet Stream-fusion(anglès)
- ↑ API del GHC(anglès)
- ↑ API Contenidors(anglès)
- ↑ 63,00 63,01 63,02 63,03 63,04 63,05 63,06 63,07 63,08 63,09 Data.Set
- ↑ 64,00 64,01 64,02 64,03 64,04 64,05 64,06 64,07 64,08 64,09 Data.Map
- ↑ 65,00 65,01 65,02 65,03 65,04 65,05 65,06 65,07 65,08 65,09 Data.IntSet
- ↑ 66,00 66,01 66,02 66,03 66,04 66,05 66,06 66,07 66,08 66,09 Data.IntMap
- ↑ paquet hashmap (anglès)
- ↑ paquet Hashable(anglès)
- ↑ 69,0 69,1 69,2 69,3 69,4 69,5 69,6 69,7 paquet bytestring-trie(anglès)
- ↑ 70,0 70,1 70,2 70,3 70,4 70,5 70,6 70,7 70,8 Data.Sequence(anglès)
- ↑ Guia per als vectors multidimensionals Repa(anglès)
- ↑ 72,0 72,1 72,2 72,3 72,4 72,5 72,6 Data.Array.Repa(anglès)
- ↑ paquet Vector(anglès)
- ↑ Haskell numèric: Guia sobre Vector(anglès)
- ↑ Famílies de tipus (anglès)
- ↑ 76,0 76,1 76,2 76,3 76,4 76,5 76,6 76,7 76,8 Data.List
- ↑ 77,00 77,01 77,02 77,03 77,04 77,05 77,06 77,07 77,08 77,09 Data.ByteString(anglès)
- ↑ 78,00 78,01 78,02 78,03 78,04 78,05 78,06 78,07 78,08 78,09 Data.Text(anglès)
- ↑ Data.Text - Tipus intern del(anglès)
- ↑ 80,0 80,1 80,2 80,3 80,4 80,5 80,6 80,7 Data.Array
- ↑ 81,0 81,1 81,2 81,3 81,4 81,5 81,6 81,7 Data.Vector(anglès)
- ↑ 82,0 82,1 82,2 82,3 82,4 Data.Vector.Mutable(anglès)
- ↑ 83,0 83,1 83,2 83,3 83,4 83,5 Data.Array.IArray
- ↑ 84,0 84,1 84,2 84,3 84,4 84,5 Data.Array.MArray
- ↑ 85,0 85,1 85,2 85,3 85,4 85,5 85,6 85,7 Data.Tree
- ↑ Data.Text - Tipus intern del(anglès)
- ↑ 87,0 87,1 87,2 Data.Foldable
- ↑ Data.Monoid
- ↑ Data.Functor
- ↑ Data.Traversable
- ↑ Data.Binary(anglès) Serialització
- ↑ 92,0 92,1 Data.String
- ↑ Extensió OverloadedStrings(anglès)
- ↑ Dependent type (anglès)
- ↑ Paquet type-level-natural-number(anglès)
- ↑ Paquet numtype-tf(anglès)
- ↑ dimensional-tf(anglès)
- ↑ Haskell Trac - TypeNats(anglès)
- ↑ GHC >= 7.6.1. - Promoted literals
- ↑ No more exceptions: debugging Haskell code with GHCi(anglès)
- ↑ Tipus fantasma (anglès)
- ↑ Paquet hlint que genera el programa del mateix nom(anglès)
- ↑ Cabal-dev - Sandboxed development builds for Haskell(anglès)
- ↑ cabal-meta (anglès)
- ↑ cabal-ghci (anglès)
- ↑ cabal-progdeps (anglès)
- ↑ yackage (anglès)
- ↑ Threadscope
- ↑ Ajustatge fi del paral·lelisme amb ThreadScope
- ↑ Haskell Program Coverage(anglès) eina que mostra quin codi no s'ha executat mai i condicions sempre certes o sempre falses
- ↑ Hp2any - Obtenció del perfil d'ús de memòria(anglès)
- ↑ Eines de desenvolupament de programes(anglès)
- ↑ The Exception type (anglès)
- ↑ GHC extra - Seleccions estil SQL a les llistes(anglès)
- ↑ Data.Tree(anglès)
- ↑ GHC floatRange(anglès)
- ↑ wiki anglesa - Significand(anglès)
- ↑ 118,0 118,1 Mòduls mútuament recursius(anglès)
Enllaços externs
- Pàgina oficial de GHC (anglès)
- GHC a les distribucions Linux/Unix(anglès)
- GHC - Opcions de compilació (anglès)
- El Run Time System del GHC (anglès)
- Fent servir biblioteques compartides (de relligat dinàmic) (anglès)
- Codi Haskell tan ràpid que treu fum amb el nou rerefons LLVM (anglès) Estudi de la millora de rendiment arran de la incorporació del rerefons de compilació LLVM.
- Cadarache-2012 (anglès) Diapos. de presentacions de Simon Marlow: 1. Parallel haskell, 2. La mònada Par, 3. Concurrent Haskell, 4. STM, 5 Server apps., 6. Cloud Haskell, 7. Accelerate
- Haskell Exchange 2012 (anglès)
Notícies
- Blog de GHC (anglès) mudat a [1](anglès)
- Parallel Haskell Digest (anglès)
- Reddit.com - etiqueta Haskell
Pròximament
- GHC stable docs (anglès) API del muntatge diari (nightly build) i nova documentació.
- GHC 7.4 Constraint kinds (anglès)
- Type Level Naturals (anglès)
Col·laboracions
- Well-typed - Parallel Haskell - Projecte de dos anys per impulsar-ne l'ús (anglès) Microsoft Research patrocina millores de desenvolupament per popularitzar el paral·lelisme de GHC, coordinat per Well-Typed - Es cerquen organitzacions per col·laborar-hi. (29 d'abril de 2010)