Vés al contingut

Glasgow Haskell Compiler

De Viki.cat

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

  1. si la versió de GHC és la 7.4.x i dóna error en compilar el paquet bmp,
  2. 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">

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

  • Pròximament: "SIMD Support to Data Parallel Haskell" [34][29]

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] i Map κ ε[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] i IntMap ε[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 ε i MVector σ ε 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
      • ...

    generació

    seqüències - generació
    paquet tipus context buit amb un elem. de llista o
    funció generadora
    base [ε][76] [ ] [x] ll. per comprensió
    repeat x
    replicate n x
    iterate (f::a -> a) x
    cycle llista
    containers Seq ε[70] empty singleton x fromList llista
    bytestring ByteString[77]
    -- UArray Word8
    empty 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ó generadora
    array Array ι ε[80]
    -- immutable
    listArray (iMin, iMax) llista
    array (iMin, iMax) llistaD'Associacions -- parells (índx, x)
    vector Vector ε[81]
    -- immutable
    empty singleton x fromList llista
    replicate n x
    generate llarg (f :: Int -> ε)
    MVector σ ε[82]
    -- mudable
    Vector.thaw ivector -- descongela immutable
    replicate n x
    replicateM n acció
    clone mvector
    repa 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 iarray
    estructures - generació
    paquet tipus context buit amb un elem. de llista o
    funció generadora
    containers IntSet[65] empty singleton x fromList llista
    fromAscList llista -- de llista ascendent
    IntMap ε[66] empty singleton clau x fromList llistaD'Associacions -- parells (clau, x)
    fromAscList llistaD'Associacions
    containers
    hashmap
    Set ε[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                                        -- congela
    

    main = 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
    permutations
    Seq ε[70] null length -- encaix de viewl seq
    EmptyL | (x :< xseq)
    -- encaix de viewr seq
    EmptyR | (x :> xseq)
    -- via Foldable
    ByteString[77]
    -- UArray Word8
    null 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
    assocs
    Vector ε[81] (Eq ε) null length elem x find predicat toList
    MVector σ ε[82] null length Vector.freeze --cap a Vector
    multidim. repa
    Array sh ε[72]
    (Shape sh,
    Elt ε)
    size . extent toList
    toScalar -- cas de shape DIM0
    extent
      ::(Shape sh)
    classes interfícies
    IArray α ε[83]
    -- immutables
    bounds
    indices
    elems
    assocs
    MArray α ε m[84]
    -- mudables
    (Monad m) getBounds
    getElems
    getAssocs
    -- cap a IArray
    freeze
    estructures - 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
    toList
    submap 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. / índexos
    altres transforma'n
    una llista
    [ε][76] (x :) delete x map (f:: a -> b)
    mapAccum{L|R} (f:: acc -> x -> (acc, y)) acumIni
    nub --elimina duplicats
    reverse
    intersperse x
    transpose
    intercalate llista
    Seq ε[70] (x <|) -- a l'esquerra
    (|> x) -- a la dreta
    mapWithIndex (f:: Int -> a -> b) reverse
    ByteString[77] cons x -- al davant
    snoc x -- al darrera
    map (f:: Word8 -> Word8) reverse
    intersperse byte
    transpose
    intercalate bstr
    Text[78] 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
    transpose
    intercalate txt
    vectors - actualitza/transforma
    transforma
    tipus afegeix elimina actualitza aplicació
    als elems. / índexos
    altres transforma'n
    una llista
    Array ι ε[80] (// llistaD'Associacions) ixmap (iMin, iMax) (f::i -> j)
    Vector ε[81] cons x -- pel davant
    snoc x -- pel darrera
    map (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
    transpose
    classes 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. / índexos
    altres
    (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 clau
    map (f:: a -> b)
    mapWithKey (f:: κ -> a -> b)
    mapAccum (f:: acc -> a -> (acc, b)) acumIni
    Trie ε[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]
    (++)
    append
    classes interfícies
    IArray α ε[83]
    MArray α ε m[84]
    com a conjunt
    tipus diferència unió uneix-ne
    una llista
    intersecció és sub. és sub.
    estrictament
    [ε][76] (\\) union intersect
    Seq ε[70]
    ByteString[77]
    Text[78]
    (Ord ε) => Set ε[63]
    IntSet[65]
    (\\)
    difference
    union unions intersection isSubsetOf isProperSubsetOf
    (Ord κ) => Map κ ε[64]
    IntMap ε[66]
    (\\)
    difference
    union unions intersection isSubmapOf isProperSubmapOf
    Trie ε[69] unionL
    unionR
    Tree ε[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 clau
    cerca l'índex
    de l'elem.
    :: Maybe Int
    cerca indexos
    ::[Int]
    elimina actualitza
    [ε][76] (!! índx) elemIndex x
    findIndex predicat
    elemIndices x
    findIndices predicat
    Seq ε[70] (`í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[77] (`índex` índx) elemIndex x -- pel davant
    elemIndexEnd x -- pel darrera
    findIndex predicat
    elemIndices x
    findIndices predicat
    Text[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 clau
    cerca l'índex
    de l'elem.
    :: Maybe Int
    cerca indexos
    elimina actualitza
    Array ι ε[80] (! índx)
    Vector ε[81] (! índx) elemIndex x
    findIndex predicat
    -- retornen :: Vector Int
    elemIndices x
    findIndices predicat
    MVector σ ε[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 clau
    cerca l'índex
    de l'elem.
    :: Maybe Int
    cerca 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]
    • la versió total per a (! clau) de Map és (lookup clau)

    plegat

    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 ε)
    [ε][76] and
    or
    all predicat
    any predicat
    minimum
    maximum
    sum
    product
    concatMap (f::a->[b]) concat
    ByteString[77] all predicat
    any predicat
    minimum
    maximum
    concatMap (f::Word8->ByteString) concat
    Text[78] all predicat
    any predicat
    minimum
    maximum
    concatMap (f::Char->Text) concat
    Set ε[63]
    IntSet[65]
    findMin
    findMax
    Map κ ε[64]
    IntMap ε[66]
    -- min/max de la clau
    findMin
    findMax
    Vector ε[81] and
    or
    all predicat
    any predicat
    minimum
    maximum
    sum
    product
    concatMap (f::a -> Vector b) concat minIndex
    maxIndex
    classes
    Foldable contenidor[87] and
    or
    all predicat
    any predicat
    minimum
    maximum
    sum
    product
    concatMap (f::a->[b]) concat

    partició

    seqüències - partició
    tipus a l'índex en inferiors
    i superiors
    s/.
    predicat
    :: (t,t) :: (t,t)
    [ε][76] take n
    drop n
    splitAt índx filter predicat
    takeWhile predicat
    dropWhile predicat
    partition predicat
    Seq ε[70] 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[77] take n
    drop n
    splitAt índx filter predicat
    takeWhile predicat
    dropWhile predicat
    partition predicat
    Text[78] take n
    drop n
    splitAt índx filter predicat
    takeWhile predicat
    dropWhile predicat
    partition predicat
    vectors - partició
    tipus a l'índex en inferiors
    i superiors
    s/.
    predicat
    Array ι ε[80]
    Vector ε[81] splitAt índx
    take n
    drop n
    slice índx n
    partition predicat
    filter predicat
    takeWhile predicat
    dropWhile predicat
    MVector σ ε[82] splitAt índx
    take n
    drop n
    slice índx n
    estructures - partició
    tipus a l'índex en inferiors
    i superiors
    (estrictament)
    s/.
    predicat
    pel prefix
    (Ord ε) => Set ε[63]
    IntSet[65]
    split pivot partition predicat
    filter predicat
    (Ord κ) => Map κ ε[64]
    IntMap ε[66]
    split clauPivot partition predicat
    filter predicat
    Trie ε[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] (Binary ε)
    Seq ε[70] (Binary ε)
    Set ε[63]
    IntSet[65]
    (Ord ε) No No No No No (Binary ε)
    Map κ ε[64]
    IntMap ε[66]
    (Ord κ) No No No (Binary κ,
    Binary ε)
    Trie ε[69] (Monoid ε) No (Binary ε)
    Tree ε[85] No No (Binary ε)
    Array ι ε[80] No No No No No No (Binary ι,
    Binary ε)
    ByteString[77] No No No No No No
    Text[78] 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]
    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 imprimeix

    aproxAmbDepuració 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 -> Nothing
    

    totalIndex [] 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]
    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.[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 = acum
    

    avalua_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)


    main = print consulta

    </source>

    dó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
           |> putStrLn
    

    main = 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)

    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é

    Referències

    1. Estàndard actual del llenguatge Haskell
    2. La "Plataforma Haskell"(anglès)
    3. 3,0 3,1 3,2 Docum. de la Haskell Platform(anglès)
    4. Anunci de la GHC 6.12.1(anglès)
    5. Documentació del compilador GHC
    6. Opcions del RunTimeSystem per controlar la mem. dinàmica i el recollidor de memòria brossa(anglès)
    7. RTS - Envelliment(anglès)
    8. RTS - Allotjament en Blocs
    9. RTS - El planificador(anglès)
    10. El recollidor de brossa(anglès)
    11. RTS - El recollidor de brossa.
    12. Ús de biblioteques d'enllaç dinàmic (anglès)
    13. Suport d'enllaç dinàmic a les diferents plataformes (anglès)
    14. Building and using Win32 DLLs(anglès)
    15. Parallel Haskell Digest(anglès)
    16. primitives de paral·lelisme(anglès)
    17. Haskell Paral·lel
    18. GHC seq vs. pseq(anglès) Comparació de les primitives seq i pseq
    19. GHC - primitiva pseq(anglès)
    20. GHC - Utilitzant multiprocés simètric SMP
    21. El planificador del GHC - fils d'exec. del sistema i fils del GHC (anglès)
    22. GHC - Hints (cat:Pistes) for using SMP parallelism (anglès)
    23. 23,0 23,1 Glasgow Parallel Haskell (anglès) Estratègies de paral·lelisme
    24. 24,0 24,1 Control.Parallel.Strategies(anglès)
    25. Seq no more: Better Strategies for Parallel Haskell(anglès) Prou de seq (seqüencial) - millors estratègies per al Haskell paral·lel
    26. haskellWiki - paral·lelisme(anglès)
    27. REgular PArallel arrays(anglès)
    28. SIMD Support for the vector library (anglès)
    29. 29,0 29,1 GHC SIMD (anglès)
    30. Haskell - Paral·lelisme de dades
    31. 31,0 31,1 Nested Data Parallelism - presentació (diapos)
    32. Paquet dph-examples
    33. Guy E. Blelloch - Programming Parallel Algorithms - Nested Data Parallellism
    34. SIMD Support to Data Parallel Haskell(anglès)
    35. El paquet accelerate-opencl(anglès)
    36. El paquet accelerate-repa(anglès)
    37. Biblioteca Accelerate per al paral·lelisme a les GPU(anglès)
    38. paquet OpenCLWrappers(anglès)
    39. OpenCL from Haskell(anglès)
    40. How to write hybrid CPU/GPU programs with Haskell(anglès)
    41. El paquet meta-par(anglès)
    42. Template Haskell(anglès)
    43. Depuració(anglès)
    44. 44,0 44,1 HaskellWiki - Stack overflow
    45. Explicit CallStack (anglès)
    46. 46,0 46,1 HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
    47. El paquet loch-th aporta un mecanisme per informar de les crides a les assercions fallides(anglès)
    48. force del paquet deepseq permet avaluar els components d'una dada
    49. 49,0 49,1 Yesod - plantilles(anglès)
    50. Quasi-Quotation a la viqui anglesa(anglès)
    51. Sintaxi dels quasiQuoters(anglès)
    52. QuasiQuotation
    53. Tipus algebraics del TemplateHaskell (anglès)
    54. Haskellwiki - Quasi-Quotation(anglès)
    55. Constructor QuasiQuoter al paquet template-haskell(anglès)
    56. Fusionant successius Map: Fent que Haskell sigui un 225% més ràpid (anglès)
    57. From lists to streams to nothing at all(anglès)
    58. Loop fusion (anglès)
    59. Stream fusion for Haskell Arrays (anglès)
    60. 60,0 60,1 paquet Stream-fusion(anglès)
    61. API del GHC(anglès)
    62. API Contenidors(anglès)
    63. 63,00 63,01 63,02 63,03 63,04 63,05 63,06 63,07 63,08 63,09 Data.Set
    64. 64,00 64,01 64,02 64,03 64,04 64,05 64,06 64,07 64,08 64,09 Data.Map
    65. 65,00 65,01 65,02 65,03 65,04 65,05 65,06 65,07 65,08 65,09 Data.IntSet
    66. 66,00 66,01 66,02 66,03 66,04 66,05 66,06 66,07 66,08 66,09 Data.IntMap
    67. paquet hashmap (anglès)
    68. paquet Hashable(anglès)
    69. 69,0 69,1 69,2 69,3 69,4 69,5 69,6 69,7 paquet bytestring-trie(anglès)
    70. 70,0 70,1 70,2 70,3 70,4 70,5 70,6 70,7 70,8 Data.Sequence(anglès)
    71. Guia per als vectors multidimensionals Repa(anglès)
    72. 72,0 72,1 72,2 72,3 72,4 72,5 72,6 Data.Array.Repa(anglès)
    73. paquet Vector(anglès)
    74. Haskell numèric: Guia sobre Vector(anglès)
    75. Famílies de tipus (anglès)
    76. 76,0 76,1 76,2 76,3 76,4 76,5 76,6 76,7 76,8 Data.List
    77. 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. 78,00 78,01 78,02 78,03 78,04 78,05 78,06 78,07 78,08 78,09 Data.Text(anglès)
    79. Data.Text - Tipus intern del(anglès)
    80. 80,0 80,1 80,2 80,3 80,4 80,5 80,6 80,7 Data.Array
    81. 81,0 81,1 81,2 81,3 81,4 81,5 81,6 81,7 Data.Vector(anglès)
    82. 82,0 82,1 82,2 82,3 82,4 Data.Vector.Mutable(anglès)
    83. 83,0 83,1 83,2 83,3 83,4 83,5 Data.Array.IArray
    84. 84,0 84,1 84,2 84,3 84,4 84,5 Data.Array.MArray
    85. 85,0 85,1 85,2 85,3 85,4 85,5 85,6 85,7 Data.Tree
    86. Data.Text - Tipus intern del(anglès)
    87. 87,0 87,1 87,2 Data.Foldable
    88. Data.Monoid
    89. Data.Functor
    90. Data.Traversable
    91. Data.Binary(anglès) Serialització
    92. 92,0 92,1 Data.String
    93. Extensió OverloadedStrings(anglès)
    94. Dependent type (anglès)
    95. Paquet type-level-natural-number(anglès)
    96. Paquet numtype-tf(anglès)
    97. dimensional-tf(anglès)
    98. Haskell Trac - TypeNats(anglès)
    99. GHC >= 7.6.1. - Promoted literals
    100. No more exceptions: debugging Haskell code with GHCi(anglès)
    101. Tipus fantasma (anglès)
    102. Paquet hlint que genera el programa del mateix nom(anglès)
    103. Cabal-dev - Sandboxed development builds for Haskell(anglès)
    104. cabal-meta (anglès)
    105. cabal-ghci (anglès)
    106. cabal-progdeps (anglès)
    107. yackage (anglès)
    108. Threadscope
    109. Ajustatge fi del paral·lelisme amb ThreadScope
    110. Haskell Program Coverage(anglès) eina que mostra quin codi no s'ha executat mai i condicions sempre certes o sempre falses
    111. Hp2any - Obtenció del perfil d'ús de memòria(anglès)
    112. Eines de desenvolupament de programes(anglès)
    113. The Exception type (anglès)
    114. GHC extra - Seleccions estil SQL a les llistes(anglès)
    115. Data.Tree(anglès)
    116. GHC floatRange(anglès)
    117. wiki anglesa - Significand(anglès)
    118. 118,0 118,1 Mòduls mútuament recursius(anglès)

    Enllaços externs

    Notícies

    Pròximament

    Col·laboracions