Compilador_Haskell_de_Glasgow
Tipus | compilador, programari lliure, programari de codi obert, programari lliure i de codi obert i compilador optimitzador |
---|---|
Versió inicial | 1989 |
Versió estable | |
Llicència | llicència BSD de 3 clàusules |
Part de | Haskell Platform (en) |
Característiques tècniques | |
Sistema operatiu | Linux, FreeBSD, NetBSD, OpenBSD, Solaris, macOS, Microsoft Windows i DragonFly BSD |
Escrit en | C i Haskell |
Equip | |
Desenvolupador(s) | Universitat de Glasgow |
Més informació | |
Lloc web | haskell.org… (anglès) |
Free Software Directory | Ghc |
| |
ghc, ordre d'ordinador que respon a la sigla de "Glasgow Haskell Compiler", en català Compilador Haskell de Glasgow, és un compilador nadiu de codi lliure per al llenguatge de programació funcional Haskell, el qual va ser originalment desenvolupat a la universitat de Glasgow per Simon Peyton Jones i Simon Marlow. El desenvolupament continua actualment sota l'auspici del Departament d'Investigació de Microsoft (Microsoft Research), al Regne unit on treballa Peyton Jones. GHC ha pres humorísticament el sobrenom "Glorious Haskell Compiler".
El nom ghc segueix el costum antic d'anomenar les ordres amb un identificador curt d'una a tres lletres, on la 'c' designa compilador, la 'h' designa el llenguatge i la primera lletra, la ciutat de la universitat que el desenvolupà originàriament (com també ho fa mmc: "Compilador Mercury de Melbourne",[1] o bé yhc:"Compilador Haskell de York"[2] o bé uhc:"Compilador Haskell d'Utrecht").[3]
El compilador també està escrit en Haskell (una tècnica coneguda com a bootstrapping), però el nucli de sistema per a Haskell està escrit en C i una adaptació del C--[4][5] (versió de C, no per a l'ús humà sinó específica per a sortida de compiladors, com a interfície independent del maquinari) referida com a Cmm en el codi del compilador.[6] L'última versió del compilador compleix amb l'estàndard més nou del llenguatge, que és, ara per ara, el Haskell 2010.[7] 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 venen amb el compilador sinó que es distribueixen en edicions diferides, dins la Plataforma Haskell ("The Haskell Platform").[8][9] 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[10]
- HaskellStack subordina GHC
Stackage és un rebost que per cada versió de GHC guarda instantànies d'un conjunt de biblioteques compatibles amb compilació verificada, evitant la problemàtica de seleccionar per cada biblioteca una versió que encaixi en els requeriments de les restants.
HaskellStack (2015) possibilita el desenvolupament multiversió respecte de GHC, i descarrega automàticament la versió de GHC corresponent al rebost Stackage més recent que inclogui les dependències (biblioteques) d'una aplicació si hi ha fitxer de projecte .cabal al directori, proporcionant una eina que subordina i encapsula diverses versions de GHC i dels rebostos per al desenvolupament d'aplicacions en capsa de proves (Sandbox).
També proporciona un conjunt de plantilles d'aplicacions (comanda: stack templates) per engegar projectes nous (stack new projecte plantilla).[11][12][13]
Biblioteques estàndard i correspondències de versions
Biblioteques de la darrera versió.[14]
Normalment la propietat "since version X.X.X.X" de la doc. de les funcions ofereix la versió inicial de la biblioteca on es van definir.
Per saber quina versió mínima de GHC suporta una biblioteca estàndard, cal consultar les taules.
- taula de versions corresp. a GHC de 6.6 a 7.2[15]
- taula de versions més recents.[16] Es pot consultar versions anteriors al llistat d'actualitzacions de la pàg.
Compilar codi antic Haskell98 amb GHC
Per compilar projectes antics en Haskell98 cal tenir en compte que
- el llenguatge per defecte ja no és el mateix (ara és Haskell2010), caldrà afegir al fitxer de projecte (.cabal):
Default-language: Haskell98
- especificar la biblioteca haskell98 a les dependències
Si no fa servir excepcions, potser en tindreu prou amb els mòduls de la biblio haskell98
Build-Depends: haskell98, ...
Si fa servir excepcions caldrà incloure el paquet base, i desambiguar, a les importacions, la biblioteca d'origen d'alguns mòduls Prelude, Numeric, ... presents a totes dues, com s'explica més avall.
Build-Depends: base, haskell98, ...
- el sistema d'excepcions ja no és el mateix.
- Haskell98 agrupa les excepcions en un tipus Exception, unió discriminada dels diferents tipus d'excepcions. La pega és que és un grup tancat.
-- Haskell98
data Exception
= IOException IOException -- IO exceptions
| ArithException ArithException -- Arithmetic exceptions
| ArrayException ArrayException -- Array-related exceptions
| ErrorCall String -- Calls to 'error'
| ExitException ExitCode -- Calls to 'System.exitWith'
| NonTermination -- Program is in an infinite loop
| UserError String -- General purpose exception
...
- Haskell2010 modela les excepcions en una interfície, permetent caracteritzar com a excepcions, diferents tipus on l'usuari pot especificar els camps que convingui, requerint que han d'implementar les interfícies de les classes de tipus Exception, Show i Data.Typeable.
- La implementació de la classe Exception (del H2010) es pot derivar automàticament si el tipus implementa Show, afegint la derivació de la classe Data.Typeable i esmentant l'extensió de llenguatge DeriveDataTypeable.
- Hi ha un tipus genèric per recollir les excepcions (del H2010) no tractades, SomeException definit de manera existencial amb l'excepció com a component.
-- Haskell2010
data SomeException = forall e. Exception e => SomeException e
-- classe x convertir en excepció un tipus definit x l'usuari
class (Typeable e, Show e) => Exception e where
toException :: e -> SomeException
fromException :: SomeException -> Maybe e
Exemple de tractament d'excepcions al H98 i H2010 aquí.
Les excepcions antigues, des de GHC 6.10.1 van passar al mòdul Control.OldException del paquet base,[17] s'hi van conservar fins a la versió 7.4.2[18] i ha estat eliminat a la versió GHC-7.6.1.[19]
Primer de tot cal descarregar[20] el compilador de GHC-7.4.2, l'últim que conté el mòdul Control.OldException, configurar i instal·lar, en una carpeta de l'usuari (subdir. de $HOME):
# en un sistema UNIX
cd carpeta-d-extracció-del-paquet-GHC-7.4.2
./configure --prefix=$HOME/carpeta-d-instal·lacio
make install
# establir variables d'entorn
GHC_VER=7.4.2
GHC_HOME=$HOME/carpeta-d-instal·lacio/ghc-$GHC_VER
export GHC=$GHC_HOME/bin/ghc # camí de GHC per a fitxers de comandes (.sh | Makefile)
export PATH=$GHC_HOME/bin:$PATH # camí d'executables
export LD_LIBRARY_PATH=$GHC_HOME/lib/ghc-$GHC_VER/:$LD_LIBRARY_PATH # camí de biblioteques de càrrega dinàmica (.so | .dll)
export LIBRARY_PATH=$GHC_HOME/lib/ghc-$GHC_VER/:$LIBRARY_PATH # camí de biblioteques per a compilació
- Caldrà reanomenar les importacions de Exception i també Control.Exception
import Control.OldException ... -- en comptes de Control.Exception
- Després, en compilar un mòdul H98 apareix un error respecte el Prelude (mòdul de predefinits):
Ambiguous module name ‘Prelude’: it was found in multiple packages: base haskell98
Una manera de desambiguar-ho és explicitant a cada mòdul l'origen del Prelude desitjat.
-- Al fitxer de projecte (.cabal) indicar: Extensions: NoImplicitPrelude -- no volem el Prelude per defecte PackageImports -- volem especificar el paquet als "import"
-- als fitxers fonts:
import "haskell98" Prelude
Gestió de memòria
Vegeu doc.[21] GHC compila per defecte amb memòria dinàmica il·limitada. Per establir-ne límits vegeu el senyal de compilació -M.[22] 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ó.[23]
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.[24]
- 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 multiprocessador (quan es fa servir l'opció -threaded).
- Nursery (maternitat)
- zona d'allotjament de dades noves (una per capability/P.E.), del recollidor de memòria brossa generacional.
A l'inici les dades s'allotgen a la maternitat (ang:nursery) (conjunt fix de blocs) i se'n disposa d'una per capability.[25] després passen a la generació 0, i següents[26] tantes com s'estableixi a les opcions de l'intèrpret d'ordres per al RunTimeSystem.
Per la generació més vella, es pot triar el sistema de recollida de la brossa, entre el mètode de Cheney (aturar la recollida i copiar dades a un espai net) per defecte i el mètode de compactació que estalvia espai però és més lent. Les altres generacions fan servir el mètode de Cheney.[27]
- Per forçar la recollida de brossa quan convingui:
System.Mem.performGC
[28] - Referències dèbils (ang:weak pointer) per a taules de memoïtzació[29][30] i també per a MVar's (Mutable variable) amb
mkWeakMVar
.[31]
Fent servir biblioteques de relligat dinàmic
A partir de GHC >= 6.12[32][33][34] Cal que GHC s'hagi compilat amb ./configure --enable-shared
# relliga amb les versions compartides (no estàtiques) (bib.so o bé bib.dll segons sistema op.) del RunTimeSystem i dels paquets
ghc --make -dynamic Main.hs
Paral·lelisme
Novetats al bloc butlletí "Parallel Haskell Digest"[35]
Paral·lelisme de tasques
Primitives de paral·lelisme - Compilació per a processadors multicor
Vegeu-ho a Haskell concurrent#Primitives de paral·lelisme - Compilació per a processadors multicor
Futurs
Encadenament de càlculs asíncrons - la mònada Par
Vegeu-ho a Haskell concurrent#Encadenament de càlculs asíncrons - la mònada Par
El paquet meta-par ofereix una versió de la mònada Par per compartir recursos de hardware amb un planificador de tasques SMP.[36]
Paral·lelisme en operacions IO - El functor aplicatiu Concurrently
Vegeu-ho a Haskell concurrent#Operacions d'Entrada/Sortida asíncrones i simultànies
Paral·lelisme de dades
Vectors pluridimensionals de procés paral·lel (Regular parallel arrays)
Un primer nivell de tractament paral·lel s'ofereix mitjançant el paquet Repa ("Regular parallel arrays")[37][38] 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"[39][40][41] basat en treballs de Guy E. Blelloch.[42]
Per a proves cal instal·lar el paquet dph-examples.
cabal install dph-examples
#si la versió de GHC és la 7.4.x i dona error en compilar el paquet ''bmp'',
# caldrà provar forçant la versió anterior a la que peta.
cabal install dph-examples "--constraint= bmp < 1.2.3.1"
Cal fixar-se en la versió instal·lada (diferent segons la versió de GHC) i descarregar-ne les fonts per investigar.
# per obtenir-ne la versió instal·lada
ghc-pkg list | grep dph-examples
Per desenvolupar-hi cal tenir en compte que...
- Cal separar les operacions per mòduls segons si són o no vectoritzades
- A la interfície el tipus ha de ser (PArray t)
- El tipus vector (
[: t :]
) és NO-polimòrfic i cal especificar el tipus de l'element, com ara[: Double :]
, les operacions sobre els tipus primitius no estan sobrecarregades i per cada tipus cal importar-ne el mòdul d'operacions corresponent (per ex.: Data.Array.Parallel.Prelude.Double).
Vegeu exemple més avall.
SIMD - Single Instruction, Multiple Data - Ús de les instruccions de CPU vectoritzades
Aprofitament de les instruccions de dades vectoritzades dels processadors (MMX, 3DNow!, SSE, Altivec, AVX, ...) per al procés a CPU de corrents de dades multimèdia.
Tipus i operacions primitius SIMD a GHC.Prim.[43]
Pàg. inicial sobre SIMD a GHC (Hi ha una branca de GHC per a desenvolupament en SIMD, però els paquets esmentats més avall i l'exemple funcionen en versions estàndard de GHC utilitzant el rerefons de compilació LLVM).[44]
Paquets d'interfície SIMD: simd,[45] primitive-simd.[46]
Exemple amb dades vectoritzades de 4 Floats (4x32 bits), per a un ordinador amb SIMD sse2 de 128 bits (Pentium 4 o posterior). Compilat amb GHC v. 7.10.1. Requereix l'ús del rerefons de compilació LLVM (compilar amb l'opció -fllvm).
# la versió del paquet "simd" del Hackage té un error a la funció unVectorizeUnboxedX4
# cal baixar la del GitHub
git clone https://github.com/mikeizbicki/simd
cd simd
cabal install --allow-newer # (--allow-newer): ignora els límits superiors de les dependències
{-# OPTIONS_GHC -fllvm #-} -- opció per compilar amb el rerefons de compilació LLVM
{-# LANGUAGE PackageImports, ExistentialQuantification, StandaloneDeriving #-}
import "vector" Data.Vector.Unboxed as V
import "simd" Data.SIMD (X4, vectorizeUnboxedX4, unVectorizeUnboxedX4)
import Control.Monad as M
-- Hi ha versions de tipus (X<n> a), n ∈ {4,8,16} per a paquets SIMD de n elements de tipus 'a'
-- amb instàncies de les classes numèriques definides per als tipus (Xn Float), (Xn IntN), ...
-- vectorizeUnboxedX4 :: V.Vector a -> V.Vector (X4 a) -- simd-vectoritza (empaqueta els elements) un vector Unboxed
-- unVectorizeUnboxedX4 :: V.Vector (X4 a) -> V.Vector a -- inversa de vectorizeUnboxedX4
-- hi ha un error a la funció unVectorizeUnboxedXn del paquet simd-0.1.0.1 (a tots els mòduls SIMD<n>),
-- però ja està arreglat a la versió que hi ha al 'github'
default (Int, Float)
mostra :: Int -> V.Vector Float
mostra n = V.fromList [1 .. fromIntegral n]
mkFloatVec :: V.Vector Float -> V.Vector (X4 Float) -- comprova i simd-vectoritza un vector normal de 'floats'
mkFloatVec vec
| V.length vec `mod` 4 == 0 = vectorizeUnboxedX4 vec
| otherwise = error "mkVec: el nombre d'elements ha d'omplir els paquets completament"
data Obj = forall a. Show a => Obj a -- objectes presentables
deriving instance Show Obj -- la derivació d'instàncies dels objectes existencials va separada
main = do
let v1 = mkFloatVec v0
v2 = V.map (/ 2.0) v1
v3 = unVectorizeUnboxedX4 v2 -- cal la versió del GitHub perquè funcioni correctament
-- com que v0..v3 són de tipus diferents, farem una llista d'objectes presentables
M.forM_ [Obj v0, Obj v1,
Obj v2, Obj v3] print -- print = show >>> putStrLn
where
v0 = mostra 8
dona:
Obj [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0]
Obj [(1.0,2.0,3.0,4.0),(5.0,6.0,7.0,8.0)]
Obj [(0.5,1.0,1.5,2.0),(2.5,3.0,3.5,4.0)]
Obj [0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0]
Paral·lelisme GPGPU
- Per a targetes gràfiques amb capacitat GPGPU de Nvidia o AMD-ATI, o bé sobre CPU's multicor de AMD o bé Intel via OpenCL
En l'execució de rutines sobre vectors de dades, ús de la GPU per l'execució simultània del nucli d'iteració sobre els elements dels paràmetres vectors, repartint automàticament valors del cursor (que indexa els paràmetres vectors) als diferents processadors elementals.
- Biblioteca Accelerate[47][48] amb suport per a rerefons CUDA,[49] OpenCL[50] i RePa[51] (per a CPUs), i genera nuclis (d'iteracions en paral·lel) per a GPU/CPU partint d'un llenguatge incrustat (EDSL) en Haskell. La biblioteca Accelerate va ser desenvolupada a la Universitat de Nova Gal·les del Sud (Austràlia) amb els auspicis de NVIDIA, desenvolupant inicialment per a dispositius NVIDIA-CUDA.[47]
- Biblio. OpenCLWrappers facilita l'ús de lligams amb biblioteques externes que implementin l'estàndard OpenCL,[52] article: OpenCL from Haskell[53]
- Simultanejant càrregues de treball en CPU i GPU. Vegeu "How to write hybrid CPU/GPU programs with Haskell".[54]
Exemple GPGPU d'ús de la biblioteca Accelerate
- Mòdul principal amb intèrpret de simulació del llenguatge incrustat (EDSL: "embedded domain specific language").[55]
- Mòduls per al paral·lelisme a GPUs
- RerefonsCUDA de NVIDIA.[56] Requereix tenir instal·lat el compilador NVCC del ToolKit de NVIDIA.[57]
- Rerefons OpenCL de HIPERFIT.[58] Cal tenir instal·lat el kit de desenvolupament (SDK) del fabricant de la GPU. L'estàndard OpenCL està implementat tant per NVIDIA[59] com per AMD-ATI, però AMD a més d'implementar-lo en dispositius GPU, tracta la CPU multicor com un altre dispositiu de paral·lelisme.[60]
- Rerefons OpenCL basat en el llenguatge per al paral·lelisme Icc-Cilk de Intel.[61]
- Mòduls per al paral·lelisme a CPUs multicor
cabal install accelerate # instal·la paquet base amb simulador (intèrpret del llenguatge de domini específic)
# si teniu targeta gràfica NVIDIA amb capacitat GPGPU i heu instal·lat el kit de desenvolupament CUDA
CUDA_HOME=/usr/local/cuda-7.0
cabal install cuda \
--extra-include-dirs=$CUDA_HOME/include \
--extra-lib-dirs=$CUDA_HOME/lib64
cabal install accelerate-cuda
La versió per a OpenCL és al GitHub i el funcionament bàsic és idèntic.
{-# LANGUAGE CPP, PackageImports #-}
import Data.Array.Accelerate as A
-- biblioteques "accelerate" de procés paral·lel
#define PROC_PAR_CUDA 1 /* maquinari NVIDIA */
#define PROC_PAR_HIPERFIT_OPENCL 2 /* maquinari que suporta l'estàndard OPENCL */
#define PROC_PAR_INTEL_OPENCL 3 /* maquinari INTEL */
#define PROC_PAR_INTERPRET 0 /* sense maquinari de procés paral·lel (simulació per l'intèrpret) */
#define BIBLIO PROC_PAR_CUDA /* assigneu la vostra */
#if BIBLIO == PROC_PAR_CUDA
import qualified "accelerate-cuda" Data.Array.Accelerate.CUDA as ProcPar
#elif BIBLIO == PROC_PAR_HIPERFIT_OPENCL
import qualified "accelerate-opencl" Data.Array.Accelerate.OpenCL as ProcPar
#elif BIBLIO == PROC_PAR_INTEL_OPENCL
import qualified "accelerate-icc-opencl" Data.Array.Accelerate.OpenCL as ProcPar
#elif BIBLIO == PROC_PAR_INTERPRET
-- intèrpret del llenguatge simulant un procés paral·lel (per manca de maquinari)
import qualified "accelerate" Data.Array.Accelerate.Interpreter as ProcPar
#endif
-- `Acc` (:: Type -> Type) designa el context del dispositiu accelerador
-- `use` (:: a -> Acc a) carrega un paràmetre a la memòria del dispositiu accelerador
-- `run` (:: Acc a -> a) executa el càlcul a l'accelerador, i descarrega el resultat
-- vectors de dades amb indicador de dimensionalitat (tipus Shape)
xs, ys :: Array DIM1 Float
xs = A.fromList (Z :. 2) [1, 2]
ys = A.fromList (Z :. 2) [2, 2]
-- type Scalar a = Array DIM0 a
-- producte escalar de vectors carregats al dispositiu accelerador (Acc a)
prodEscalar :: Acc (Array DIM1 Float) -> Acc (Array DIM1 Float) -> Acc (Scalar Float)
prodEscalar xs ys = A.fold (+) 0 (A.zipWith (*) xs ys)
-- prova per al dispositiu de paral·lelisme principal (n'hi pot haver diversos GPU's o bé CPU)
-- càlcul al dispositiu: carrega dades amb 'use'; 'run' executa i descarrega el resultat
càlcul :: Scalar Float
càlcul = ProcPar.run $ prodEscalar (use xs) (use ys)
main = print càlcul
Cal compilar amb la versió multi-tasca (opció -threaded) del RunTimeSystem
$ ghc --make -threaded prova.hs
$ ./prova
Array (Z) [6.0]
Concurrència
- MVar's - Mutable/Mailbox Variable—variables protegides, aprofitant la concurrència basada en bústies d'un sol element.
- forkIO / forkOS
- Software Transactional Memory
- Futurs—Encadenament de càlculs asíncrons - la mònada Par
- Operacions d'Entrada/Sortida asíncrones i simultànies
- Cloud Haskell (computació distribuïda, a l'estil del sistema d'actors distribuïts de l'Erlang)
- Biblioteca Actor
- Communicating Haskell Processes (CSP)
Vegeu Haskell concurrent.
Metaprogramació - 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.[64]
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 o d'inserció, i vol dir que cal avaluar l'expressió en temps de compilació i substituir-la, en el codi, pel seu valor. Exemple: #Punts de control al codi amb Loch-TH
Quasi-Quotations (Plantilles per a gramàtiques DSL)
Construcció[65] que s'avalua en temps de compilació, que permet incrustar texts convertibles a codi, escrits en gramàtiques anomenades "DSL" (Domain Specific Language), específiques per a una temàtica o domini d'aplicació, generant, per metaprogramació, expressions inter-operables amb les del mòdul que l'allotja, per exemple, plantilles HTML amb interpolació d'expressions vàlides en l'àmbit d'avaluació de la plantilla.[66]
- Sintaxi
- Les acotacions QuasiQuotation utilitzen una sintaxi anomenada Claus d'Oxford [|... |] o bé [prefix|... |] on el nom del prefix indica el registre que aporta els traductors del text acotat per les claus.[67]
- Context
- Una acotació QuasiQuotation pot descriure, segons el lloc on s'utilitza, una expressió, o bé un patró, o bé un tipus, o bé una llista de declaracions a nivell de mòdul, i, per traduir-la, s'utilitzarà el traductor corresponent al context, component del tipus descrit tot seguit.
- Tipus
- Un tipus quasiQuoter es defineix com un registre amb traductors opcionals per cadascun dels 4 possibles contextos en què es pretengui inserir, assignant als altres el valor undefined.[68]
- La seva definició ha d'aportar funcions per la traducció del text a expressions del llenguatge Template Haskell que incorpora combinadors per generar clàusules de codi Haskell (metaprogramació).[69][70][71]
-- tipus del registre que aporta els traductors d'una plantilla on s'esmenta com a prefix [elMeuTraductor|...|]
data QuasiQuoter =
QuasiQuoter { -- ha d'incloure algun dels camps següents:
quoteExp :: String → Q Exp, -- traductor en context d'expressions (metaprogramació d'expressions)
quotePat :: String → Q Pat, -- traductor en context de patrons (metaprogramació de patrons)
quoteType :: String → Q Type, -- traductor en context de tipus (metaprogramació de tipus)
quoteDec :: String → Q [Dec] -- traductor en context de declaracions (metaprogramació de declaracions)
}
elMeuTraductor = QuasiQuoter { quoteExp = tradCasDExpressions, quotePat = tradCasDePatrons, ...}
Exemple senzill: document tot seguit
Traductor simple per incloure textos multilínia en expressions (estil hereDoc o document-tot-seguit), com a literals, amb la funció stringE de T.H.[72]
- les funcions de traducció per als contextos que no interessen es deixen undefined.
- l'ús i la definició han d'estar en mòduls separats.
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-missing-fields #-} -- que GHC no es queixi pel registre QuasiQuoter incomplet
module ElMeuDocTotSeguit where
import Language.Haskell.TH (stringE)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
docTotSeguit = QuasiQuoter {quoteExp = stringE} -- aporta només el traductor per al context d'expressions
-- ús
{-# LANGUAGE QuasiQuotes #-}
module Main where
import ElMeuDocTotSeguit (docTotSeguit)
-- format a GHC 7+ :
elMeuTextPluriLínia = [docTotSeguit|text de la primera línia
segona línia i següents
|]
-- format a GHC 6.12 (cal prefixar el traductor amb '$'): [$docTotSeguit|text|]
Exemple: Incrustació de HTML amb interpolació d'expressions
De les biblioteques del Yesod.[66]
La plantilla shamlet tradueix, a expressions del Template Haskell, un llenguatge de marques afegint automàticament les marques de tancament segons el sagnat.
Permet la interpolació d'expressions Haskell delimitades amb claus prefixades per un caràcter que determina la funcionalitat.
- Les claus "#{" ... "}" inclouen expressions avaluables quin tipus resultant ha de ser convertible implementant la classe Text.Blaze.ToMarkup.
- Les claus "^{" ... "}" permeten la inclusió per referència d'altres sub-plantilles de tipus coincident amb la que les allotja.
{-# LANGUAGE PackageImports, QuasiQuotes, OverloadedStrings #-}
module Plantilla (pàgina) where
-- "Hamlet" fa referència a un trosset de Html (és un joc de paraules amb les lletres de "HTML")
import "shakespeare" Text.Hamlet (Html, shamlet)
import Data.Text (Text)
nom = "Biel" :: Text
títol = "Títol de la pàgina" :: Text
data TAmic = Amic {amicNom::String, amicEdat:: Int}
elsAmics = [Amic "Joan" 30, Amic "Montse" 40]
-- Plantilla per al cos de la pàgina
-- amb #{expr} interpola l'avaluació de l'expressió per a tipus que implementin la classe Text.Blaze.ToMarkup
cosDeLaPàg :: [TAmic] -> Html
cosDeLaPàg amics = [shamlet|
<p>Hola el meu nom és #{nom}
$if null amics
<p>Ho sento, no hi ha amics.
$else
<p>Els meus amics són:
<ul>
$forall amic <- amics
<li>#{amicNom amic} que té #{amicEdat amic} anys
|]
-- Plantilla principal
-- amb ^{expr} interpola una crida a una altra plantilla QQ del mateix tipus de resultat.
pàgina :: Html
pàgina = [shamlet|
!!!
<html>
<head>
<title>#{títol}
<body>^{cosDeLaPàg elsAmics}
|]
{-# LANGUAGE PackageImports #-}
import Plantilla (pàgina)
import "blaze-markup" Text.Blaze.Renderer.Text (renderMarkup)
import qualified Data.Text.Lazy.IO as TLIO
main = TLIO.putStrLn $ renderMarkup pàgina
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 Arxivat 2009-04-22 a Wayback Machine. permet que el compilador, en fase de pre-procés, refaci (reescrigui) operacions de composició.[73]
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)[74] i especificant l'operació per cada element del corrent.
El corrent incorpora la possibilitat de tenir elements sense dades, a conseqüència del filtratge.
Les funcions com ara els filtres s'han d'aplicar començant per l'operació de conversió stream sobre l'estructura, que la converteix en corrent de dades, 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ó.[75][76]
Vegeu biblioteca Stream-fusion al rebost Hackage[77]
Referència ràpida de la biblioteca
Vegeu l'API del GHC[78] i la de la plataforma[9] Haskell.
contenidors
Vegeu ref.[79]
- Recordatori d'algunes operacions
- S'esmenten les ops. ometent el paràm. de l'estructura sobre la qual s'aplica (Col·lecció -> Col·lecció). Si l'estructura va primer, l'operació s'esmenta en forma d'aplicació parcial infix
(`op` p2 p3..)
. Cas de consultes o bé resultats opcionals, s'especifica ocasionalment el tipus del retorn. Respecte als tipus de les funcions, consulteu les refs.
notes
llistes amb optimització de fusió
- el paquet stream-fusion[77] aporta llistes amb funcions fusionables pel mètode stream fusion.
conjunts i diccionaris
Set ε
[80] iMap κ ε
[81] del paquet "containers" implementen conjunts i diccionaris basats en arbres de cerca binària balancejats amb cost de cerca O(log n).IntSet
[82] iIntMap ε
[83] 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.- el paquet hashmap[84] aporta una implementació de conjunts[85] i diccionaris[86] 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)[87] i desant al conjunt/diccionari o bé un sol element/parell o bé, en en cas de col·lisions, un cistell dels elements/parells quines claus col·lideixen, implementat com a Set/Map del paquet containers. El paquet hashmap ha quedat desaconsellat en favor del paquet unordered-containers.
- el paquet unordered-containers[88] implementa conjunts[89] i diccionaris[90] (HashSet i HashMap) quines claus han d'implementar una funció resum (classe Hashable) però no cal que implementin Ord (ordenable). El cistell de col·lisions en té prou amb la implementació de Eq per distingir-les.
seqüències finites de dos caps, amb accés més ràpid que les llistes
Seq ε
de Data.Sequence[91] 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.
- Cost d'accés aleatori inferior a les llistes: O(log(min(i,n-i))) per a l'accés indexat;
- Cost de la concatenació: O(log(min(n1,n2)));
- Cost de la consulta de llargària O(1). Avantatge sobre les llistes (on és O(n)) per prevenir l'accés indexat fora de rang.
vectors pluridimensionals
- Amb vector fem referència a seqüències d'accés aleatori (accés a qualsevol element amb cost constant O(1))
- El tipus
Array shape ε
del paquet Repa[37][92] aporta vectors pluridimensionals d'elements no-encapsulats (unboxed) (allotjats directament)- Els elements del Repa han d'implementar
class (Show a, Unbox a) => Elt a zero :: a one :: a ...
- Els tipus dels índexs del Repa prenen una forma semblant a una pila de dimensions, i implementen la classe Shape:
type DIM0 = Z -- zero dimensions: escalar type DIM1 = DIM0 :. Int -- una dimensió, ex. Z :. 3 type DIM2 = DIM1 :. Int -- dues dimensions, ex. Z :. 3 :. 3 ...
- Data.Array.Repa utilitza la terminologia dels llenguatges on l'avaluació tardana és explícita, anomenant vectors diferits (ang:delayed) les aplicacions no-avaluades, per exemple amb map, forçant-ne l'avaluació amb la funció force (com a l'OCaml) que avalua, paral·lelitzant els càlculs.
A ghci:
Prelude>:m +Data.Array.Repa Prelude Data.Array.Repa> let v22 = fromList (Z :. 2 :. 2) [1,2,3,4] :: Array DIM2 Int Prelude Data.Array.Repa> let consultaV22 i j = v22 ! (Z :. i :. j) Prelude Data.Array.Repa> consultaV22 1 0 3 Prelude Data.Array.Repa> extent v22 (Z :. 2) :. 2 Prelude Data.Array.Repa> size $ extent v22 4 Prelude Data.Array.Repa> let w22 = force $ Data.Array.Repa.map (\x → x * 2) v22 Prelude Data.Array.Repa> w22 -- Data.Array.Repa.force avalua els mapejos en paral·lel Array (Z :. 2 :. 2) [2,4,6,8]
Vectors d'alt rendiment
Vectors amb índexs enters amb base 0.
Vector ε
i MVector σ ε
del paquet Vector[93] 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ó).[94][95]
- cas d'allotjament directe dels elements: Per a vectors d'elements de tipus primitius el mòdul Data.Vector.Unboxed aporta vectors immutables i mudables d'elements no encapsulats.[96]
- hi ha altres paquets relacionats amb funcionalitats diverses
- vector-algorithms: algorismes sobre vectors mudables MVector
- vector-binary-instances: serialització
- vector-read-instances: instància de Read
- vector-instances: instàncies de diverses classes
- ...
biblioteca mono-traversable de Tipus Abstractes de Dades
Les classes de la biblio estàndard requereixen que les col·leccions estiguin parametritzades amb el tipus de l'element com a variable. Això fa que no es puguin aplicar a les col·leccions monomòrfiques on el tipus de l'element és fix (ex.: Word8 a Bytestring) perquè l'aritat del tipus de la col·lecció no concorda.
El paquet mono-traversable[97] aporta un conjunt de classes d'un sol índex, on els paràmetres de tipus dependents de les col·leccions hi son referits mitjançant tipus associats a la classe o bé famílies de tipus, aplicables tant a estructures paramètriques com monomòrfiques.
class SemiSequence t -- classe monoparàmetre, per a seqüències no buides (implementen Semigrup)
type Index t -- tipus de l'índex de seqüències com a tipus associat (indexat al paràm. de la classe)
type family Element t -- definit globalment per evitar fer diferents versions de les interfícies.
class MonoFunctor t
omap :: (Element t -> Element t) -> t -> t
-- Si Element t fos un tipus associat, caldria fer-ne versions diferents per a {seqüències, conjunts, diccionaris} i particularitzar {Functor, Foldable, Traversable, ..} per cada classe de contenidors
A més a més aporta instàncies dels TADs per a les estructures més utilitzades relacionant-hi les operacions.
- MonoPointed:[98] operacions per elevar un element a la categoria de contenidor mono amb l'element d'entrada.
- MonoFunctor:[99] Functor sobre contenidors mono.
- MonoZip:[100] operacions de combinació per posició (zips) de contenidors mono. Requereix MonoFunctor
- MonoFoldable:[101] Catamorfismes sobre contenidors mono.
- MonoTraversable:[102] Recorregut (visita) dels elements dels contenidors mono que es poden travessar seqüencialment d'esquerra a dreta, aplicant-los una funció d'efectes o avaluant-los si ja ho són.
- NonNull:[103] tipus per embolcallar estructures no buides i efectuar-hi amb seguretat operacions parcials que petarien en estructures buides.
- Interfícies per l'abstracció de seqüències
- SemiSequence:[104] Abstracció de les seqüències no-buides amb operacions basades en Semigrups. Aporta {cons, snoc, find, sortBy, reverse, intersperse}.
- IsSequence:[105] Abstracció de les seqüències buidables, afegint l'element neutre a SemiSequence formant un Monoide. Requereix SemiSequence, Monoid, MonoPointed, MonoTraversable
- Textual:[106] TAD per a seqüències de caràcters divisibles en paraules i línies. Aporta {words, unwords, lines, unlines, toUpper, toLower, ...}
- Utf8:[107] TAD multiparàmetre per codificar seqüències textuals en UTF-8, parametritzat amb el tipus de l'estructura d'intercanvi no codificada.
- Interfícies per l'abstracció de conjunts i diccionaris
- SetContainer:[108] TAD amb les operacions comunes als contenidors d'elements distingits per una clau. (tipus de la clau, aporta consultes de la clau {member, notMember, keys}, ops. de combinació {union, unions, difference, intersection})
- IsSet:[109] SetContainer quins elements són claus,
(Element t)
coincideix amb(ContainerKey t)
, amb operacions dels conjunts. Requereix SetContainer. - IsMap:[110] SetContainer travessable, amb les operacions dels diccionaris. Requereix SetContainer i MonoTraversable
Els noms de mono-traversable que defineixen funcions coincidents amb les del Prelude duen el prefix "o" (onull, olength, oelem, ...) per evitar haver de desfer ambiguïtats contínuament.
{-| prova.hs
* Implementa la func. 'trossos' d'un màxim de n elements d'una seqüència retornant-ne la seqüència de seqüències.
* Abstracció dels tipus de la seq. d'origen i de la seq de seqs de sortida.
-}
{-# LANGUAGE PackageImports, TypeFamilies #-}
import "mono-traversable" Data.Sequences as S
import "mono-traversable" Data.MonoTraversable as M
import Data.Function ((&)) -- (&): aplicació cap enrere, (paquet base)
import Control.Monad (mfilter)
-- IsSequence implica MonoFoldable a la qual pertany 'onull', i també implica Monoid
trossos :: (IsSequence t, IsSequence t', Element t' ~ t) => Index t -> t -> t'
trossos n seq = desplega (parteixIValida n) seq
-- desplega equival a 'unfoldr' a les llistes
desplega :: (IsSequence t, a ~ Element t) => (b -> Maybe (a, b)) -> b -> t
desplega f estat = case f estat of
Just (x, estat') -> S.cons x $ desplega f estat'
Nothing -> mempty -- cas final, seqüència buida (neutre de concat) (IsSequence implica Monoid)
parteixIValida :: IsSequence t => Index t -> t -> Maybe (t, t)
parteixIValida n seq =
S.splitAt n seq
& Just
& mfilter (not. M.onull. fst) -- si el trosset era buit, estem al final (retorna Nothing)
provant-ho sobre instàncies de IsSequence com ara String o bé Vector de Data.Vector. Caldrà especificar els tipus de seqüències desitjats com a restricció de tipus:
$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> :load prova
[1 of 1] Compiling Main (prova.hs, interpreted)
Ok, modules loaded: Main.
* Main> trossos 2 "abcdef" :: [String]
["ab","cd","ef"]
* Main> import Data.Vector as Vec
* Main Vec> trossos 2 (Vec.fromList [1..4]) :: Vector (Vector Int)
[[1,2],[3,4]]
altres biblioteques rellevants
- el paquet bytestring-trie[111] aporta una implementació de diccionaris com a arbres de prefix (Tries) per a claus de tipus ByteString
- el paquet dlist[112] aporta llistes per diferència que redueixen el cost de concatenació en cas d'aniuament d'expressions Vegeu ref.[113]
- el paquet heap[114] aporta cues amb prioritat per a seqüències d'ordenables ({Min|Max}Heap) o bé de parells (prioritat, valor) ({Min|Max}PrioHeap)
- el paquet semigroups,[115] a partir de GHC 8.0 queda integrat al paquet base. Aporta la classe Semigroup[116] i Data.List.NonEmpty per a llistes no buides.[117]
module Data.List.NonEmpty
...
data NonEmpty a = a :| [a] -- infixr 5
-- afegeix pel davant
(<|), cons :: a -> NonEmpty a -> NonEmpty a
...
generació
- Contenidors mono-valor d'opcionalitat o bé de dualitat resultat/error
mono-valor - generació | |||||
---|---|---|---|---|---|
paquet | tipus | context | sense valor o bé error |
amb valor | de llista o funció generadora |
base | Maybe ε [118] |
Nothing | Just x | listToMaybe llista | |
Either tipError ε [119] |
Left error | Right x |
- Seqüències d'accés lineal
seqüències - generació | ||||||
---|---|---|---|---|---|---|
paquet | tipus | context | buit | amb un elem. | de llista o altra estructura |
anamorfismes(A → A*)
|
base | [ε] [120] |
[ ] | [x] | ll. per comprensió cycle llista |
repeat x replicate n x iterate (f::ε→ε) x unfoldr (f::acc→Maybe(ε,acc)) x | |
semigroups / base (des de GHC 8.0) |
NonEmpty ε [117] |
x :| [] | nonEmpty llista :: Maybe (NonEmpty ε) cycle xs |
repeat x iterate (f::ε→ε) x unfoldr (f::acc→(ε,Maybe acc)) x unfold (f::acc→(ε,Maybe acc)) x | ||
containers | Seq ε [91] |
empty | singleton x | fromList llista | ||
bytestring | ByteString [121]-- UArray Word8 |
empty | singleton x | pack llistaDeBytes | ||
text -- ∈ H. Platform[9] |
Text [122]-- ByteString codif. UTF16 indexat com [Word16][123] |
empty | singleton ch | pack string | ||
classes | interfície | |||||
base | Monoid[124] | mempty |
Data.List.cycle dispara error si la llista era buida.
- Seqüències d'accés aleatori
vectors - generació | |||||||||
---|---|---|---|---|---|---|---|---|---|
paquet | tipus | context | buit | amb un elem. | de llista o funció generadora -- associació = (índex, valor) | ||||
array | Array ι ε [125]-- immutable amb allotj. indirecte dels elements |
(Ix ι) |
listArray (iMin, iMax) llista array (iMin, iMax) llistaD'Associacions | ||||||
UArray ι ε [126]-- immutable amb allotj. directe |
-- via interfície IArray | ||||||||
vector (tipus Índex = Int) |
Vector ε [127]-- immutable amb allotj. directe |
empty | singleton x | fromList llista replicate n x generate llarg (f :: Índex → ε) | |||||
MVector σ ε [128]-- mudable amb allotj. directe |
Vector.thaw ivector -- descongela immutable replicate n x replicateM n acció clone mvector | ||||||||
repa | multidim.Array sh ε [92] |
(Shape sh, |
singleton x:: Array DIM0 ε |
fromList shape llista | |||||
classes | interfícies | ||||||||
array | IArray α ε [129]-- immutables -- α ∈ {Array, UArray} |
-- cal esmentar el tipus desitjat listArray (iMin, iMax) llista :: α ι ε array (iMin, iMax) llistaD'Associacions :: α ι ε -- des de MArray MArray.freeze mArray | |||||||
MArray α ε m [130]-- 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 d'IArray thaw iArray |
- Altres estructures
estructures - generació | ||||||
---|---|---|---|---|---|---|
paquet | tipus | context | buit | amb un elem. | de llista o funció generadora -- associació = (clau, valor) |
de llista ordenada -- més ràpid |
containers | IntSet [82] |
empty | singleton x | fromList llista | fromAscList llista | |
IntMap ε [83] |
empty | singleton clau x | fromList llistaD'Associacions | fromAscList llistaD'Associacions | ||
containers | Set ε [80] |
(Ord ε) |
empty | singleton x | fromList llista | fromAscList llista |
Map κ ε [81] |
(Ord κ) |
empty | singleton clau x | fromList llistaD'Associacions | fromAscList llistaD'Associacions | |
unordered-containers | HashSet ε [89] |
(Hashable ε, Eq ε) |
empty | singleton x | fromList llista | |
HashMap κ ε [90] |
(Hashable κ, Eq κ) |
empty | singleton clau x | fromList llistaD'Associacions | ||
bytestring-trie | Trie ε [111] |
empty | singleton bytestringClau x | fromList llistaD'Associacions | ||
containers | Tree ε [131] |
Node x [ ] | unfoldTree f llavor | |||
heap[114] | {Min|Max}Heap ε -- munt d'elems. ordenables |
Ord ε | empty | singleton x | fromList | fromDescList fromAscList |
{Min|Max}PrioHeap prio val -- munt de parells (prioritat, valor) |
Ord prio | empty | singleton (prio,val) | -- de llista de (prio,val) fromList |
-- de llista de (prio,val) fromDescList fromAscList |
TADs de mono-traversable -- generació | ||||
---|---|---|---|---|
paquet | tipus/classe | context | amb un elem. | de llista o funció generadora |
mono-traversable[132] ε ~ Element t |
IsSequence t[105] | singleton ε | fromList | |
NonNull t[103] | fromNullable --resultat opcional | |||
MonoPointed t[98] | opoint ε | |||
IsSet t[109] | Eq ε | singletonSet ε | setFromList llista | |
IsMap t[110] | k ~ ContainerKey t, Eq k | singletonMap k v | mapFromList llistaDeParells |
consulta
mono-valor - consulta | |||||||
---|---|---|---|---|---|---|---|
(en vermell si parcialment definides, disparen error) | |||||||
tipus | context | és buit? |
mida | pertinença | obtenir elem | components | altres |
Maybe ε [118] |
isNothing | fromEnum . isJust | (==). Just | -- encaix Nothing | Just x |
maybeToList fromJust fromMaybe default |
--retornant elements catMaybes llistaDeMaybes | |
Either tipError ε [119] |
isLeft | fromEnum . isRight | (==). Right | -- encaix Left error | Right x |
--retornant elements lefts llistaDeEithers rights llistaDeEithers |
- fromJust dispara error si la Maybe era buida ; fromMaybe n'és una versió segura (amb valor per defecte per als casos sense valor).
seqüències - consulta | ||||||||
---|---|---|---|---|---|---|---|---|
(en vermell si parcialment definides, disparen error) | ||||||||
tipus | context | és buit? |
mida | pertinença | obtenir elem | components | altres | |
[ε] [120] |
null | length | elem x | find predicat:: Maybe ε -- encaix [ ] | (x : xs) |
head tail |
subsequences permutations init last | ||
NonEmpty ε [117] |
length | elem x | uncons xs :: (ε, Maybe (NonEmpty ε)) -- encaix (x :| llista) |
head tail |
init last | |||
Seq ε [91] |
null | length | (=/ Nothing). (elemIndex{L|R} x) | -- encaix de viewl seq EmptyL | (x :< xseq) -- encaix de viewr seq EmptyR | (x :> xseq) |
-- via Foldable |
|||
ByteString [121]-- UArray Word8 |
null | length | elem x | find predicat -- encaix de uncons byteStr Nothing | Just (x, xs) :: Maybe (Word8, ByteString) |
unpack:: [Word8] head tail |
init last | ||
Text [122]-- ByteString codif. UTF16 indexat com [Word16][123] |
null | length | elem ch | find predicat -- encaix de uncons text Nothing | Just (ch, txt) :: Maybe (Char, Text) |
unpack:: [Char] head tail |
init last | ||
classes | interfícies | |||||||
Foldable contenidor [133] |
null | length | elem x | find predicat | toList |
- Al mòdul Data.List head, tail, init i last disparen error si la seqüència era buida. Al mòdul Data.List.NonEmpty no, perquè les seq. són no-buides:
data NonEmpty a = a :| [a]
Els errors generats per funcions definides parcialment, com ara head en seqüències buidables, tenen una depuració complicada (la crida a error no informa de la situació de la crida que incompleix la pre-condició (excepte si es compila per l'ajustatge, ang:profiling i s'executa amb opcions addicionals per la depuració), ni normalment tampoc del valor causant (ex.: (dicc `Map.!` clau) pot donar l'error "Map.!: given key is not an element in the map" perquè la implementació de Show pels paràmetres no es requereix), només dispara una excepció genèrica ErrorCall amb el missatge).[134][135]
vectors - consulta | ||||||||
---|---|---|---|---|---|---|---|---|
tipus | context | és buit? |
mida | pertinença | obtenir elem | components | altres | |
Array ι ε [125] |
(Ix ι) |
bounds—límits de l'índex indices elems assocs—parells (índex, valor) |
||||||
Vector ε [127] |
(Eq ε) |
null | length | elem x | find predicat | toList head tail |
init last | |
MVector σ ε [128] |
null | length | Vector.freeze --cap a Vector |
|||||
multidim. repaArray sh ε [92] |
(Shape sh, |
size . extent | toList toScalar -- cas de shape DIM0 extent ::(Shape sh) |
|||||
classes | interfícies | |||||||
IArray α ε [129]-- immutables |
bounds indices elems assocs |
|||||||
MArray α ε m [130]-- mudables |
(Monad m) |
getBounds getElems getAssocs |
estructures - consulta | |||||||
---|---|---|---|---|---|---|---|
tipus | context | és buit? |
mida | pertinença | obtenir elem | components | altres |
Set ε [80]IntSet [82] |
null | size | elems | ||||
(Ord ε) |
member x | -- cerca aproximació lookup{LT,GT,LE,GE} x |
toAscList | ||||
HashSet ε [89] |
(Hashable ε, Eq ε) |
null | size | member x | toList | ||
Map κ ε [81]IntMap ε [83] |
null | size | keys keysSet elems assocs |
||||
(Ord κ) |
member clau | (! clau) lookup clau -- :: Maybe ε -- cerca aproximació lookup{LT,GT,LE,GE} clau |
toAscList | ||||
HashMap κ ε [90] |
(Hashable κ, Eq κ) |
null | size | member clau | (! clau) lookup clau -- :: Maybe ε lookupDefault default clau -- :: ε |
keys elems toList |
|
Trie ε [111] |
null | size | member bytestringClau | lookup bstrClau | keys elems toList |
submap bstrClau | |
Tree ε [131] |
rootLabel subForest -- llista en pre-ordre flatten |
-- elems per nivells levels | |||||
{Min|Max}Heap ε [114] |
Ord ε | null | size | -- encaix de view heap Nothing | Just (x, heap) :: Maybe (ε, {Min|Max}Heap ε) |
toList toDescList toAscList |
||
{Min|Max}PrioHeap prio val [114] |
Ord prio | null | size | -- encaix de view heap Nothing | Just ((pri, v), heap) :: Maybe ((prio, val), {Min|Max}PrioHeap prio val) |
-- llista de (prio, val) toList toDescList toAscList |
TADs de mono-traversable -- consulta | ||||||
---|---|---|---|---|---|---|
tipus/classe | context | és buit? |
mida | pertinença | obtenir elem | altres |
MonoFoldable t[101] | ε ~ Element t | onull | olength | oelem ε | headMay lastMay | |
SemiSequence t[104] | find predicat -- retorna :: Maybe (Element t) | |||||
NonNull t[103] | MonoFoldable t | head last | ||||
IsSequence t | tail init | |||||
IsSet t[109] | ε ~ Element t | member ε notMember ε |
||||
IsMap t[110] | k ~ ContainerKey t | member k notMember k |
lookup k -- resultat opcional findWithDefault def k |
actualització / transformació
mono-valor - actualitza/transforma | ||||||
---|---|---|---|---|---|---|
tipus | aplicació (map) als elems. |
altres | ||||
Maybe ε [118] |
-- via Functor | maybe default (f::ε→b) | ||||
Either tipError ε [119] |
-- via Functor | either (f::tipError→b) (g::ε→b) | ||||
classes | interfícies | |||||
Functor[136] | fmap (f::a → b) |
seqüències - actualitza/transforma | ||||||
---|---|---|---|---|---|---|
transforma | ||||||
tipus | context | afegeix | elimina | aplicació (map) als elems. |
altres | transforma'n una llista |
[ε] [120] |
(x :) | delete x | map (f:: ε → b) mapAccum{L|R} (f::acc→x→ (acc, y)) acumIni -- amb imatge opcional Maybe.mapMaybe (f::ε→Maybe b) |
nub—elimina duplicats reverse intersperse x |
transpose intercalate llista | |
Ord ε |
insert x | sort | ||||
Seq ε [91] |
(x <|) -- a l'esquerra (|> x)—a la dreta |
mapWithIndex (f:: Int → ε → b) | reverse | |||
ByteString [121] |
cons x -- al davant snoc x -- al darrere |
map (f:: Word8 → Word8) | reverse intersperse byte |
transpose intercalate bstr | ||
Text [122] |
cons ch snoc ch |
map (f:: Char → Char) | reverse intersperse ch -- específics replace cerca subst toUpper toLower toCaseFold justifyLeft llarg ch justifyRight llarg ch center llarg ch -- retalla espais strip stripLeft stripRight |
transpose intercalate txt | ||
classes | interfícies | |||||
Functor[136] | fmap (f::a → b) |
- Els tipus monomòrfics (ByteString, Text i també IntSet) no implementen Functor, que requereix tipus amb l'element com a paràmetre, altrament dit, kind
* -> *
(aritat del tipus == 1)
vectors - actualitza/transforma | ||||||||
---|---|---|---|---|---|---|---|---|
transforma | ||||||||
tipus | afegeix | actualitza | aplicació (map) | altres | ||||
Array ι ε [125] |
(// llistaD'Associacions) | -- via Functor | ||||||
Vector ε [127] |
cons x -- pel davant snoc x -- pel darrere |
map (f:: a → b) -- amb l'índex imap (f:: Int → a → b) |
reverse | |||||
MVector σ ε [128] |
(`set` x) clear—desvincula refs. -- copy dst src (`copy` mvectorOrigen) (`move` mvectorOrigen) |
(`grow` n) | ||||||
multidim. repaArray sh ε [92] |
map (f:: a → b) | reshape shape transpose | ||||||
classes | interfícies | |||||||
IArray α ε [129] |
(// llistaD'Associacions) | amap (f::a→b) | ||||||
MArray α ε m [130] |
mapArray (f::a → b) |
estructures - actualitza/transforma | ||||||
---|---|---|---|---|---|---|
transforma | ||||||
tipus | context | afegeix | elimina | actualitza | aplicació (map) als elems. | |
Set ε [80]IntSet [82] |
(Ord ε) | insert x | delete x | insert x -- subst. l'existent | map (f:: a → b) -- cas que f conservi l'ordre, més ràpid amb mapMonotonic (f:: a → b) | |
HashSet ε [89] |
(Hashable ε, Eq ε) | insert x | delete x | insert x -- subst. l'existent | map (f:: a → b) | |
Map κ ε [81]IntMap ε [83] |
(Ord κ) | insert clau x | delete clau | adjust (f::a→a) clau update (f::a→Maybe a) clau alter (f::Maybe a → Maybe a) clau |
map (f:: a → b) mapWithKey (f:: κ → a → b) mapAccum (f:: acc → a → (acc, b)) acumIni -- mapeig a les claus amb mapKeys (f::k1 -> k2) -- si f conserva l'ordre, més ràpid amb mapKeysMonotònic (f::k1 -> k2) -- en les col·lisions de f, combina valors mapKeysWith (en_col·lisions::a->a->a) (f::k1 -> k2) | |
HashMap κ ε [90] |
(Hashable κ, Eq κ) | insert clau x | delete clau | adjust (f::a→a) clau | map (f:: a → b) mapWithKey (f:: κ → a → b) | |
Trie ε[111] | insert bytestringClau | delete bstrClau | adjust f bstrClau | mapBy (f:: ByteString → a → Maybe b) filterMap (f:: a → Maybe b) | ||
Tree ε [131] |
-- via Functor | |||||
{Min|Max}Heap ε [114] |
(Ord ε) | insert x | ||||
{Min|Max}PrioHeap prio val [114] |
(Ord prio) | insert (pri, v) | -- via Functor |
TADs de mono-traversable -- actualitza/transforma | |||||
---|---|---|---|---|---|
tipus/classe | context | afegeix | elimina | aplicació (map) als elems. |
altres |
(ε = Element t) | |||||
SemiSequence t[104] | cons -- pel davant snoc -- pel darrere |
intersperse reverse sortBy (f::ε->ε->Ordering) | |||
IsSequence t[105] | Eq (Element t) | delete ε | |||
Ord (Element t) | sort | ||||
MonoFunctor t[99] | omap (f::ε->ε) | ||||
IsSet t[109] | (ε ~ ContainerKey t, Eq ε) | insertSet ε | deleteSet ε | ||
IsMap t[110] | (k ~ ContainerKey t, Eq k) | insertMap k v | deleteMap k | adjustMap f k updateMap f k alterMap f k |
- Els tipus (Set a) i (HashSet a) no implementen Functor (#perquè els conjunts no implementen Functor).
Nota per les funcions d'actualització als diccionaris:
- adjust (f::v→v) clau dicc : actualitza el valor, si i només si clau pertany al diccionari dicc.
- update (f::v→Maybe v) clau dicc : actualitza o bé elimina, segons existeixi (isJust) la imatge de la funció f per al valor corresponent a la clau
- alter (f::Maybe v → Maybe v) clau dicc : (actualitza o bé elimina o bé insereix) si clau no existeix, la insereix amb el valor (f Nothing); altrament com update
accés indexat
seqüències - accés indexat | ||||||
---|---|---|---|---|---|---|
en color si disparen error, en marró si és
| ||||||
tipus | context | elem. a l'índex |
cerca l'índex de l'elem. :: Maybe Int |
cerca índexs::[Int] |
elimina | actualitza |
[ε] [120] |
(!! índx) | elemIndex x -- per valor findIndex predicat -- per predicat |
elemIndices x findIndices predicat |
|||
Seq ε [91] |
(`índex` índx) -- amb result. opcional: lookup indx (`!?` indx) |
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 [121] |
(`índex` índx) | elemIndex x -- pel davant elemIndexEnd x -- pel darrere findIndex predicat |
elemIndices x findIndices predicat |
|||
Text [122] |
(`índex` índx) | findIndex predicat |
vectors - accés indexat | |||||||
---|---|---|---|---|---|---|---|
en color si disparen error, en marró si és
| |||||||
tipus | context | elem. a l'índex |
cerca l'índex de l'elem. :: Maybe Int |
cerca índexs | actualitza | ||
Array ι ε [125] |
(! índx) | ||||||
Vector ε [127] |
(! índx) (!? índx) -- :: Maybe ε |
elemIndex x findIndex predicat |
-- retornen :: Vector Int elemIndices x findIndices predicat |
||||
MVector σ ε [128] |
(`read` indx) | (`write` indx x) (`swap` i j) | |||||
multidim. repaArray sh ε [92] |
(Shape sh, |
(! índxMultiDim) (`índex` índxMultiDim) |
|||||
(`safeIndex` índxMultiDim) | |||||||
classes | interfícies | ||||||
IArray α ε [129] |
(! índx) | ||||||
MArray α ε m [130] |
(Monad m) |
(`readArray` indx) | (`writeArray` indx x) |
estructures - accés indexat (índex basat en zero sobre seq. ordenada) | |||||
---|---|---|---|---|---|
en color si disparen error, en marró si és
| |||||
tipus | context | elem. a l'índex |
cerca l'índex de l'elem. |
elimina | actualitza |
Set ε [80] |
(Ord ε) |
elemAt índxretorna :: ε |
findIndex valor -- :: Int lookupIndex valor -- :: Maybe Int |
deleteAt índx | |
Map κ ε [81] |
(Ord κ) |
elemAt índxretorna :: (κ,ε) |
findIndex clau -- :: Int lookupIndex clau -- :: Maybe Int |
deleteAt índx | updateAt f índx |
- TADs de mono-traversable[132] -- accés indexat
-- Del mòdul Data.Sequences del paquet ''mono-traversable''
-- les funcions parcials (poden disparar ''error'') duen el sufix Ex
indexEx :: IsSequence seq => seq -> Index seq -> Element seq -- funció parcial !! (dispara ''error'')
index :: IsSequence seq => seq -> Index seq -> Maybe (Element seq) -- resultat opcional
combinació
combina per op. associativa (semigrup) o per aparellament correlatiu (zips) | |||||
---|---|---|---|---|---|
tipus | context | combinació associativa |
combina'n una llista |
combina per posició (zips) zip = zipWith (,) |
desacobla llista de tuples (unzips) |
Maybe ε [118] |
-- via Monoid | -- via Monoid | |||
[ε] [120] |
(++) | concat | (`zip` llista) (`zip{3..7}` llista...) zipWith (f::a→b→c) llista1 llista2 zipWith{3..7} f llista... |
unzip unzip{3..7} | |
Seq ε [91] |
(><) | (`zip` seq) (`zip{3..4}` seq...) zipWith (f::a→b→c) seq1 seq2 zipWith{3..4} f seq... |
|||
ByteString [121] |
append | concat | (`zip` byteStr) zipWith (f::Word8→Word8→a) byteStr1 byteStr2 |
unzip | |
Text [122] |
append | concat | (`zip` txt) zipWith (f::Char→Char→Char) txt1 txt2 |
||
Set ε [80]IntSet [82] |
(Ord ε) | union | unions | ||
HashSet ε [89] |
(Hashable ε, Eq ε) | union | unions | ||
Map κ ε [81]IntMap ε [83] |
(Ord κ) | union -- amb func. combinació unionWith (f::ε -> ε -> ε) unionWithKey (f::κ -> ε -> ε -> ε) |
unions -- amb func. combinació unionsWith (f::ε -> ε -> ε) |
||
HashMap κ ε [90] |
(Hashable κ, Eq κ) | union -- amb func. combinació unionWith (f::ε -> ε -> ε) |
unions | ||
Trie ε [111] |
union{L|R} mergeBy (f::ε -> ε -> Maybe ε) |
-- via Monoid | |||
Tree ε [131] |
|||||
{Min|Max}Heap ε [114] |
(Ord ε) | union | unions | ||
{Min|Max}PrioHeap prio val [114] |
(Ord prio) | union | unions | ||
Array ι ε [125] |
|||||
Vector ε [127] |
(++) | concat | (`zip` vect) (`zip{3..6}` vect) zipWith (f::a→b→c) vect1 vect2 zipWith{3..6} f vect... -- amb l'índex a la funció izipWith (f::Int→a→b→c) vect1 vect2 izipWith{3..6} f vect... |
unzip unzip{3..6} | |
multidim. repaArray sh ε [92] |
(++) append |
||||
classes | interfícies | ||||
Monoid[124] | mappend | mconcat | |||
IArray α ε [129] |
|||||
MArray α ε m [130] |
combina com a conjunt o bé multiconjunt | ||||||||
---|---|---|---|---|---|---|---|---|
tipus | context | diferència | unió | uneix-ne una llista |
intersecció | està contingut | contingut estrictament |
obtenir distints |
[ε] [120] |
(\\) -- multi | union—vegeu ref.[137] | intersect—multi | (`isPrefixOf` llista) (`isSuffixOf` llista) (`isInfixOf` llista) |
nub | |||
Seq ε [91] |
||||||||
ByteString [121] |
(`isPrefixOf` byteStr) (`isSuffixOf` byteStr) (`isInfixOf` byteStr) |
|||||||
Text [122] |
(`isPrefixOf` txt) (`isSuffixOf` txt) (`isInfixOf` txt) |
|||||||
Set ε [80]IntSet [82] |
(Ord ε) | (\\) difference |
union | unions | intersection | isSubsetOf | isProperSubsetOf | |
HashSet ε [89] |
(Hashable ε, Eq ε) | difference | union | unions | intersection | |||
Map κ ε [81]IntMap ε [83] |
(Ord κ) | (\\) difference |
union | unions | intersection | isSubmapOf | isProperSubmapOf | |
HashMap κ ε [81] |
(Hashable κ, Eq κ) | difference | union unionWith (f::ε -> ε -> ε) |
unions | intersection intersectionWith (f::ε -> ε -> ε) |
|||
Trie ε [111] |
unionL unionR |
|||||||
{Min|Max}Heap ε [114] |
(Ord ε) | union | unions | |||||
{Min|Max}PrioHeap prio val [114] |
(Ord prio) | union | unions |
- TADs de mono-traversable[132] -- combinació
{-| Composició a ''mono-traversable'' -}
-- els zips sobre llistes es resolen amb un (''newtype'' ZipList a) que implementa un ''functor aplicatiu''.
import Data.MonoTraversable (ZipList (ZipList, getZipList))
zipWithN combinador xs1 xs2 ... xsN = getZipList $ combinador <$> ZipList xs1 <*> ... <*> ZipList xsN
-- les seqüències són Monoides
import Data.Sequences
class (Monoid seq, ...) => IsSequence seq
-- consulta d'inclusió
is{Prefix|Suffix|Infix}Of :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Bool
-- composició de conjunts i diccionaris
import Data.Containers
class (Monoid set, ...) => SetContainer set
union :: set -> set -> set
unions :: (MonoFoldable t, Element t ~ set) => t -> set -- redueix una col·lecció de ''SetContainer'' a un de sol.
-- com que (SetContainer set) requereix (Monoid set) llavors: unions = oconcat
difference :: set -> set -> set
intersection :: set -> set -> set
...
-- instàncies de contenidors ordenats (del mòdul ''containers'')
instance Ord element => SetContainer (Set.Set element) -- els conjunts d'ordenables es poden compondre
instance Ord k => SetContainer (Map.Map k v) -- els diccionaris amb clau ordenable també
-- instàncies de contenidors amb claus que implementen Hashable (funció resum) (del mòdul ''unordered-containers'')
instance (Eq element, Hashable element) => SetContainer (HashSet.HashSet element)
instance (Eq key, Hashable key) => SetContainer (HashMap.HashMap key value)
reducció
plegats | ||||
---|---|---|---|---|
(en color si parcialment definides, disparen error) | ||||
tipus | reducció | successió de plegats parcials | ||
per l'esquerra | per la dreta | per l'esquerra | per la dreta | |
[ε] [120] |
foldl (f::a→ε→a ) inicialfoldl' f ini -- estricte foldl1 ( g::ε→ε→ε ) -- sobre el primer elem. |
foldr f ini foldr1 g |
scanl f ini scanl1 g |
scanr f ini scanr1 g |
NonEmpty ε [117] |
via Foldable | via Foldable | scanl f ini scanl1 g |
scanr f ini scanr1 g |
Seq ε [91] |
via Foldable | via Foldable | scanl f ini scanl1 g |
scanr f ini scanr1 g |
classes | ||||
Foldable t [133] |
foldl f ini foldl' f ini -- estricte foldl1 g |
foldr f ini foldr' f ini -- estricte foldr1 g |
- foldl1, foldr1, scanl1, scanr1: disparen error si la col·lecció era buida.
llistes de mono-valor - plegats especials | ||
---|---|---|
tipus | llista de contenidors | |
retornant [a] | retornant ([a], [b]) | |
Maybe ε [118] |
catMaybes | |
Either tipError ε [119] |
lefts rights |
-- (lefts, rights): partitionEithers |
plegats especials | |||||||||
---|---|---|---|---|---|---|---|---|---|
tipus | sobre booleans |
s/. predicat | s/. ordre | numèrics | amb funció de projecció al tipus de l'estructura |
contenidor d'estructures |
contenidor de llistes |
índex del menor/major | |
ε ~ Bool |
(Ord ε / κ) |
(Num ε) |
(Ord ε)
| ||||||
[ε] [120] |
and or |
all predicat any predicat |
minimum maximum |
sum product |
concatMap (f::a→[b]) | concat | |||
ByteString [121] |
all predicat any predicat |
minimum maximum |
concatMap (f::Word8→ByteString) | concat | |||||
Text [122] |
all predicat any predicat |
minimum maximum |
concatMap (f::Char→Text) | concat | |||||
Vector ε [127] |
and or |
all predicat any predicat |
minimum maximum |
sum product |
concatMap (f::a → Vector b) | concat | minIndex maxIndex | ||
Set ε [80] |
-- via Foldable | -- via Foldable | findMin findMax |
-- via Foldable | -- via Foldable | -- via Monoid | |||
IntSet [82] |
findMin findMax |
-- via Monoid | |||||||
HashSet ε [89] |
-- via Foldable | -- via Foldable | -- via Foldable | -- via Foldable | -- via Monoid | ||||
Map κ ε [81]IntMap ε [83] |
-- via Foldable | -- via Foldable | -- min/max de la clau findMin findMax |
-- via Foldable | -- via Foldable | -- via Monoid | |||
HashMap κ ε [90] |
-- via Foldable | -- via Foldable | -- via Foldable | -- via Foldable | -- via Monoid | ||||
{Min|Max}PrioHeap prio val [114] |
-- via Foldable | -- via Foldable | -- via Foldable | -- via Foldable | -- via Monoid | ||||
classes | |||||||||
(Monoid m) |
|||||||||
Foldable t [133] |
and or |
all predicat any predicat |
minimum maximum |
sum product |
foldMap (f::a→m) concatMap (f::a→[b]) |
concat | |||
Monoid t [124] |
mconcat |
- minimum, maximum disparen error si la col·lecció era buida
- minIndex, maxIndex disparen error si el vector era buit
- findMin, findMax disparen error si el conjunt o diccionari eren buits
- els tipus monomòrfics (ByteString, Text, IntSet) no implementen Foldable que requereix tipus amb l'element com a paràmetre, altrament dit Kind
(* -> *)
(aritat del tipus == 1)
TADs de mono-traversable -- plegats | ||
---|---|---|
les funcions parcials (disparen error) duen el sufix Ex | ||
tipus/classes | reducció | |
ε = Element t | per l'esquerra | per la dreta |
MonoFoldable t [101] |
ofoldl' (f::a→ε→a ) ini -- estricteofoldl1Ex' ( g::ε→ε→ε ) -- sobre el primer elem. |
ofoldr f ini ofoldr1Ex g -- sobre el primer elem. |
NonNull t [103] |
ofoldl1' (g::ε→ε→ε ) -- sobre el primer elem. |
ofoldr1 g |
TADs de mono-traversable -- plegats especials | ||||||||
---|---|---|---|---|---|---|---|---|
* sufix Ex: funcions parcials, disparen error * sufix May: resultat opcional | ||||||||
tipus/classes | sobre booleans |
s/. predicat | s/. ordre | numèrics | elements monoides | amb projecció a monoide | ||
ε = Element t | ε ~ Bool |
(Ord ε) |
(Num ε) |
(Monoid ε) |
(Monoid m)
| |||
MonoFoldable t[101] | oand oor |
oall predicat oany predicat |
minimum{May|Ex} maximum{May|Ex} |
osum oproduct |
oconcat | oconcatMap (f::ε -> m) | ||
NonNull t[103] | minimum maximum |
partició
mono-valor - filtratge | ||
---|---|---|
tipus | context | filtre |
Maybe ε [118] |
via MonadPlus | |
classes | ||
MonadPlus t |
mfilter[138] |
seqüències - partició | ||||||||
---|---|---|---|---|---|---|---|---|
tipus | a l'índex | s/. predicat |
en trams d'elements consecutius equivalents |
per ocurrències d'un separador |
en trams de llargada fixa | |||
:: (t,t) |
:: (t,t) |
group = groupBy (==) | ||||||
[ε] [120] |
take n drop n |
splitAt índx | filter predicat takeWhile predicat dropWhile predicat |
partition predicat span predicat break predicat |
group groupBy (equiv::a→a→Bool) |
|||
Seq ε [91] |
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 [121] |
take n drop n |
splitAt índx | filter predicat takeWhile predicat dropWhile predicat |
partition predicat span predicat break predicat |
group groupBy (equiv::Word8→Word8→Bool) |
split separador splitWith predicatDelSeparador |
||
Text [122] |
take n drop n -- pel final takeEnd n dropEnd n |
splitAt índx | filter predicat takeWhile predicat dropWhile predicat -- pel final takeWhileEnd predicat dropWhileEnd predicat -- ambdós extrems dropAround predicat |
partition predicat span predicat break predicat |
group groupBy (equiv::Char→Char→Bool) |
splitOn textSeparador split predicatDelSeparador |
chunksOf n |
- (
span{l|r} predicat
) parteix com(takeWhile{L|R} predicat, dropWhile{L|R} predicat)
- (
break{l|r} predicat
) equival a (span{l|r} (not. predicat)
)[139]
vectors - partició | ||||||
---|---|---|---|---|---|---|
tipus | a l'índex | s/. predicat |
en funció d'un rang -- array de parells [(i, arr ! f i) | i <- [iMin..iMax] ] | |||
(Ix i, Ix j) =>
| ||||||
Array ι ε [125] |
ixmap (iMin, iMax) (f::i → j) | |||||
Vector ε [127] |
take n drop n slice índx n |
splitAt índx | filter predicat takeWhile predicat dropWhile predicat |
partition predicat span predicat break predicat |
||
MVector σ ε [128] |
take n drop n slice índx n |
splitAt índx | ||||
classes | ||||||
IArray α ε [129] |
ixmap (iMin, iMax) (f::i → j) | |||||
MArray α ε m [130] |
mapIndices (iMin, iMax) (f::i → j) |
estructures - partició | |||||
---|---|---|---|---|---|
tipus | context | a l'índex | en inferiors i superiors (estrictament) |
s/. predicat |
pel prefix |
Set ε [80]IntSet [82] |
(Ord ε) | split pivot | partition predicat filter predicat |
||
HashSet ε [89] |
(Hashable ε, Eq ε) | filter predicat | |||
Map κ ε [81]IntMap ε [83] |
(Ord κ) | split clauPivot | partition predicat filter predicat |
||
HashMap κ ε [90] |
(Hashable κ, Eq κ) | filter predicat filterWithKey (f::clau->predicat) |
|||
Trie ε [111] |
submap bytestrPrefixClau | ||||
Tree ε [131] |
|||||
{Min|Max}Heap ε [114] |
(Ord ε) | take n drop n splitAt n |
filter predicat partition predicat takeWhile predicat dropWhile predicat span predicat break predicat |
||
{Min|Max}PrioHeap prio val [114] |
(Ord prio) | take n drop n splitAt n |
filter predicat partition predicat takeWhile predicat dropWhile predicat span predicat break predicat |
TADs de mono-traversable - partició | |||||||
---|---|---|---|---|---|---|---|
tipus | context | a l'índex | s/. predicat |
en trams d'elements consecutius equivalents |
per ocurrències d'un separador | ||
índx ∈ Index t | :: (t,t) |
:: (t,t) |
group = groupBy (==) | ||||
IsSequence t[105] | take índx drop índx |
splitAt índx | filter predicat takeWhile predicat dropWhile predicat |
partition predicat span predicat break predicat |
groupBy (equiv::a→a→Bool) | splitWhen predicat -- retorna [t] | |
Eq (Element t) | group | splitElem x splitSeq xs |
implementació de classes
classes de la biblioteca GHC que les col·leccions implementen:
- control seqüencial:
- Monad: encadenament d'accions funció del resultat de la precedent. Seqüencia les accions.
- Applicative: combinació de resultats d'accions: Seqüencia només les engegades. Aplicable en paral·lelisme.
- composició no-seqüencial: Monoid
- composició seqüencial (control seqüencial amb Monoide): Alternative, MonadPlus
- mapejat no-seqüencial: Functor (Functor garanteix que l'aplicació de la composició de morfismes equival a la composició de les aplicacions dels morfismes individualment, estalviant la multiplicitat de travessaments)
- visita seqüencial (cas d'elements amb efectes o bé les imatges d'un mapeig amb una funció d'efectes): Traversable
- reducció: Foldable
- comparació de les col·leccions: Eq, Ord
- textualitzacio de les col·leccions: Show,[140] Read[141]
- travessament genèric de l'estructura de les col·leccions: Data,[142] Generic[143]
- avaluació en profunditat (a forma normal) de les col·leccions: NFData (contracció de Normal Form Data)[144]
- serialització de les col·leccions: Binary[145]
excepcions i crítiques
- els tipus monomòrfics (ByteString,[121] Text[122] i IntSet[82] no poden implementar les classes Functor i Foldable, Applicative, Monad, Alternative, MonadPlus, Traversable (quines funcions estan definides per a tipus amb un paràmetre). En intentar-ho, dona l'error: Kind mis-match (l'aritat del tipus no és l'esperada). Solució paquet mono-traversable (vegeu #alternatives)
- no implementen Functor:
- Els conjunts. #perquè els conjunts no implementen Functor!
- alternatives
- El paquet mono-traversable[146] aporta versions genèriques de classes estàndard per a contenidors, sense l'exclusió dels tipus monomòrfics, definint l'element com una família de tipus.
- Les funcions parcials tenen versions amb sufix Ex (poden disparar excepció cridant error), o bé sufix May (resultat opcional)
classe (context de la classe) |
context del mètode | mètodes |
---|---|---|
MonoFunctor t | omap (f :: Element t -> Element t) | |
MonoFoldable t | Monoid m | ofoldMap (f :: Element t -> m) |
ofoldr (op :: Element t -> acc -> acc) accIni | ||
ofoldl' (op :: acc -> Element t -> acc) accIni | ||
onull | ||
olength | ||
otoList | ||
{oall, oany} predicat | ||
head{Ex|May}, last{Ex|May} | ||
(MonoFoldable t, Num (Element t)) |
osum, oproduct | |
(MonoFoldable t, Element t ~ Bool) |
oand, oor | |
MonoFoldableMonoid t (MonoFoldable t, Monoid t) |
oconcatMap (f :: Element t -> t) | |
MonoFoldableEq t (MonoFoldable t, Eq (Element t)) |
oelem x | |
onotElem x | ||
MonoFoldableOrd t (MonoFoldable t, Ord (Element t)) |
{minimum|maximum}{Ex|May} | |
{minimum|maximum}By{Ex|May} (f :: Element t -> Element t -> Ordering) | ||
MonoTraversable t (MonoFunctor t, MonoFoldable t) |
Applicative efecte | otraverse (f :: Element t -> efecte (Element t)) |
Monad efecte | omapM (f :: Element t -> efecte (Element t)) | |
MonoPointed t | opoint x |
perquè els conjunts no implementen Functor
Perquè no mantenen la regla de la composició de morfismes dels Functors: per a tot morfisme i . Vegeu Functor#Els conjunts no són Functor
implementacions
implementació de classes d'avaluació seqüencial dels elements | |||||
---|---|---|---|---|---|
tipus | Applicative (combinador de resultats d'accions) |
Alternative (applicative amb monoide) -- requereix Applicative |
Monad (encadenament d'accions funció del resultat de la precedent) |
MonadPlus (mònada amb monoide) --requereix Monad |
Traversable[147] (visita avaluant seqüencialment els elements si són accions o bé aplicant una funció d'efectes als elements) -- requereix Functor, Foldable |
Maybe ε [118] |
Sí | Sí | Sí | Sí | Sí |
Either tipError ε [119] |
Sí | No | Sí | No | des de ghc 7.8 |
[ε] [120] |
Sí | Sí | Sí | Sí | Sí |
Seq ε [91] |
Sí | des de ghc 7.8 | Sí | Sí | Sí |
Array ι ε [125] |
No | No | No | No | No |
Vector ε [127] |
Sí | Sí | Sí | Sí | Sí |
Set ε [80] |
No | No | No | No | No |
HashSet ε [89] |
No | No | No | No | No |
Map κ ε [81]IntMap ε [83] |
No | No | No | No | Sí |
HashMap κ ε [90] |
No | No | No | No | Sí |
Trie ε [111] |
Sí | No | Sí | No | Sí |
Tree ε [131] |
Sí | No | Sí | No | Sí |
- Al GHC 7.10 està previst que Applicative esdevingui superclasse de Monad, i que Alternative ho sigui de MonadPlus.[148]
- Alternative i MonadPlus inclouen implementacions que obeeixen regles diferents. Hi ha una proposta per separar cadascuna d'elles en dues classes.[149][150]
- La regla de l'element absorbent per l'esquerra (ang:Left Catch): Maybe la compleix; la Llista no
mplus (return x) k ≡ return x -- element absorbent per l'esquerra
- La propietat distributiva per l'esquerra (ang:Left Distribution): La Llista la compleix; Maybe no
(mplus a b) >>= k ≡ mplus (a >>= k) (b >>= k) -- propietat distributiva per l'esquerra
implementació de classes | ||||||
---|---|---|---|---|---|---|
tipus | Monoid[124] (componible: mempty, mappend ) |
Functor[136] (mapejable: fmap ) |
Foldable[133] (reduïble: foldl, foldr )-- requereix Functor |
Binary[151] (serialitzable: put, get ) |
NFData[152] (avaluable a Forma Normal: rnf, deepseq ) |
Data[153] (que se'n pot recórrer l'estructura genèricament: gfoldl, gmapX )
|
Maybe ε [118] |
(Monoid ε) compon els elements |
Sí | Sí | (Binary ε) | (NFData ε) | (Data ε) |
Either tipError ε [119] |
No | Sí | des de ghc 7.8 | (Binary tipError, Binary ε) |
(NFData tipError, NFData ε) |
(Data tipError, Data ε) |
[ε] [120] |
mappend = (++) | Sí | Sí | (Binary ε) | (NFData ε) | (Data ε) |
Seq ε [91] |
mappend = (><) | Sí | Sí | (Binary ε) | (NFData ε) | (Data ε) |
ByteString [121] |
mappend = append | N/A: l'aritat del tipus no concorda (Kind mis-match) |
N/A: l'aritat del tipus no concorda |
Sí | Sí | Sí |
Text [122] |
mappend = append | N/A: l'aritat del tipus no concorda (Kind mis-match) |
N/A: l'aritat del tipus no concorda |
(-- implementable via "binary-generic") |
Sí | Sí |
Array ι ε [125] |
No | Sí | No | (Binary ι, Binary ε) |
(NFData ι, NFData ε) |
No |
Vector ε [127] |
mappend = (++) | Sí | Sí | (Binary ε) -- instàncies al paquet vector-binary-instances |
(NFData ε) | (Data ε) |
Set ε [80] |
(Ord ε) mappend = union |
No #perquè els conjunts no implementen Functor | Sí | (Binary ε) | (NFData ε) | (Data ε, Ord ε) |
HashSet ε [89] |
mappend = union |
No #perquè els conjunts no implementen Functor | Sí | (-- implementable via "binary-generic") |
(NFData ε) | (Data ε) |
IntSet [82] |
mappend = union | #perquè els conjunts no implementen Functor | N/A: l'aritat del tipus no concorda |
Sí | Sí | Sí |
Map κ ε [81]IntMap ε [83] |
(Ord κ) mappend = union cas de claus coincidents es pren el valor del primer operand |
Sí | Sí | (Binary ε) | (NFData κ, NFData ε) |
(Data κ, Data ε, Ord κ) |
HashMap κ ε [90] |
mappend = union |
Sí | Sí | (-- implementable via "binary-generic") |
(NFData κ, NFData ε) |
(Data κ, Data ε) |
Trie ε [111] |
(Monoid ε) mappend = mergeBy... cas de claus coincidents en compon els valors |
Sí | Sí | (Binary ε) | No | No |
Tree ε [131] |
No | Sí | Sí | (Binary ε) | (NFData ε) | (Data ε) |
{Min|Max}Heap ε [114] |
mappend = union |
No | No | No | No | No |
{Min|Max}PrioHeap prio val [114] |
mappend = union |
Sí | Sí | No | No | No |
classes requerides pels TAD de mono-traversable | ||||||
---|---|---|---|---|---|---|
TAD | Semigroup (semigrup: <> ) |
Monoid[124] (componible: mempty, mappend ) |
MonoFunctor[99] (mapejable: omap ) |
MonoFoldable[101] (reduïble: ofoldl, ofoldr ) |
MonoTraversable[102] (travessable: otraverse, omapM ) |
MonoPointed[98] (generable des d'un valor: opoint -- com pure de Applicative) |
SemiSequence[104] -- seqs. no-buides |
Sí | No | No | Sí | No | No |
IsSequence[105] -- seqs. buidables |
Sí | Sí | Sí | Sí | Sí | Sí |
SetContainer[108] | Sí | Sí | No | Sí | No | No |
IsSet[109] | Sí | Sí | No | Sí | No | No |
IsMap[110] | Sí | Sí | Sí | Sí | Sí | No |
Altres classes habituals dels contenidors:
- Eq t: Compara contenidors igualables comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista.
- Ord t: Compara contenidors ordenables comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista ascendent.
- Show t: Contenidor textualitzable a String independent de la codificació de caràcters (els caràcters ASCII imprimibles (0x20 a 0x7F) tal com són, els metacaràcters (com ara '\t',..) amb el prefix '\', altres caràcters en representació numèrica, inclosos els no-anglosaxons). Per una sortida humanament llegible dels caràcters no anglosaxons, cal fer servir el paquet Text.
- Read t: Omple contenidor llegint la sortida de (Show t).
implementa classes | ||||
---|---|---|---|---|
tipus | Eq[154] (Igualable) |
Ord[155] (Ordenable) |
Show[156] (Textualitzable) --només ASCII altrament codis numèrics |
Read[157] (Llegible) --només ASCII altrament codis numèrics |
--monomòrficsByteString [121]Text [122]IntSet [82] |
Sí | Sí | Sí | Sí |
--tipus d'aritat 1Maybe ε [118][ε] [120]Seq ε [91]Vector ε [127]Set ε [80]IntMap ε [83] |
(Eq ε) | (Ord ε) | (Show ε) | (Read ε) |
--tipus d'aritat 2Map κ ε [81]Either κ ε [119] |
(Eq κ, Eq ε) | (Ord κ, Ord ε) | (Show κ, Show ε) | (Read κ, Read ε) |
--tipus amb particularitats | ||||
(Ix ι) => Array ι ε [125] |
(Eq ε) | (Ord ε) | (Show ι, Show ε) | No |
Tree ε [131] |
(Eq ε) | No | (Show ε) | (Read ε) |
(Hashable ε, Eq ε) => HashSet ε [89] |
Sí | No | (Show ε) | (Read ε) |
(Hashable κ, Eq κ) => HashMap κ ε [90] |
(Eq ε) | No | (Show κ, Show ε) | (Read κ, Read ε) |
(Ord ε) => {Min|Max}Heap ε [114] |
Sí | Sí | Sí | Sí |
(Ord prio) => {Min|Max}PrioHeap prio val [114] |
Sí | Sí | Sí | Sí |
Implementació de Binary (serialitzable) amb binary-generic
Cal que el tipus implementi la classe Data[153] del mòdul Data.Data que possibilita el recorregut genèric de l'estructura del tipus. Vegeu el paquet binary-generic[158]
{-# LANGUAGE PackageImports #-}
import Data.Data (Data) -- Data possibilita el travessament genèric de l'estructura
import Data.Binary (Binary(..)) -- la classe Binary i els seus mètodes (..)
import "binary-generic" Data.Binary.Generic (getGeneric, putGeneric)
-- Text de Data.Text ja implementa Data
import Data.Text (Text)
import Data.HashSet (HashSet)
import Data.Hashable (Hashable)
instance Binary Text where
get = getGeneric
put = putGeneric -- versió tàcita (sense paràm. formals reduïbles)
instance (Data a, Hashable a, Eq a) => Binary (HashSet a) where
get = getGeneric
put = putGeneric
prova:
cabal install binary-generic
ghc -c prova.hs
Sobrecàrrega de literals
La sobrecàrrega de literals permet associar-los a una classe de tipus, en comptes d'a un tipus fix, permetent-ne l'ús en posicions d'operands d'aquells tipus que les implementin.
literals i decodificació | |||
---|---|---|---|
literal (EBNF) | classe | funció decodificadora o de conversió |
extensió de llenguatge |
9{9} -[\.E] (* dígits sense parts decimal/exp. *) |
Num (anell) |
fromInteger | |
9{9}[\.{9}] ['E'[±]9{9}] (* notació científica *) |
Fractional (cos) |
fromRational | |
9{9}[\.{9}] ['E'[±]9{9}] (* n.c. avaluable a enter *) |
Num (anell) |
fromInteger | NumDecimals[159] |
'"' caràcter {caràcter} '"' (* seqs. de caràcters*) |
IsString[160] | fromString | OverloadedStrings[161] |
'\[' valor {',' valor} '\]' (* seqs. de valors *) |
IsList[162] | fromList | OverloadedLists[163] |
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.[160] Cal especificar l'extensió de llenguatge OverloadedStrings.[161]
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString.Char8 as BS
-- "abc" convertit automàticament al tipus esperat
-- sempre que instancii IsString implementant ''fromString''
main = BS.putStrLn "abc"
implementa classes | ||
---|---|---|
tipus | IsString[160] | |
[Char] [120] |
Sí | |
ByteString [164] |
Sí | |
Text [122] |
Sí |
A partir de GHC 7.8, els literals String, si hi ha l'extensió OverloadedStrings, ja no tenen tipus String, sinó que el tipus es descriu de manera existencial com un tipus d'aquells que implementen IsString
(IsString t) => t
de manera similar als literals numèrics.
Prelude> import Data.ByteString as SBS -- Strict ByteStrings
Prelude SBS> :set -XOverloadedStrings
Prelude SBS> "abc" :: SBS.ByteString -- fromString, de la instància de IsString, fa la conversió
"abc"
Sobrecàrrega de literals Llista
- Des de GHC 7.8. OverloadedLists: Els literals llista poden passar com a literals d'altres tipus que implementin (IsList t) aportant-hi la conversió.[163]
implementa classes | ||
---|---|---|
tipus | IsList[162] | Item (tipus de l'element del literal per a la instància) |
[ε] [120] |
Sí | ε |
NonEmpty ε [117] |
Sí | ε |
Text [122] |
Sí | Char |
Seq ε [91] |
Sí | ε |
IntSet [82] |
Sí | Int |
Set ε [80] |
Sí | ε |
IntMap ε [83] |
Sí | (Int, ε) |
Map κ ε [81] |
Sí | (κ, ε) |
HashSet ε [89] |
Sí | ε |
HashMap κ ε [90] |
Sí | (κ, ε) |
- Exemple de generació d'instàncies d'IsList per a HashSet i HashMap, però sobre tipus derivats amb newtype altrament donaria error per duplicació. Vegeu instàncies òrfenes.[165]
cabal install unordered-containers # conté Data.HashSet i Data.HashMap
-- Provat amb GHC 7.10.1
{-# LANGUAGE OverloadedLists, TypeFamilies, PackageImports, GeneralizedNewtypeDeriving #-}
import GHC.Exts (IsList(..))
import "hashable" Data.Hashable
import "unordered-containers" Data.HashSet as HS
import "unordered-containers" Data.HashMap.Lazy as HM
import Control.Category ((>>>)) -- f >>> g == g. f
newtype MyHashSet a = MyHashSet { getHashSet :: HashSet a} deriving (Eq, Show, Foldable)
instance (Hashable a, Eq a) => IsList (MyHashSet a) where
-- Item col·lecció: tipus de l'element del literal llista relatiu al tipus de la instància
type Item (MyHashSet a) = a
fromList = HS.fromList >>> MyHashSet
toList = getHashSet >>> HS.toList
provaConjunt = ["Joan", "Pere"] :: MyHashSet String
---------------
newtype MyHashMap k a = MyHashMap { getHashMap :: HashMap k a} deriving (Eq, Show, Functor, Foldable, Traversable)
instance (Hashable k, Eq k) => IsList (MyHashMap k a) where
-- Item col·lecció: tipus de l'element del literal llista relatiu al tipus de la classe
type Item (MyHashMap k a) = (k, a)
fromList = HM.fromList >>> MyHashMap
toList = getHashMap >>> HM.toList
provaDicc = [("Joan",2),("Pere",3)] :: MyHashMap String Int
Preprocessadors
Compilació condicional amb codi CPP
Inclusió de codi de preprocessador de C (CPP).[166] Compilació condicional.[167]
Cal esmentar el senyal de compilació -cpp o bé la pragma d'extensió de llenguatge {-# LANGUAGE CPP #-}.
El valor de la constant __GLASGOW_HASKELL__ definida pel compilador està relacionat amb els dos primers nombres de la versió de GHC que si és x.y.z llavors __GLASGOW_HASKELL__ == xyy
, per ex. 708 per a GHC 7.8.*, 710 per a GHC 7.10.*.[168]
El gestor de projectes Cabal genera amb cabal build
el fitxer de macros dist/build/autogen/cabal_macros.h definint per cada biblioteca l'identificador de versió i una macro.
// fitxer dist/build/autogen/cabal_macros.h -- generat automàticament en compilar el paquet "biblio-4.6.0.1"
/* package biblio-4.6.0.1 */
#define VERSION_biblio "4.6.0.1"
// MIN_VERSION macro que comprova si la versió actual (4.6.0) iguala o supera la versió dels paràmetres
#define MIN_VERSION_biblio(major1,major2,minor) (\
(major1) < 4 || \
(major1) == 4 && (major2) < 6 || \
(major1) == 4 && (major2) == 6 && (minor) <= 0)
Això permet especificar compilació condicional segons la versió de biblioteca.
{-# LANGUAGE CPP, PackageImports #-}
#if defined(__GLASGOW_HASKELL__) /* cas de compilador GHC */
-- pragmes condicionats a la versió de GHC
#if __GLASGOW_HASKELL__ >= 707 && __GLASGOW_HASKELL__ < 710 /* GHC >= 7.7 && < 7.10 */
{-# OPTIONS_GHC -fno-warn-amp #-}
#endif
#elif defined(__UHC__) /* cas de compilador UHC */
...
#else
#error "no ens fem responsables d'altres compiladors"
#endif
-- codi segons la versió de la biblioteca/paquet "biblio"
#if MIN_VERSION_biblio(4,0,0)
... codi que compila si la versió actual de "biblio" iguala o supera l'esmentada
#elif MIN_VERSION_biblio(3,0,0)
... codi que compila si la versió actual de "biblio" >= 3.0.0
#else
#error "les versions de 'biblio' anteriors a la 3.0.0 no estan suportades"
#endif
- Recomanat: Mòduls de compatibilitat per als casos de trencament de l'API[169]
altres preprocessadors
El gestor de projectes Cabal obté la llista de mòduls del fitxer de projecte (proj.cabal). Aquests mòduls poden correspondre a fitxers amb extensions diverses. Segons l'extensió, Cabal hi aplica el preprocessador corresponent, generant el fitxer de codi Haskell a compilar.
- cas de document de programa, extensions {.hs, .lhs}, ja tenim el codi font.
extensió d'origen | convertidor | notes |
---|---|---|
.x, .lx | alex[170] | document d'analitzador de lèxic, de l'estil del programa en C lex |
.y, .ly | happy[171] | document d'analitzador gramatical, de l'estil del Yacc |
preprocessadors de generació de codi d'enllaç amb biblioteques d'altres llenguatges
Vegeu HaskellWiki - Foreign Function Interface
extensió d'origen | convertidor | genera | notes |
---|---|---|---|
.hsc | hsc2hs[172] | .hs, mòdul_hsc.h, mòdul_hsc.c | haskell amb incrustacions de C [173][174] |
.gc | greencard[175] | .hs, .h, .c | document haskell GreenCard[176] |
.h | c2hs[177] | .hs | generador de haskell partint de signatures en C |
Forats en expressions per consultar-ne el tipus (ang: Type Holes)
És una nova característica de GHC 7.8 que permet consultar el tipus que pertoca a una posició de paràmetre actual en una expressió, designada amb el guió baix '_', abans d'escriure-hi una expressió.[178]
$> ghci
GHCi, version 7.7.20140111:...
Prelude> :set -XTypeHoles
Prelude> :{
Prelude| data Free f a
Prelude| = Pure a
Prelude| | Free (f (Free f a))
Prelude|
Prelude| instance Functor f => Monad (Free f) where
Prelude| return a = Pure a
Prelude| Pure a >>= f = f a
Prelude| Free f >>= g = Free _
Prelude| :}
<interactive>:32:23:
Found hole ‛_’ with type: f (Free f b)
Where: ‛f’ is a rigid type variable bound by
the instance declaration at <interactive>:29:10
‛b’ is a rigid type variable bound by
the type signature for
(>>=) :: Free f a -> (a -> Free f b) -> Free f b
at <interactive>:31:10
Relevant bindings include
g :: a -> Free f b (bound at <interactive>:32:14)
f :: f (Free f a) (bound at <interactive>:32:8)
(>>=) :: Free f a -> (a -> Free f b) -> Free f b
(bound at <interactive>:31:3)
In the first argument of ‛Free’, namely ‛_’
In the expression: Free _
In an equation for ‛>>=’: (Free f) >>= g = Free _
Prelude>
Depuració
Pistes per la depuració aquí.[179]
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 en versions anteriors a GHC 8.0. A partir de GHC 8.0 el missatge afegeix la posició de la funció error excepte per al paquet base.
Per exemple, en cas de manca en la consulta en un diccionari Data.Map (dicc ! clau), dona l'error "Map.find: element not in the map". Cap pista (a GHC) de la situació de la crida a error, ni de la rutina de procedència, ni del valor causant de l'error. (no s'exigeix que les claus dels Map siguin textualitzables (instàncies de Show)).
A Haskell no hi ha la pila per a crides dels llenguatges estrictes sinó que la pila és només d'expressions pendents d'ésser avaluades per a diferents patrons.[180] Per tant tampoc tindrem el bolcat de crides per situar-se.
- A partir de GHC 7.4.1, per quan es compila amb l'opció de l'ajustatge de rendiment (ang:profiling), (
ghc -prof -fprof-auto
), s'ha habilitat una reescriptura del codi intern per la simulació d'una pila de crides estricta, per obtenir-ne una traça de crides (sense posicions de les crides) en cas d'excepció.[181][182] Cal afegir+RTS -xc
al llançament del programa.[183] - A partir de GHC 7.8.1 hi ha una versió d'error errorWithStackTrace del mòdul GHC.Stack, que per bolcar la pila de crides (amb les posicions) requereix haver compilat i relligat amb les opcions d'ajustatge (ang:profiling)(
ghc -prof -fprof-auto
). Les opcions d'execució de depuració (+RTS -xc
) no són necessàries.[184]
- GHC 8.0: errorWithStackTrace ha sigut devaluada (ang:deprecated). Ara error incorpora la funcionalitat de errorWithStackTrace. L'anterior error passa a denominar-se errorWithoutStackTrace. Les crides a error a la biblioteca base han estat reanomenades a errorWithoutStackTrace !!.
- A partir de GHC 7.10.2 hi ha la possibilitat d'explicitar les crides a traçar amb paràmetres implícits (definits a l'àmbit que fa la crida) específics
(?loc :: CallStack)
, i NO requereix l'ús del RunTimeSystem d'ajustatge, com s'explica a #Obtenció programàtica del punt de crida. Cal l'extensió ImplícitParams.
- A partir de GHC 8.0 hi ha un truc per estalviar l'ús de l'extensió de sintaxi ImplicitPararms que és la definició de la restricció HasCallStack, fixant el nom de la variable de la pila de crides i fent que la crida a error la bolqui automàticament. (vegeu exemple)
- Atenció: l'ús de CallStack/HasCallStack no funciona si no hi ha continuïtat en la propagació de l'àmbit de la variable implícita a les crides intermèdies, repetint la restricció a totes elles.
- Per exemple a GHC 8.2, Data.Map.(!) del paquet containers no esmenta HasCallStack a la crida, per tant l'ús de HasCallStack en una rutina d'usuari que la invoqui NO afegirà el punt de crida a la pila de crides del missatge d'error perquè la primera crida no s'apila a la pila de la rutina que té la crida a error.
-- A GHC 8.0 !!
module GHC.Stack where ...
type HasCallStack = (?callStack :: CallStack) :: Constraint
-------------
-- ús
import GHC.Stack (HasCallStack)
funcióParcial :: HasCallStack => a -> b
funcióParcial p
| precondició p = resultat
| otherwise = error $ "funcióParcial: la precondició falla\n" -- a GHC 8.0 el bolcat de "?callStack" és automàtic
-- a GHC >= 7.10.2 i < 8.0 cal afegir el bolcat manualment amb 'showCallStack'
- L'ús de l'ajustatge (ang:profiling) requereix relligar el programa amb la versió profiling del RunTimeSystem.[185] Per fer proves amb perfilat cal disposar d'una compilació amb -prof de totes les biblioteques que relliguem.[186]
- Per evitar l'error de manca de biblioteca de perfilat en les dependències, cal generar versions amb perfilat de les biblioteques que s'instal·lin. Per això, per al desenvolupament en aïllament (sandbox), convé afegir l'opció de perfilat per defecte (library-profiling: True) a l'arxiu de configuració de cabal específic del projecte (cabal.config) a la carpeta del projecte.
Tanmateix si sabeu per on poden anar els trets, es pot anar acorralant amb paranys (catch) i afegir traces als llocs sospitosos.
Traça de crides a l'intèrpret GHCi
Des de GHC 8.0.1 (excepte per a Windows): Vegeu ref.[187]
ghci -fexternal-interpreter -prof # intèrpret que relliga amb la biblioteca RTS d'ajustatge (ang:profiling)
Exemple de bolcat de pila de crides per excepció amb prova de crida errònia d'una clau inexistent al diccionari:
- CallStack (from HasCallStack):
- La restricció HasCallStack aquí no funciona (no afegeix els punts de crida de l'exemple en cas d'excepció) perquè l'àmbit de la variable implícita ?callStack no es propaga amb continuïtat entre la crida a error i la funció amb HasCallStack de l'exemple (hi ha funcions interposades sense la restricció HasCallStack que, per tant, no connecten la variable implícita amb l'àmbit del punt de crida, per exemple Data.IntMap.(!)).
- CallStack (from -prof):
- La compilació d'ajustatge (ang:profiling), en cas d'excepció mostra els punts de crida corresponents als mòduls compilats amb l'opció -prof.
{-| file prova3.hs -}
{-# LANGUAGE OverloadedLists #-}
import Data.IntMap
import GHC.Stack (HasCallStack)
import Control.Exception (evaluate)
mostra = [(1,"a"),(2,"b")] :: IntMap String
mapGet :: HasCallStack => Int -> IntMap a -> a
mapGet i m = m!i -- crida que dispara excepció si la clau 'i' no hi és
test1 :: IO ()
test1 = do
evaluate $ mapGet 3 mostra -- error a posta (clau inexistent) per mostrar el bolcat de la pila de crides
return ()
{- fi de fitxer -}
$ ghci -fexternal-interpreter -prof
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Prelude> :load prova3
[1 of 1] Compiling Main (prova3.hs, interpreted)
Ok, 1 module loaded.
* Main> test1
*** Exception: IntMap.!: key 3 is not an element of the map
CallStack (from HasCallStack):
error, called at libraries/containers/Data/IntMap/Internal.hs:569:17 in containers-0.5.10.2:Data.IntMap.Internal
CallStack (from -prof):
Main.mapGet (prova3.hs:10:14-16)
Main.test1 (prova3.hs:14:22-36)
Main.test1 (prova3.hs:14:11-36)
Main.test1 (prova3.hs:(13,9)-(15,19))
* Main>
aproximació amb paranys (catch)
Desavantatge: Les clàusules catch no es poden utilitzar dins el codi funcional, sinó només en codi de la mònada IO.[188] El paquet exceptions[189] aporta una implementació vàlida en altres mònades.
El catch del Prelude està devaluat (eliminat a GHC v. 7.6.1) en front del recomanat de Control.Exception.
La funció catches evita la situació produïda en una seqüència d'aplicacions de catch (per gestionar diferents tipus d'excepcions), on una excepció produïda dins un handler pot ser interceptada pel catch següent.
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Exception (catches, Handler(..), ErrorCall, SomeException, try)
default (Int, Double) -- seq. de desambiguació dels literals numèrics
cap (x:_xs) = x
cap [] = error "l'has espifiada, calça't per trobar qui m'ha cridat!!"
erroni n = cap $ drop n [1,2,3]
aproxAmbParanys n = (print $ erroni n)
`catches` [ Handler (\(err :: ErrorCall) → putStrLn $ "caçada op. no definida per al valor: " ++ show err),
Handler (\(err :: SomeException) → putStrLn $ "caçada: " ++ show err) ]
-- prova
main = aproxAmbParanys 5
Vegeu també #Punts de control al codi amb Loch-TH.
aproximació amb traces
Desavantatges:
- en el codi funcional l'ordre d'impressió de múltiples traces és aleatori perquè l'ordre d'execució de subexpressions és indeterminat.
- en el codi monàdic (seqüencial) a causa de l'avaluació tardana, s'imprimeix quan s'avalua i no pas en l'ordre especificat.
import Debug.Trace (trace, traceStack) -- traceStack (des de GHC v.7.4.1)
default (Int, Double) -- seq. de desambiguació dels literals numèrics
cap (x:_xs) = x
cap [] = error "l'has espifiada, calça't per trobar qui m'ha cridat!!"
erroni n = cap $ drop n [1,2,3]
-- aproximació amb traces:
-- trace imprimeix el missatge via unsafePerformIO
-- traceStack, a més, imprimeix una "pila de crides" artificial (no n'hi ha al Haskell)
-- sempre que es compili amb opcions de perfilat (ghc --make -prof -fprof-auto-calls prova.hs)
aproxAmbTraces n = resultat'
where
resultat = erroni n
resultat' = trace msg resultat -- o bé amb ''traceStack'' si GHC >= 7.4
msg = "el paràmetre era " ++ show n
main = aproxAmbTraces 5
-- des de GHC 7.8.1 hi ha 'errorWithStackTrace' (del mòdul GHC.Stack)
-- a GHC 8.0, la funció 'error' assumeix 'errorWithStackTrace' excepte al paquet base, reanomenada 'errorWithoutStackTrace'
$ 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ça't per trobar qui m'ha cridat!!
aproximació amb depuració a l'intèrpret ghci
Amb GHCi debugger,[190] Vegeu l'article "No more exceptions: debugging Haskell code with GHCi".[191] establint punts d'aturada (break-points)
- cal habilitar el senyal break-on-exception
- l'excepció ErrorCall, generada per la crida a la funció error, provoca l'aturada
- amb :back retrocedeix al punt d'aturada (ang:break-point) anterior
- GHCi permet magatzemar en un fitxer "./.ghci"[192] les ordres a executar en arrencar ghci, facilitant desar una llista de ordres de càrrega de mòduls i declaració de break-points per a un ús reiteratiu. Requereix permís d'escriptura exclusiu del propietari al fitxer i al directori.
- (a UNIX caldrà desactivar el permís d'escriptura de grup del fitxer i del directori:
chmod g-w ".ghci" && chmod g-w $PWD
)
default (Int, Double) -- seq. de desambiguació dels literals numèrics
cap (x:_xs) = x
cap [] = error "l'has espifiada, calça't per trobar qui m'ha cridat!!"
erroni n = cap $ drop n [1,2,3]
aproxAmbExecucióControlada n = erroni n
-- ordres GHCi:
-- :set -fbreak-on-exception
-- :break <ident-global|num-linia> -- crea un punt d'aturada a l'identificador o bé num. de línia
-- :back -- retrocedeix a break-point anterior
-- :continue -- endavant sense aturar-se
-- :step -- avança fins al proper punt d'aturada
-- :steplocal -- avança fins al proper punt d'aturada que pertanyi a la mateixa funció a nivell de declaracions
-- :stepmodule -- avança fins al proper punt d'aturada que pertanyi al mateix mòdul.
-- :show breaks -- llista punts d'aturada
-- :list -- llista el codi al voltant del punt d'aturada
--
-- consulta de variables:
-- :show bindings -- llista identificadors visibles (variables i retorn consultables)
-- :print <ident> -- consulta ident. avaluats, visibles en l'àmbit
-- :force <ident> -- cas de no estar avaluat, força'n l'avaluació i imprimeix-lo
main = aproxAmbExecucióControlada 5
ghci prova.hs Ok, modules loaded: Main. * Main> :set -fbreak-on-exception * Main> :break aproxAmbExecucióControlada Breakpoint 0 activated at prova.hs:(30,1)-(32,35) * Main> aproxAmbExecucióControlada 5 Stopped at prova.hs:(30,1)-(32,35) _result :: Int = _ [prova.hs:(30,1)-(32,35)] *Main> :continue Stopped at <exception thrown> _exception :: e = _ [<exception thrown>] *Main> :back Logged breakpoint at prova.hs:(30,1)-(32,35)
Obtenció programàtica del punt de crida
Des de la versió 7.10.2[193] existeix un mecanisme per l'obtenció programàtica del punt de crida, per a ser inclòs en missatges d'error, com es descriu a l'exemple.[194]
A l'hora d'obtenir-ne el valor, es mostren només les crides corresponents al nom de variable implícita coincident.
-- A GHC >= 7.10.2
{-# LANGUAGE ImplicitParams #-}
import GHC.Stack (CallStack, showCallStack) -- a GHC 8.0 'showCallStack' serà 'prettyCallStack'
-- GHC 8.0 estandarditza el nom ?callStack quin bolcat afegeix automàticament al msg d'error
f :: (?loc :: CallStack) => String
f = showCallStack ?loc -- retorna la pila de crides de nom "?loc": [@f, @g, @main]
g :: (?loc :: CallStack) => String -- apila les traces de crida segons el nom de la variable implícita
g = f
main = putStrLn g
Per a versions anteriors la biblioteca file-location permet incloure la posició actual en missatges d'error. Vegeu exemple a la secció següent.[195]
Traslladant la petada de les funcions parcials a la rutina que la crida amb paràmetres no vàlids
La crida a la funció error genera una excepció genèrica ErrorCall que cal evitar perquè proporciona molt poca info. de l'error.
Una solució: substituir la funció parcial per una de resultat opcional (Maybe) i avaluar-ne el resultat.
- Si n'avaluem només el patró esperat, i es dona l'inesperat, la petada de l'encaix dona informació de la situació.[196]
- Però és més correcte avaluar tots els patrons i assenyalar l'error amb la funció (err') del paquet file-location que, mitjançant una crida splice ($(x)) (extensió TemplateHaskell), avalua, en temps de compilació, la posició de la crida.
{-# LANGUAGE PackageImports, TemplateHaskell #-}
import "file-location" FileLocation (err') -- obsolet a GHC 8.0 ('error' ja incorpora la situació)
-- funció total headMay equivalent a la parcial ''head''
headMay :: [a] -> Maybe a
headMay (x : _) = Just x
headMay _ = Nothing
obtenirElCap :: [a] -> a
obtenirElCap llista =
case headMay llista of
Just cap -> cap
Nothing -> $(err') "OH NO!" -- cas de GHC < 8.0, $(err') avalua en temps de compilació capturant la posició per al msg.
-- per a GHC >= 8.0, ''error'' ja mostra la posició del punt de crida
main = print $ obtenirElCap ([] :: [Int])
Resultat:
$ ./prova
prova: main:Main prova.hs:14:21 OH NO!
La biblioteca Safe millora les funcions parcials del Prelude
La biblioteca Safe[197] ofereix diverses versions de les rutines predefinides al Prelude que poden petar.
- sufix Def per a versions amb valor per defecte per al cas no definit.
- sufix May per a versions amb resultat opcional (Maybe)
- sufix Note per a versions amb afegitó per etiquetar el missatge d'error per distingir-ne l'origen,
- sufix Safe: cas de no definició, retorna l'element zero del mateix tipus. (
tailSafe [] = []; initSafe [] = []
)
Exemple: at: funció substitutiva de la indexació de llistes (!!), definida parcialment per a valors no negatius de l'índex, menors que la llargada:
atDef :: a -> [a] -> Int -> a -- amb valor per defecte
atMay :: [a] -> Int -> Maybe a -- amb resultat opcional
atNote :: String -> [a] -> Int -> a -- amb nota diferenciadora per etiquetar el missatge d'error
Evitant les funcions parcials
El més convenient és evitar les funcions parcialment definides substituint-les per totals,
- amb resultat opcional, analitzant el resultat per patrons
- proporcionant un valor per defecte per al cas no definit
- definint un tipus específic per al subdomini vàlid, amb generadors que en validin els valors.
Opcions de compilació per evitar fallades
Vegeu ref.[198]
- -fwarn-incomplete-patterns : avisa quan l'encaix pot fallar
- -fwarn-incomplete-uni-patterns : avisa quan l'encaix en una lambda és incomplet
- -fwarn-incomplete-record-updates: avisa quan les actualitzacions de registres poden fallar
Un Prelude alternatiu sense funcions parcials
El paquet classy-prelude[199] ofereix diversos avantatges, i es pot utilitzar amagant el Prelude oficial amb la pragma {-# LANGUAGE NoImplicitPrelude #-}
Incorpora, a més a més, la biblio. mono-traversable[200] que proposa un tractament unificat de col·leccions, tant monomòrfiques com paramètriques, basat en famílies de tipus.
Evitant les petades amb Verificació Formal estàtica
Afegint anotacions de "tipus refinats amb predicats".
Vegeu #Verificació Formal estàtica amb la biblio LiquidHaskell
Punts de control al codi amb Loch-TH
El paquet loch-th[201] aporta una solució a les assercions fallides, generant automàticament en els punts de control $check <expr>, amb template haskell, gestors d'excepcions sobre l'expressió que prefixen la situació (fitxer : línia : columnes) del $check al missatge d'error. (el prefix '$' avalua en temps de compilació la funció o expressió (entre parèntesis) prefixades)
module Erroni where
import Control.Exception (assert)
erroni :: String
erroni = assert False "abc"
-- fitxer prova.hs
{-# LANGUAGE PackageImports, TemplateHaskell #-}
import "loch-th" Debug.Trace.LocationTH (check)
import Erroni
msg = $check erroni
main = print ($check msg)
la seva execució, en cas d'excepció, mostra les posicions dels $check travessats:
ghc --make prova.hs Erroni.hs -o prova ./prova prova: prova.hs:8:14-19: prova.hs:6:6-11: Erroni.hs:6:9-14: Assertion failed
Loch-TH i els constructors
$check avalua amb evaluate que ho fa a WHNF per tant les expressions dels components dels contructors no són avaluades. Caldrà explicitar l'avaluació estricta per forçar-ne l'avaluació per aflorar les excepcions. Vegeu explicació a Haskell / estrictesa
force del paquet "deepseq"[202] avalua estrictament també els components (avaluació a Forma Normal), si el tipus implementa la classe NFData del mateix paquet. El paquet deepseq-th aporta la derivació genèrica d'instàncies NFData per a tipus de dades algebraics.
{-# LANGUAGE PackageImports, ScopedTypeVariables, TemplateHaskell, BangPatterns #-}
{-# LANGUAGE UnicodeSyntax #-} -- per les fletxes Unicode
import Prelude hiding (catch) -- El catch del Prelude no detecta excepcions en el codi funcional
import Control.Exception (catch, SomeException)
import Control.Monad
import "loch-th" Debug.Trace.LocationTH (check)
import "deepseq" Control.DeepSeq (force)
erroni1 = $check (head []) : "abc" -- sí que prefixa posició
erroni2 = $check (head [] : "abc") -- el constructor (:) atura l'avaluació de (head []) que no és avaluada (vegeu WHNF)
erroni3 = $check (head [] `consEstricteALElem` "abc") -- amb ! a l'elem a WHNF, sí que prefixa la posició
erroni4 = $check (force (head [] : "abc")) -- amb 'force' de deepseq a Forma Normal, sí que prefixa la posició
consEstricteALElem !x xs = x:xs -- ! al paràmetre, l'avalua a ''WHNF'' (vegeu ext. BangPatterns)
prova :: String -> String -> IO ()
prova títol cas = do
putStr $ "cas " ++ títol ++ ": "
putStrLn cas `catch` (\(excep :: SomeException) → print excep)
main = do
forM_ [("erroni1", erroni1),("erroni2", erroni2),
("erroni3", erroni3),("erroni4", erroni4)]
$ \(títol, cas) -> prova títol cas
dona, mostrant si s'avalua o no el codi erroni, assenyalant-ne la posició:
$ runhaskell prova cas erroni1: prova.hs:11:11-16: Prelude.head: empty list cas erroni2: Prelude.head: empty list cas erroni3: prova.hs:13:11-16: Prelude.head: empty list cas erroni4: prova.hs:14:11-16: Prelude.head: empty list
Traçabilitat
Vegeu ref.[203]
En el codi funcional
L'ordre d'impressió de diverses traces en una expressió serà aleatori!!, perquè l'ordre d'execució de subexpressions en el codi funcional és indeterminat.
- trace: mostra un missatge mitjançant unsafePerformIO
- traceShow: mostra el valor d'una variable textualitzable
- traceStack (des de GHC 7.4): Cal compilar amb la biblio. de perfilat (ang:profiling) com trace afegint la pila de crides simulada de l'estudi de costos.
En context seqüencial
- traceIO: mostra el missatge en el context de la mònada IO, retornant unit.
- traceM: mostra el missatge en un context seqüencial (monàdic o bé aplicatiu), retornant unit.
- traceShowM: mostra el valor d'una variable textualitzable en context seqüencial (monàdic o bé aplicatiu), retornant unit.
EventLog Tracing
És una traça per al registre d'esdeveniments, per a ser llegides per eines per l'ajustatge del rendiment (perfilat, ang:profiling).[204]
- Cal compilar amb l'opció
-eventLog
.[205] - per a mostrar la traça textualment, cal compilar a més a més amb
-debug
i executar amb+RTS -v[flags]
.[206] Mostra les traces pel dispositiu de sortida stdout. - per a un ús de la traça per eines de perfilat, s'envien les traces al fitxer nom_programa.eventlog, amb codificació binària (més compacte), executant amb
+RTS -l[flags]
.[206]
Exemple:
import Control.Monad
import Debug.Trace
main = forM_ [1..6] $ \cnt -> do
traceEventIO $ "cnt: " ++ show cnt
$ ghc --make -eventlog -debug -rtsopts prova.hs
Linking prova...
$./prova +RTS -vu
...
Més problemàtiques
El problema de les configuracions
Se n'hauria de dir millor "el problema de les preferències canviants" o "de l'entorn global modificable".
L'accés, des del codi funcional, a variables globals carregades en temps d'execució es pot abordar des de diverses perspectives, cap d'elles plenament satisfactòria.[207]
- Amb la mònada Reader[208] (proporciona accés a variables d'entorn d'un entorn canviant). Desavantage: no-accessible des del codi funcional, caldrà, llavors, passar-lo com a paràmetre.
- Intent de solució mitjançant paràmetres implícits: Desavantatge: cal propagar el paràmetre implícit a totes les declaracions de tipus de les rutines intermèdies.
- Accedint via unsafePerformIO, assegurant-se bé que l'escriptura de les variables ha estat avaluada ("the unsafePerformIO hack").[209] Desavantatge: hi ha el risc que les ops. no s'efectuïn en l'ordre correcte si no se'n controla bé l'avaluació.
- Altres sistemes més sofisticats:
Una solució: El hack unsafePerformIO
unsafePerformIO[213] És una manera poc ortodoxa de definir variables globals mudables, com les ref del ML, modificar-les i accedir-hi des de codi funcional pur, sense necessitat de passar-les com a paràmetre
La variabilitat de les definicions globals trenca el principi de transparència referencial (constància del resultat d'una funció amb les mateixes entrades) de les funcions que hi accedeixen.
El resultat dependrà de l'ordre de les operacions mutadores i del moment que se n'avaluï el thunk corresponent. (Vegeu "IO inside: The dark side of the IO Monad".)[214]
hack, en informàtica, segons la viqui. anglesa vol dir "solució poc elegant però que permet sortir del pas",[215] aquí en diríem "un pedaç".
unsafePerformIO[213] és la porta del darrere de la mònada IO.[214]
unsafePerformIO :: IO a -> a
Per emprar amb seguretat unsafePerformIO en el codi funcional, cal que l'acció no-funcional mantingui la idempotència (no tingui efectes col·laterals i sigui independent de l'entorn).[213]
Aquesta solució és incompatible amb la certificació Safe Haskell.
El sistema més correcte és tractar les preferències com un entorn canviant, fent servir la mònada Reader o bé el transformador de mònades corresponent ReaderT com s'explica tot seguit.[216]
Tractament d'un entorn canviant, amb la mònada Reader
És la millor solució al problema de les preferències (variables globals) que tracta com un entorn variable mitjançant l'aplicació d'una funció (entorn -> entorn)
newtype Reader env a = Reader { runReader :: (env -> a) } -- el valor de la mònada Reader és una funció sobre l'entorn ('env' abbrev. de environment)
class MonadReader env m | m -> env where
ask :: m env -- obté l'entorn
local :: (env -> env) -> m a -> m a -- avalua localment una acció amb l'entorn modificat
asks :: MonadReader env m => (env -> a) -> m a -- obté el resultat d'una projecció sobre l'entorn
Exemple d'ús del transformador de mònades equivalent de la mònada Reader (ReaderT) en un procés interactiu:
{-# LANGUAGE PackageImports #-}
import "mtl" Control.Monad.Reader (ReaderT(runReaderT), MonadReader(ask, local))
import "mtl" Control.Monad.Trans (liftIO)
import Control.Monad (when)
import System.IO (hFlush, stdout)
import "safe" Safe (readMay)
type Env = Int -- entorn simplificat per a l'exemple
entorn_inicial = 0
bucle :: ReaderT Env IO ()
bucle = do
env <- ask -- obté l'entorn en aquest punt
str <- liftIO $ do -- cal elevar amb ''liftIO'' el tipus de les oper. de la mònada IO a la transformada
putStrLn $ "l'entorn és: " ++ show env
putStrLn "entreu valor Int a afegir (cas de <= 0, acaba): "
hFlush stdout
getLine
case (readMay str :: Maybe Int) of
Nothing -> do
liftIO $ putStrLn "entrada incorrecta!!"
bucle
Just v -> when (v > 0) $ local (v+) bucle -- si v > 0, repeteix amb l'entorn modificat per (v+)
-- altrament, acaba
main :: IO ()
main = runReaderT bucle entorn_inicial
El problema del sobreiximent de la pila
L'ús del "plegat per l'esquerra estricte" foldl' no garanteix que es treballi a espai constant de la pila.
En cas de funcions amb recursivitat final,
- les expressions (o variables definides localment) en la crida recursiva generen thunks pendents d'avaluar, que s'apilen mentre no s'arriba al cas no recursiu.
- l'avaluació estricta (amb '!' BangPatterns (a WHNF)) en els paràmetres acumuladors tampoc ho garanteix, si l'avaluació es topa amb un constructor perquè per disseny els constructors aturen l'avaluació.
Vegeu "Stack overflow" al HaskellWiki[180] i la secció Haskell#Avaluació estricta explícita (treballar a espai constant de la pila). També "Retainer profiling" per investigar "Perquè l'avaluació d'un objecte és retinguda"[217]
La solució més fàcil és que el tipus de l'acumulador algebraic estigui definit estricte (prefix '!') en tots els seus components.
Altrament el paquet deepseq[218] aporta funcions d'avaluació en profunditat a Forma Normal. El paquet deepseq-th[219] facilita la derivació d'instàncies de la classe NFData (contracció de NormalFormData), només per als tipus algebraics. Altrament caldrà instanciar-la manualment, definint la funció rnf (acrònim de reduce to normal form) de manera que per cada constructor n'avaluï tots els components.
-- ''deepseq'': versió de ''seq'' que avalua també els components
-- dels tipus de dades, si i només si, aquests instancien NFData,
-- derivable, si el tipus és algebraic, amb el paquet deepseq-th.
deepseq :: NFData a => a -> b -> b
El problema de les fugues de memòria
Vegeu enllaços "Memory leaks","[220] Space leak zoo"[221]
Eines per l'ajustatge: "GHC: Perfilant (ajustant) l'ús de memòria".[222]
El maleït problema de les dependències de biblioteques (ang: cabal dependency hell)
El relligat requereix que hi hagi una sola versió de cadascuna de les biblioteques relligades.
El desenvolupament en aïllament (sandbox) es fa per evitar dependre de biblioteques instal·lades en l'entorn d'usuari, subjectes a alteracions per la instal·lació d'aplicacions i també per evitar que estiguin relligades amb biblioteques instal·lades relligades al seu torn amb versions diferents d'una mateixa biblioteca.
cabal sandbox init
inicialitza un dipòsit de biblioteques exclusiu del projecte,[223] que impedeix tenir múltiples versions instal·lades d'una mateixa biblio.
Tanmateix la incorporació a posteriori de biblioteques en un projecte, pot portar a la situació que les dependències de la nova siguin incompatibles amb el conjunt de biblioteques instal·lades, i calgui eliminar el dipòsit i reintentar-ho de cap i de nou, havent de reinstal·lar-ho tot, amb la consegüent aturada d'hores que suposa la recompilació d'un programa complex per la quantitat de biblioteques com ara els servidors web yesod.
Stackage - rebost alternatiu amb dependències verificades
Stackage[224] vol ser una solució al problema de les dependències (biblioteques de les quals es depèn), oferint les biblioteques en conjunts de versions compatibles entre elles per una determinada versió de GHC garantint la correctesa de la compilació. L'evolució per modificació de les biblioteques resultarà en un seguit d'imatges instantànies (ang: snapshots) del conjunt de biblioteques, promovent que tots els autors s'actualitzin quan es produeix una modificació en una biblio. que trenqui la compatibilitat, quedant fora del conjunt les que no s'actualitzin.[225]
Les imatges etiquetades "Nightly" (compilació nocturna o en desenvolupament) tenen versions més recents de biblioteques, però poden ometre aquelles endarrerides respecte al suport de dependències que han modificat l'API i les que en depenguin, mentre que les etiquetades LTS Haskell (Long Term Support) són més completes però no tant recents.[226][227][228]
Cal especificar el rebost al fitxer cabal.config de la carpeta del projecte amb la propietat remote-repo. I després fer cabal update
des del mateix directori.
Exemple per a l'edició lts-6.27:
# si utilitzem el gestor cabal, al fitxer cabal.config del projecte remote-repo: stackage-lts-6.27:http://www.stackage.org/lts-6.27 # o bé si utilitzem el meta-gestor "stack" stack --resolver=lts-6.27
Stackage, contracció de "Stable, Vetted Hackage" (cat: rebost Hackage estable i verificat), és una iniciativa[229] dels creadors de l'entorn de desenvolupament web Yesod, auspiciada per FPComplete.[230]
- També s'ofereix una eina stack, que treballa amb el rebost Stackage i millora el desenvolupament en aïllament de cabal sandbox.[231] Vegeu Haskell#Stack - Meta-gestor de projectes
Programació de nivell baix
Aspectes d'optimització
- "Més aviat: com fer que un programa compili més de pressa".[232]
- "Més ràpid: com fer que un programa corri més".[233]
- "Més petit: com fer que un programa ocupi menys".[234]
- "Més auster: com fer que un programa engoleixi menys memòria dinàmica".[235]
Optimitzacions d'allotjament
Tipus primitius que es passen per valor (no encapsulats, ang:unboxed)
Boxing és el procés de convertir una dada que es passa per valor en un objecte que es passa per referència. Unboxing és el procés invers.
Els valors "no encapsulats" eviten l'allotjament en memòria dinàmica, però en ésser valors nus no disposen d'informació de tipus i les funcions polimòrfiques no s'hi podrán aplicar.[236]
Els operadors, tipus i literals de dades "unboxed" hauran de dur un indicador '#' al darrere, extensió de sintaxi que requereix la pragma {-# LANGUAGE MagicHash #-}:
- tipus: Int#, Float#, Double#
- operadors: (+#), (-#), (*#), (==#)
- literals: 4# 3.2# 'c'#
- De unboxed a boxed
- el tipus de 4# és Int# ;
- Amb el constructor I# de GHC.Exts, el tipus de
(I# 4#)
és Int
- De boxed a unboxed per encaix
let !(I# unboxed_int) = 5::Int
Vegeu[237] i el farragós exemple següent:
{-# LANGUAGE MagicHash, BangPatterns #-}
import GHC.Exts (Int#, Int(I#)) -- importa el tipus Int# i el constructor I# del tipus Int
import GHC.Prim ((*#),(-#), (==#), (>#))
fact_rf :: Int# → Int# → Int#
fact_rf !acum !n -- avaluació estricta dels paràmetres
| n ==# 0# = acum
| n ># 0# = fact_rf (acum *# n) (n -# 1#)
imprimeix :: Int# → IO ()
imprimeix unboxed_int = do
putStrLn $ "resultat " ++ show (I# unboxed_int) -- de unboxed a boxed aplicant el constructor
main = do
let !(I# unboxed_int) = 5::Int -- de boxed a unboxed per encaix (requereix estrictesa(!))
result = fact_rf 1# unboxed_int
imprimeix result
Desencapsulament en un tipus - La pragma UNPACK
Desempaqueta un component d'un constructor de dades en el mateix constructor evitant un nivell d'indirecció.[238]
data T = T {-# UNPACK #-} !Float
{-# UNPACK #-} !Float
La pragma {-# OPTIONS_GHC -funbox-strict-fields #-} ho fa automàtic.
Des de ghc 7.8, la optimització UNPACK ve per defecte, per als components estrictes de mida "petita" (igual o inferior a la de la paraula de la màquina, i també per als de coma flotant).[239]
Tuples no encapsulades per al retorn de múltiples valors
Les tuples unboxed[240]
(# a, b #)
són un capítol a part i s'utilitzen per retornar més d'un valor alhora, sense allotjar la tupla a la memòria dinàmica. Cal especificar l'extensió de lleng. UnboxedTuples i l'opció -fobject-code (generació de codi nadiu, perquè en codi intermedi (bytecode) no compila).
-- fitxer tuples-no-encapsulades.hs
{-# OPTIONS_GHC -fobject-code #-} -- força la generació de codi nadiu, descartant la sortida a bytecode
{-# LANGUAGE UnboxedTuples #-}
f x y = (# x+1, y-1 #) -- retorn amb tupla ''no encapsulada''
g x = case f x x of
(# a, b #) → a + b -- obrim la tupla per encaix del patró ''(#... #)''
main = do
let y = g 5
putStrLn $ "y = " ++ show y
Compilació i exec.
ghc —make tuples-no-encapsulades.hs
./tuples-no-encapsulades
Especialització d'operacions sobrecarregades
L'especialització es fa servir per millorar el rendiment en les operacions sobrecarregades, quan es fan servir molt amb un tipus concret. Cal especificar la pragma SPECIALIZE.[241]
{-# LANGUAGE BangPatterns #-}
factorial :: (Num a, Ord a) => a → a
factorial 0 = 1
factorial n
| n > 0 = factorial_rf 1 n
| otherwise = error "n no pot ser negatiu"
where
factorial_rf !acum !n -- aval. estricta als paràmetres formals
| n == 1 = acum
| n > 1 = factorial_rf (acum * n) (n-1)
{-# SPECIALIZE factorial :: Int → Int #-}
{-# SPECIALIZE factorial :: Integer → Integer #-}
-- en detectar el compilador el tipus esmentat a la pragma, genera versions no sobrecarregades, més eficients, de la funció.
Safe Haskell
Vegeu ref.[242][243] És una extensió de llenguatge per restringir l'ús de diverses construccions, com ara unsafePerformIO[213] que permet trencar la transparència referencial de les expressions funcionals pures (constància del resultat en diferents invocacions amb les mateixes entrades).
Pretén assegurar les propietats següents:
- seguretat dels tipus
- transparència referencial
- encapsulament estricte de mòduls
- raonament modular
- consistència semàntica.
Cal esmentar la pragma {-# LANGUAGE Safe #-}
.
GHC a la màquina virtual Java
- eta-lang.org és una modificació de GHC per generar codi JVM dels executables i biblioteques en fitxers ".jar" amb interoperabilitat amb classes Java, permetent l'ús de biblioteques haskell actuals en entorn Java, amb suport per la majoria d'extensions de GHC.
- eta: és el nom que pren el GHC modificat, admetent sintaxi FFI (Foreign Function Interface) ampliada d'enllaç amb Java per a l'importació / exportació,
(<:)
per al requeriment d'implementació d'interfícies Java, ...
- etlas: és una adaptació del programa cabal.
- Frege[244] és una aproximació a Haskell sobre la JVM incompatible amb l'estàndard del llenguatge. (ex. a les def. de classes el nom de la classe precedeix el requeriment de context, al revés del Haskell, la precedència dels operadors atorga nivells de 0 a 16 quan al Haskell és de 0 a 9, ...)[245]
JavaScript té el desavantatge de tenir un codi molt fràgil, però és una eina que s'hi pot comptar a tots els navegadors web.[246]
Haste[247][248][249][250] desenvolupat a la Universitat Chalmers de Göteborg i també GhcJs[251][252] són modificacions de GHC amb generació de codi JavaScript partint de la sortida STG del compilador.[253]
Permeten utilitzar primitives específiques juntament amb pràcticament qualsevol codi Haskell compilable amb GHC, i generar-ne codi JavaScript.
Exemple amb Haste, instal·lació (cal versió cabal-1.18+ per la compilació aïllada):
mkdir haste && cd haste # crea carpeta per la compilació aïllada
cabal sandbox init # inicialitza un dipòsit de biblioteques exclusiu del projecte
cabal update
cabal install haste-compiler
#
export PATH=$PWD/.cabal-sandbox/bin:$PATH # afegeix al PATH la carpeta d'executables generats
haste-boot # inicialitza i compila a Javascript les biblioteques bàsiques de GHC.
Programa d'exemple[247] que mostra en un element html amb id="id_sortida", el contingut de la casella de text (elem. input type="text") amb id="id_entrada_de_text"
import Haste
main = do
Just inp <- elemById "id_entrada_de_text"
Just outp <- elemById "id_sortida"
onEvent inp OnKeyUp $ \_ -> do
text <- getProp inp "value"
setProp outp "innerHTML" text
# compilació, genera "prova.js"
hastec prova.hs
# caldrà incloure (<script src="prova.js" type="text/javascript"></script>)
# al codi de capçalera de la pàg. html
- Vegeu també PureScript
Eines
- hlint
- Aquí[254] analitzador de codi amb suggeriments de millora. Avisa de construccions redundants i proposa alternatives al codi.
- cabalg
- descarrega i instal·la des del GitHub. Obsolet (actualment les instal·lacions són via Stack)
cabalg https://github.com/usuari-gh/projecte-gh # descarrega ('git clone') a la carpeta 'projecte-gh' i instal·la
- 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.[255]
- Obsolet. La funcionalitat de cabal-dev ha estat incorporada al programa Cabal amb el verb sandbox i cabal-dev ja no s'actualitzarà.
cabal sandbox init
inicialitza el dipòsit de biblioteques del projecte en el subdirectori.cabal-sandbox
deixant els executables a.cabal-sandbox/bin
.
- Obsolet. La funcionalitat de cabal-dev ha estat incorporada al programa Cabal amb el verb sandbox i cabal-dev ja no s'actualitzarà.
- cabal-meta (cabal multi-projecte)
- Permet especificar una llista de subprojectes locals o bé remots (git/hackage) per a un muntatge encadenat dels projectes dependents, en cas de modif. en un subprojecte. Obté la llista de subprojectes d'un fitxer sources.txt que pot referenciar altres directoris que també en continguin, per un tractament recursiu.[256]
- .- instal·la (amb cabal-dev cas de --dev), en una sola ordre diversos paquets (del hackage o bé subprojectes de directoris locals o remots (git))
- .- als fitxers sources.txt cada línia pot contenir:
- cas de començar per '.' o bé '/': directori (seguit d'opcions) que pot contenir un altre fitxer sources.txt i/o un projecte cabal. (les opcions són flags per al cabal)
- cas de prefix "git:", "https", "http": gitLocation gitBranca? opció*, és a dir, projecte recuperable amb git (sistema de control de versions distribuït)(les opcions són flags per al cabal)
- altrament: paquet-del-hackage opció*
- cabal-ghci
- engega ghci preparant-lo per a proves del projecte, amb camins i extensions de llenguatge obtinguts del fitxer de projecte.cabal[257]
- Obsolet !, Aquesta funcionalitat ha estat incorporada a cabal amb el verb repl (acrònim de read-eval-print loop) (
cabal repl
)
- yackage
- servidor de rebost "hackage" local per a desenvolupament.[258]
- 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).[259] Estudi "Ajustatge fi del paral·lelisme amb ThreadScope".[260]
- 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.[261]
- 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.[262]
- hp2any-manager mostra gràfics de fitxers de perfil (.hp) corresp. a execucions ja finalitzades.
- més eines de desenvolupament al HaskellWiki
Vegeu ref.[263]
Verificació Formal estàtica amb la biblio LiquidHaskell
"LiQuiD Types" és un nom adaptat de la contracció de "Logically Qualified Data Types".[264]
La biblioteca LiquidHaskell[265][266] permet la verificació mitjançant anotacions de "tipus refinats amb predicats"[267][268] (actualment només de tipus enters) que cal acotar entre {-@ @-}
com ara:
{-| comprova si es pot deduir de les crides,
que l'exponent no serà negatiu, estalviant tractar l'excepció que (^) llançaria.
(^) és una funció parcial, definida per a valors de l'exponent enters no negatius
-}
{-@ -- anotacions per al programa 'liquid'
type Nat = {v:Int | v >= 0}
elevaAPotènciaNatural :: Num a => a -> Nat -> a
@-}
elevaAPotènciaNatural :: Num a => a -> Int -> a
elevaAPotènciaNatural base exponent = base ^ exponent
i aporta un programa de verificació formal, anomenat liquid, que utilitza un solucionador de lògica de predicats SMT (Satisfiability Modulo Theory)[269] per comprovar que el codi compleix els predicats. A més incorpora comprovació de totalitat (contrari de parcialitat en funcions per garantir l'acabament dels programes).
Vegeu sintaxi, i l'apartat sobre mètrica dels tipus de dades: "2.6 Measures: From Integers to Data Types".[268] Explicació més planera aquí.[270] Vegeu bloc.[271]
És convenient separar cada instrucció LH en un bloc {-@ @-}
a part, per entendre millor els errors de l'analitzador sintàctic del liquid.
Líquid permet l'ús en predicats de mètriques dels tipus de dades. Per fer servir una funció com a mètrica cal especificar-ho al LH amb el qualificador measure.
Darrerament també és possible especificar mètriques d'acotació de funcions recursives per garantir-ne l'acabament.
{-| per utilitzar una funció mètrica de tipus de dades, a les condicions dels predicats,
cal declarar-la amb el qualificador 'measure' -}
{-@ type Nat = {v:Int | v >= 0} @-}
-- | mètrica de llistes
llarg :: [a] -> Int
llarg [] = 0
llarg (x:xs) = 1 + llarg xs
{-@ measure llarg :: [a] -> Nat @-} -- declara 'llarg' com a mètrica per al seu ús en predicats de refinament
{-@ type LlistaNoNulaDeLlargadaSuperiorA MaxIdx a = {v:[a] | llarg v > 0 && llarg v > MaxIdx } @-}
-- | indexació verificant que la llargada serà superior a l'índex
indexa :: Int -> [a] -> a
{-@
indexa :: i:Nat -> LlistaNoNulaDeLlargadaSuperiorA i a -> a
@-}
indexa i (x:xs)
| i == 0 = x
| i > 0 = indexa (i-1) xs
| otherwise = error "índex negatiu -- opció que no es donarà, si la verif. passa bé (i:Nat)"
indexa i [] = error "índex sobrepassa llargada -- opció que no es donarà si la verif. formal passa bé"
llista :: [Int]
{-@ llista :: LlistaNoNulaDeLlargadaSuperiorA 3 Int @-} -- MaxIdx == 3
llista = [1,2,3,4]
main = print $ indexa 2 llista
Comprovació amb el solucionador de lògica de predicats SMT MathSAT:.[272]
$ liquid --smtsolver=mathsat Main.hs
**** DONE: fixpoint ***********************************************************
**** DONE: solve **************************************************************
**** DONE: annotate ***********************************************************
**** SAFE **********************************************************************
Exemples
Degut a un robot viquipèdic que converteix les fletxes del codi ->
en →, per poder compilar els exemples cal afegir l'extensió de llenguatge UnicodeSyntax, com ara {-# LANGUAGE UnicodeSyntax #-}
.
Arrencada amb opcions
Exemple amb definició i anàlisi d'opcions d'arrencada per al nostre programa.
Per afegir opcions a l'intèrpret d'ordres vegeu l'enllaç.[273][274][275]
-- per una lectura correcta dels caràcters no anglosaxons a l'intèrpret de comandes a Linux (''shell'')
-- cal esmentar "System.Environment.UTF8" del paquet utf8-string
--, en lloc de "System.Environment"
import System.Environment (getProgName, getArgs) -- a Linux System.Environment.UTF8
import System.Exit (exitSuccess, exitWith, ExitCode(..))
import System.Console.GetOpt
import Data.Maybe (fromMaybe)
import Text.Printf (printf)
-- un tipus per a les nostres opcions d'arrencada (en ang.: ''flags'')
data Flag = FVersio | FEntrada String | FSortida String | FArgLliure String {- els params. -}
deriving (Eq, Show)
-- taula d'opcions amb nom curt, nom llarg, descriptor, missatge d'ajuda.
-- cadascuna pot venir amb paràmetres obligatoris (ReqArg), opcionals (OptArg), o sense (NoArg)
opcions :: [OptDescr Flag]
opcions = [
Option ['V'] ["versió"] (NoArg FVersio) "mostra versió",
Option ['i'] ["entrada"] (ReqArg FEntrada "FILE") "entrada requereix un nom de fitxer",
Option ['o'] ["sortida"] (OptArg creaFlagSortidaAmbValorPerOmissió "FILE") "sortida"
]
creaFlagSortidaAmbValorPerOmissió :: Maybe String -> Flag
creaFlagSortidaAmbValorPerOmissió ms = FSortida (fromMaybe "sortida_per_defecte" ms)
-- embolcalla els arg. lliures com a ''flags'' per un tractament unificat
argQueNoEsOpció :: String -> Flag
argQueNoEsOpció arg = FArgLliure arg
msg_com = "Ús: nom_prog [-V] [--versió] [-i nom_fitxer] [--entrada nom_fitxer] [-o sortida] [--sortida sortida] paràmetres..."
processa flags = do
print flags
exitSuccess
error_als_args nom_prog msgs_error = do
printf "%s error als paràmetres\n" nom_prog
putStrLn $ concat msgs_error ++ usageInfo msg_com opcions
exitWith $ ExitFailure 1 -- sortir amb el codi de finalització
main = do
nom_prog <- getProgName
args <- getArgs
case getOpt (ReturnInOrder argQueNoEsOpció) opcions args of
-- cas de manca d'errors (llista buida al 3r de la tupla)
(flagsCorrectes, [], []) -> processa flagsCorrectes
-- altrament
(_, _, msgs_error) -> error_als_args nom_prog msgs_error
Excepcions de tipus definits per l'usuari
Haskell2010 millora les excepcions del Haskell98, permetent definir-ne tipus nous (amb els camps que convingui), que només han d'instanciar Show i la implementació per defecte de la classe Exception, prèvia derivació de Data.Typeable[276]
-- per implementar Exception, cal que prèviament implementin les classes requerides pel context
class (Typeable e, Show e) => Exception e
L'extensió DeriveDataTypeable facilita la derivació d'instàncies de Typeable pel compilador quan s'esmenta en una clàusula deriving.
{-# LANGUAGE DeriveDataTypeable #-}
import Control.Exception
import Data.Typeable
data TExcepcionsDeLAplicacio = EParametreIlegal String | EUnaAltraExcepcio String
deriving (Show, Typeable) -- fem que el compilador derivi les instàncies requerides per la classe Exception
instance Exception TExcepcionsDeLAplicacio
Exemple:
{-# LANGUAGE DeriveDataTypeable, -- important !!
ScopedTypeVariables, BangPatterns #-}
import Prelude hiding (catch) -- cal fer servir el ''catch'' de Control.Exception
{- Del Prelude
-- Non-I/O exceptions are not caught by this variant; to catch all
-- exceptions, use 'Control.Exception.catch' from "Control.Exception".
-- The 'catch' function is deprecated. Please use the new exceptions
-}
import Control.Exception
import Data.Typeable
data TExcepcionsDeLAplicacio = EParametreIlegal String | EUnaAltraExcepcio String
deriving (Show, Typeable) -- important !!
instance Exception TExcepcionsDeLAplicacio -- instancia els mètodes per defecte de la classe ''Exception''
factorial :: Int → Maybe Integer
factorial n | n == 0 = Just 1
| n > 0 = Just (fac_rf n 1)
| otherwise = Nothing
where -- aval. estricta (!) als param. formals (extensió BangPatterns)
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")
-- els comentaris d'autodoc de Control.Exception "Catching exceptions" recomanen
-- caçar les excepcions síncrones amb "try"
-- caçar les asíncrones amb "catch" o bé "catches" o similars
-- (http://hackage.haskell.org/package/base/docs/Control-Exception.html#g:3)
prova_fac :: Int → IO ()
prova_fac n = case try (avalua_fac n) of
Right v → putStrLn "no ha petat"
Left (EParametreIlegal msg) → putStrLn $ "paràmetre ilegal! " ++ msg
Left excep → putStrLn $ "excepció: " ++ show excep
main = prova_fac (-1)
--
llistes per comprensió a l'estil de SQL
Incorporant transformacions a les llistes per comprensió amb la clàusula then.[277] En cas d'emular una consulta SQL agrupada (Group by) cal prefixar els camps agrupats amb la clàusula the. Cal esmentar la pragma {-# LANGUAGE TransformListComp #-}
{-# LANGUAGE TransformListComp #-}
import GHC.Exts (the, groupWith, sortWith)
import Data.Function ((&)) -- (&) = flip ($) -- infixl 1 -- desde GHC 7.10.1
-- 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)]
avg :: (Fractional a) => [a] → a
avg [] = 0
avg xs = sum xs / fromIntegral (xs & length)
main = print consulta
dona
[("Programació",42.5),("Comptabilitat",60.0),("Vendes",90.0)]
vectors a la mònada ST
La mònada ST permet elaborar funcions per a ser cridades des de codi funcional, que encapsulen l'encadenament de canvis d'estat d'àmbit local, en aquest cas, de vectors.
1.- Amb vectors amb índexs de tipus divers en un rang, i elements d'allotjament directe (Unboxed), com a (UArray tipusIndex tipusElem)
.
Creació del vector com a immutable, descongela a mudable (thaw), modificació, recongelació(freeze) i llistat:
import Data.Array.IArray as IArray -- interfície vectors immutables
import Data.Array.MArray as MArray -- interfície vectors mudables
import Data.Array.Unboxed -- vector immutable UArray d'elements d'allotjament directe
import Data.Array.ST -- vector mudable STUArray d'elements d'allotjament directe a la mònada ST
import Control.Monad.ST -- encapsula canvis d'estat local dins de codi funcional
modificaArray :: Int → UArray Int Int → ST s (UArray Int Int)
modificaArray indx immVect = do
mutVect <- thaw immVect :: ST s (STUArray s Int Int) -- descongela a mudable
x <- readArray mutVect indx
writeArray mutVect indx (x - 1)
freeze mutVect -- congela a immutable
main = do
let immVect = IArray.listArray (0,1) [0,1] :: UArray Int Int -- creació amb rang, elements i tipus
immVect2 = runST $ modificaArray 0 immVect
print $ elems immVect2
2.- Amb vectors "eficients" (índex Int basat en 0) i tipus del vector (Vector tipusElem)
del paquet Vector.[96] Avantatge: implementa força més classes i disposa d'algoritmes específics al paquet vector-algorithms.[278]
{-# LANGUAGE UnicodeSyntax #-}
import Data.Vector.Unboxed (Vector, MVector)
import Data.Vector.Unboxed as V
import Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST
modificaArray :: Int → Vector Int -> ST s (Vector Int)
modificaArray indx immVect = do
mutVect <- V.thaw immVect :: ST s (MVector s Int) -- descongela a mudable
x <- MV.read mutVect indx
MV.write mutVect indx (x - 1)
V.freeze mutVect -- congela a immutable
main = do
let immVect = V.fromList [0,1] :: Vector Int -- creació amb elements i tipus
immVect2 = runST $ modificaArray 0 immVect
print $ V.toList immVect2
diccionaris eficients amb HashMap
Hashable[87] aporta instàncies de funció resum per als tipus més freqüents.
HashMap i HashSet són més ràpids que les implementacions basades en arbres, especialment quan la comparació de claus és lenta, com és el cas del tipus String.[279]
L'anterior implementació de la biblioteca HashMap del paquet hashmap que feia servir un arbre balancejat per als cistells de claus coincidents requerint (Ord clau), ha quedat desaconsellada en favor de la biblioteca unordered-containers que fa servir llistes per als cistells requerint (Eq clau).
{-# LANGUAGE OverloadedStrings #-}
import Control.Exception (assert)
import Data.Text (Text)
import qualified Data.Text.IO as TextIO
import Data.HashMap.Lazy (HashMap, member, (!), lookup) -- aquí també s'importen les instàncies de Hashable
import qualified Data.HashMap.Lazy as Map
-- el tipus Text que implementa Hashable, serà la clau del diccionari eficient
dicc :: HashMap Text Text
dicc = Map.fromList [("primer", "el que va davant"),
("segon", "el que ve després")]
main = do
assert (Map.member "primer" dicc) -- (!) és funció parcial, és per això que verifiquem la precondició
$ TextIO.putStrLn $ dicc ! "primer" -- (!) és Map.!
case (Map.lookup "segon" dicc) of -- amb Map.lookup (resultat opcional) no cal la comprovació precedent
Just txt → TextIO.putStrLn txt
Nothing → TextIO.putStrLn "el segon no hi era"
# el paquet "unordered-containers" que conté Data.HashMap.Lazy ve amb la plataforma Haskell
runhaskell prova.hs
Expressions regulars
Tots els paquets d'expressions regulars implementen (=~)
que dona èxit i coincidències segons el tipus del resultat. (=~~)
equival a (=~)
en el context d'una mònada (text =~~ regex = return $ text =~ regex)
. Vegeu ref.[280]
- L'operació (=~) està sobrecarregada, oferint diferents informacions segons el tipus del resultat, exigible amb una restricció de tipus:
- Bool: indica si l'encaix té èxit
- String: ofereix el text encaixat
- MatchArray: informa de les posicions d'encaix dels grups, els elements són :: (posició, llargada)
- (txt, MatchText txt, txt): informa (text_precedent, encaixos, text_posterior), l'elem. dels encaixos és :: (txt, (posició, llargada))
- [MatchArray]: cerca múltiple oferint la llista [Array Int (posició, llargada)]
- [MatchText txt]: cerca múltiple oferint la llista [Array Int (txt, (posició, llargada))]
En cas d'efecte lateral per l'ús d'una biblioteca externa, l'operador (=~~) ofereix la mateixa funcionalitat que (=~) però en el context seqüencial d'una mònada.
Prova: cabal install regex-pcre
$ ghci
Prelude> import Text.Regex.PCRE
> import Control.Monad (forM_, mapM_)
> import Data.Function ((&)) -- (&) ≡ flip ($)
> import Data.Foldable (toList)
> import Control.Category ((>>>)) -- f >>> g ≡ g. f
> let regex = "(\\d)(\\d)"
> "12" =~ regex :: Bool -- tenim coincidència ?
True
> "12" =~ regex :: String -- text coincident ?
"12"
> ("12" =~ regex :: MatchArray) & toList -- llista grups :: [(posició, llargada)]
[(0,2),(0,1),(1,1)]
-- (MatchText txt) és un 'array' de grups :: (txt, (posició, llargada))
> "ab12cd" =~ regex :: (String, MatchText String, String) -- (text_precedent, grups, text_posterior)
("ab",array (0,2) [(0,("12",(2,2))),(1,("1",(2,1))),(2,("2",(3,1)))],"cd")
> :{ -- mode multilínia
| ("ab12cd" =~ regex :: (String, MatchText String, String))
| & \(pre, matchText, post) -> (pre, (matchText & fmap fst) & toList, post)
| :}
("ab",["12","1","2"],"cd")
-- Per cercar múltiples ocurrències cal especificar el tipus de sortida com una llista de resultats
-- per fer-ho en el context d'una mònada (IO) farem servir (=~~) en comptes de (=~)
> let {origen = "12 34"; regex = "(\\d)(\\d)"}
> (origen =~~ regex :: IO [MatchArray]) >>= (map toList >>> return)
[[(0,2),(0,1),(1,1)], [(3,2),(3,1),(4,1)]]
> :{ -- mode multilínia
| do
| ocurrències <- origen =~~ regex :: IO [MatchText String]
| forM_ ocurrències $ \ ocurrència ->
| (forM_ ocurrència $ \ grup -> (grup & fst & putStrLn))
| :}
12
1
2
34
3
4
serialització en format JSON
Per als tipus que instancien la classe Generic (tipus algebraics i d'altres) se'n poden derivar les classes {ToJSON, FromJSON} automàticament.[281]
Amb l'ús de l'extensió DeriveAnyClass, per als tipus algebraics és tan fàcil com afegir les classes {Generic, ToJSON, FromJSON} a la clàusula deriving. Requereix el paquet aeson.
{-# LANGUAGE PackageImports, DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-} -- permet incloure a la clàusula 'deriving' classes que tinguin implem. per defecte
import "aeson" Data.Aeson (ToJSON, FromJSON, encode, decode)
import GHC.Generics (Generic)
data Persona = Persona {nom :: String, edat :: Int} deriving (Show, Generic, ToJSON, FromJSON)
data Llenguatge = Lleng_Fortran | Lleng_Haskell deriving (Show, Enum, Generic, ToJSON, FromJSON)
data Programador = Programador {persona :: Persona, llenguatges :: [Llenguatge]} deriving (Show, Generic, ToJSON, FromJSON)
joanPersona = Persona {nom="Joan", edat=10}
joanProgramador = Programador {persona=joanPersona, llenguatges=[Lleng_Fortran, Lleng_Haskell]}
serialitzat = encode joanProgramador
recuperat = decode serialitzat :: Maybe Programador
main = do
putStrLn $ "serialitzat: " ++ show serialitzat
putStrLn $ "recuperat: " ++ show recuperat
dona:
serialitzat: "{\"llenguatges\":[\"Lleng_Fortran\",\"Lleng_Haskell\"],\"persona\":{\"edat\":10,\"nom\":\"Joan\"}" recuperat: Just (Programador {persona = Persona {nom = "Joan", edat = 10}, llenguatges = [Lleng_Fortran,Lleng_Haskell]})
arbres
Vegeu ref.[282]
{-# LANGUAGE UnicodeSyntax #-}
import Data.Tree (Tree)
import qualified Data.Tree as Tree
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Tuple (swap)
import Data.Maybe
import Data.Function ((&)) -- (&) = flip ($) -- infixl 1 -- desde GHC 7.10.1
-- definició del tipus arbre a Data.Tree
-- data Tree a = Node {rootLabel :: a, subForest :: [Tree a]}
-- generador d'arbre
-- Tree.unfoldTree :: (b → (a, [b])) → b → Tree a
data Espècie = Animal | Vertebrat | Mamífer | Primat | Humà | Mico
deriving (Eq, Ord, Enum, Show)
parentiu = [(Vertebrat, Animal), (Mamífer, Vertebrat), (Primat, Mamífer),
(Humà, Primat), (Mico, Primat)]
subespècies :: Map Espècie [Espècie]
subespècies = Map.fromListWith (++) -- combina valors de col·lisions amb (++)
$ fmap (elemALlista. capgira) parentiu
where
-- elemALlista = fmap (\x -> [x]) -- versió tàcita
elemALlista (k, e) = (k, [e]) -- versió explícita
capgira = swap
-- funció per generar l'arbre
jerarquitzador :: Espècie → (Espècie, [Espècie])
jerarquitzador x = (etiquetaNus, fills)
where
etiquetaNus = x
fills = Map.lookup x subespècies
& fromMaybe [] -- llista buida si no és al diccionari
elMeuArbre :: Tree Espècie
elMeuArbre = Tree.unfoldTree jerarquitzador Animal
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
dona el resultat:
Animal | `- Vertebrat | `- Mamífer | `- Primat | +- Mico | `- Humà
Combinació de filtres
La classe Applicative permet combinar, amb una funció o bé un constructor, els resultats d'una seqüència de computacions.
Prenem com a tipus que l'implementa, les funcions amb entrada del mateix tipus
((->) a) {- 'a' és el tipus del primer paràmetre de (->), o sigui el tipus de l'entrada -}
-- Del codi font del mòdul Control.Applicative
-- implementació de Applicative de les funcions amb entrada del mateix tipus ''a''
instance Applicative ((->) a) {- 'a' és el primer paràmetre d'una funció (a ->) -} where
pure = const -- generador del tipus ((->) a), que ignora l'entrada
(<*>) f g x = f x (g x) -- seqüencia funcions aplicant-los-hi la mateixa entrada
Exemple de combinació.
import Control.Applicative
ésMúltipleDe :: Integral a => a -> a -> Bool
ésMúltipleDe x y = y `mod` x == 0
-- notació amb les operacions canòniques ''pure'' i (<*>)
ésMúltipleDe3oDe5 :: Integral a => a -> Bool
ésMúltipleDe3oDe5 = pure (||) <*> ésMúltipleDe 3 <*> ésMúltipleDe 5
-- notació amb liftAn definida a Control.Applicative
ésMúltipleDe3oDe5 :: Integral a => a -> Bool
ésMúltipleDe3oDe5 = liftA2 (||) (ésMúltipleDe 3) (ésMúltipleDe 5)
-- notació amb els operadors binaris de les classes Functor i Applicative típics (<$>) i (<*>)
ésMúltipleDe3oDe5 = (||) <$> ésMúltipleDe 3 <*> ésMúltipleDe 5
-- també podem definir:
(<||>) :: Applicative efecte => efecte Bool -> efecte Bool -> efecte Bool
(<||>) = liftA2 (||)
-- llavors
ésMúltipleDe3oDe5 = ésMúltipleDe 3 <||> ésMúltipleDe 5
QuickSort amb Data Parallel Haskell
Vegeu #Data Parallel Haskell i ref.[40]
1. Mòdul d'operacions vectoritzades
{-# LANGUAGE PackageImports, ParallelArrays #-}
{-# OPTIONS -fvectorise #-}
{-# OPTIONS -fno-spec-constr-count #-}
module QSortVect (quicksortPA) where
import "dph-lifted-vseg" Data.Array.Parallel
import "dph-lifted-vseg" Data.Array.Parallel.Prelude.Double as D
import qualified "dph-lifted-vseg" Data.Array.Parallel.Prelude.Int as I
import qualified Prelude
import Control.Category (>>>) -- f >>> g == g. f
{-# NOINLINE quicksortPA #-}
quicksortPA:: PArray Double → PArray Double
quicksortPA = fromPArrayP >>> qsortVect >>> toPArrayP
qsortVect:: [: Double :] → [: Double :]
qsortVect xs
| lengthP xs I.<= 1 = xs
| otherwise =
let pivot = xs !: 0
menors = [: x | x <- xs, x D.< pivot :]
majors = [: x | x <- xs, x D.> pivot :]
iguals = [: x | x <- xs, x D.== pivot :]
-- mapeja en paral·lel qsortVect als subvectors (Nested Data Parallellism) !!
vectors = mapP qsortVect [: menors, majors :]
in
(vectors !: 0) +:+ iguals +:+ (vectors !: 1)
2. Mòdul d'operacions NO-vectoritzades
{- Main.hs -}
{-# LANGUAGE PackageImports #-}
import System.Environment
import Text.Printf
import Data.Function ((&)) -- (&) = flip ($) -- infixl 1 -- desde GHC 7.10.1
import "dph-lifted-vseg" Data.Array.Parallel.PArray (fromList, toList)
import QSortVect (quicksortPA)
llistaDesordenada :: Int → [Double]
llistaDesordenada n = map toDouble $ [n,(n-1)..1]
toDouble n = realToFrac n :: Double
ordena :: Int -> [Double]
ordena mida = llistaDesordenada mida & fromList
& quicksortPA
& toList
main = do
args <- getArgs
nomProg <- getProgName
case args of
[arg] → do
let num = read arg :: Int
num & abs
& ordena
& take 5
& print
_ → printf "afegiu nombre d'elements\nús: %s 999\n" nomProg
- Compila i executa
prompt$ ghc --make -threaded -rtsopts -with-rtsopts=-N -Odph Main.hs QSortVect.hs -o main -package dph-lifted-vseg
prompt$ time./main 100000
[1.0,2.0,3.0,4.0,5.0]
real 0m4.065s
user 0m5.552s
sys 0m1.480s
Interfícies gràfiques
Hola món amb GTK
Web del projecte GTK per a Haskell aquí.[283]
Cal que el sistema tingui instal·lades versions de desenvolupament (acabades en -dev) de les biblioteques gràfiques
cabal install gtk2hs-buildtools gtk
import Graphics.UI.Gtk
gestorEnClicarBotó :: IO ()
gestorEnClicarBotó = putStrLn "Has clicat el botó Hola món"
main :: IO ()
main = do
initGUI
finestra <- windowNew
botó <- buttonNew
set finestra [ containerBorderWidth := 10,
containerChild := botó ]
set botó [ buttonLabel := "Hola món" ]
onClicked botó gestorEnClicarBotó
onDestroy finestra mainQuit
widgetShowAll finestra
mainGUI
Si apareix el missatge "Undefined symbol ___gxx_personality_v0 on link" cal afegir la biblioteca stdc++ al relligat.[284]
ghc --make prova.hs -lstdc++
./prova
Hola món amb WxHaskell
WxHaskell[285] és una implementació de l'entorn gràfic Wx[286] que és multi-plataforma.
Cal haver instal·lat prèviament les biblioteques wx de desenvolupament per al sistema subjacent.
# cas de cabal v1.18+, senyal -jN per a muntatge multi-procés
# per a wx 2.8
cabal install "wx < 0.90"
# per a wx 2.9
cabal install "wx == 0.90.*"
# per a wx 3.x
cabal install "wx >= 0.91"
module Main where
import Graphics.UI.WX
gestorEnClicarBotó :: IO ()
gestorEnClicarBotó = putStrLn "Has clicat el botó Hola món"
hello :: IO ()
hello = do
frame1 <- frame [text := "Exemple"]
botó <- button frame1 [text := "Hola món", on command := gestorEnClicarBotó]
set frame1 [layout := widget botó]
main :: IO ()
main = start hello
Lents de Van Laarhoven -- Consulta i manipulació de parts d'estructures complexes
Una lent (referència funcional) és un mecanisme componible que permet enfocar un component d'una estructura per ésser manipulat en tant que integrant de la mateixa. Vegeu refs.[287][288][289][290] Exemple amb el paquet lens de Eduard Kmett.[291]
Vegeu també Haskell#Lents basades en profunctors
{-# LANGUAGE TemplateHaskell, PackageImports #-}
import "lens" Control.Lens (makeLenses, Lens', (^.), (^..), over, set)
import Data.Function ((&)) -- (&): aplic. cap enrere
import Data.Monoid ((<>)) -- mappend assoc per la dreta
-- per convenció, per crear les lents, cal prefixar els camps amb el caràcter de subratllat
data Arc = Arc { _graus, _minuts, _segons :: Int } deriving (Show)
data Situació = Situació { _latitud, _longitud :: Arc } deriving (Show)
-- ''makeLenses'' genera, per meta-programació, lents corresponents als accessors d'un registre, prefixats amb '_', assignant-los el mateix nom sense el prefix '_'
$(makeLenses ''Arc) -- $() avalua en temps de compilació
$(makeLenses ''Situació) -- el prefix doble apòstrof indica "tipus" en llenguatge Template Haskell
-- les lents es poden compondre
-- (.) :: Lens' a b -> Lens' b c -> Lens' a c
-- lents compostes
lentGrausDeLatitud, lentMinutsDeLatitud, lentSegonsDeLatitud :: Lens' Situació Int
lentGrausDeLatitud = latitud. graus -- composició de lents: (Lens' Situació Arc). (Lens' Arc Int)
lentMinutsDeLatitud = latitud. minuts
lentSegonsDeLatitud = latitud. segons
-- estructura a manipular
sitBcn = Situació (arcDeGrausDec 41.399423) (arcDeGrausDec 2.128037)
-- decimal a sexagesimal
arcDeGrausDec :: Double -> Arc
arcDeGrausDec v = Arc partSencera mins secs
where
-- amb 'properFraction' s'obté la "fracció pròpia" (negativa per als valors negatius de lat/lon)
(partSencera, partFracció) = properFraction v
segons = truncate (partFracció * 3600)
(mins, secs) = segons `quotRem` 60 -- partint cap a zero amb 'quotRem'
main = do
-- llegeix els graus de la latitud
let grausLat = sitBcn ^. lentGrausDeLatitud
-- llegeix diversos a llista concatenant
let enLlistaLat = sitBcn ^.. (lentGrausDeLatitud <> lentMinutsDeLatitud <> lentSegonsDeLatitud)
-- modifica els graus de la latitud
let sitDosGrausMésAlNordDeBcn = sitBcn & over lentGrausDeLatitud (+2)
-- estableix els graus de la latitud
let sitBcnAmbGrausLat45 = sitBcn & set lentGrausDeLatitud 45
putStrLn $ "situació Bcn: " ++ show sitBcn
putStrLn $ "\ngraus lat. Bcn: " ++ show grausLat
putStrLn $ "\nlatitud en llista: " ++ show enLlistaLat -- dona: latitud en llista: [41,23,57]
putStrLn $ "\ndos graus més al Nord de Bcn: " ++ show sitDosGrausMésAlNordDeBcn
putStrLn $ "\nfixa graus lat. a 45 s/. situació de Bcn: " ++ show sitBcnAmbGrausLat45
Nota de l'exemple: la funció properFraction correspon a la definició de Fracció pròpia.
Extreure informació d'un document XML
Vegeu-ho a Fletxa (programació funcional)#Exemple - Extreure informació d'un document XML
Operacions als tipus - Tipus dependents de valors
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[294] (cat: tipus solters, volent dir monovalor) o algunes definicions inductives més sofisticades.[295]
- Remarcable: El paquet dimensional-tf incorpora als tipus les dimensions de les unitats de la física.[296]
{-# 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
-- Mul, Div, Pow son operacions definides com a famílies de tipus
-- (vegeu https://hackage.haskell.org/package/numtype-tf/docs/Numeric-NumType-TF.html)
-- el tipus Pos2 té un únic valor ''pos2'' (+2)
-- negatius amb prefix ''Neg'' als tipus o bé ''neg'' per als valors
import "numtype-tf" Numeric.NumType.TF (pos2, pos3, neg2, Pos2, Pos3, Neg2)
-- les expressions són del tipus :: Quantity Dimensions a
-- on Dimensions és un vector de potències de cada unitat implementat com a tipus producte.
-- Dimensions es pot expressar en funció de vectors unitaris (DLength, DTime…)
-- type DLength = Dim Pos1 Zero Zero Zero Zero Zero Zero
-- type DArea = Dim Pos2 Zero Zero Zero Zero Zero Zero
-- i també de l'aplicació de les famílies de tipus {Mul, Div, Pow}
-- corresp. a producte, divisió i exponenciació, definides a Numeric.Units.Dimensional.TF.
-- type DArea = Pow DLength Pos2
-- type Area = Quantity DArea
llargada = 4 *~ kilo meter :: Quantity DLength Float -- sinònim :: Length Float
area = 10 *~ meter ^ pos2 :: Quantity (Pow DLength Pos2) Float -- sinònim :: Area Float
-- les anotacions de tipus permeten que el compilador validi les operacions
volum :: Quantity (Pow DLength Pos3) Float -- dimLlargada elevada a la potència Pos3
volum = llargada * area
temps = 10 *~ minute :: Quantity DTime Float -- sinònim :: Time Float
tempsAlQuadrat = 10 *~ second ^ pos2 :: Quantity (Pow DTime Pos2) Float
-- :: (Acceleration Float) també es pot expressar com segueix:
acceleració :: Quantity (Div DLength (Pow DTime Pos2)) Float
-- acceleració :: Quantity (Mul DLength (Pow DTime Neg2)) Float -- alternativa
acceleració = llargada / tempsAlQuadrat
main = do
putStrLn $ "volum: " ++ show volum
putStrLn $ "acceleració: " ++ show acceleració
passa la comprovació de tipus i dona:
volum: 40000.0 m^3 acceleració: 400.0 m s^-2 -- 4000 m / 10 s^2
Naturals als tipus
La proposta TypeNats[297] ("Type level natural numbers"), parcialment implementada a GHC 7.6.1, permet l'ús de naturals als tipus facilitant les proposicions inductives als tipus.
Cal fer servir l'endollable (ang:plugin) de GHC[298] type-nat-solver[299] de Iavor Diatchki (opció de compilació -fplugin=TypeNatSolver inclosa al codi, i afegir el paquet type-nat-solver a les dependències de l'aplicació.
Ampliant l'exemple.[300] Compilat amb GHC 7.10.1.
# instal·lar el paquet 'cabalg' que instal·la directament del GitHub (Doc a https://hackage.haskell.org/package/cabalg)
cabal install cabalg #
cabalg https://github.com/yav/type-nat-solver # descarrega ('git clone') a la carpeta type-nat-solver i instal·la
Mòdul de nombres de Peano de tipus singulars (d'un sol valor) (ang: singleton types).
{-# OPTIONS_GHC -fplugin=TypeNatSolver #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
-- {-# LANGUAGE ExplicitNamespaces #-} -- per importar el constructor de tipus (+), implicat per l'ext. TypeOperators
{-# LANGUAGE GADTs #-}
module UNat (
UNat(..),
UNat.toInteger,
plus,
) where
import Data.Proxy (Proxy(..))
import GHC.TypeLits (Nat, natVal, KnownNat, type (+)) -- prefix 'type' per explicitar l'espai de noms del "constructor de tipus" (+)
-- el constructor de tipus (+) definit a GHC.TypeLits: type family (m :: Nat) + (n :: Nat) :: Nat
import Control.Category (>>>) -- f >>> g == g. f
data UNat :: Nat -> * where
Z :: UNat 0
S :: UNat n -> UNat (n + 1)
toProxy :: UNat n -> Proxy n
toProxy _ = Proxy
toInteger :: (KnownNat n) => UNat n -> Integer
toInteger = toProxy >>> natVal
plus :: UNat n -> UNat m -> UNat (n + m) -- utilitza el constructor de tipus (+)
plus Z x = x
plus (S x) y = S (plus x y)
Mòdul de contenidor amb naturals al tipus.
{-# OPTIONS_GHC -fplugin=TypeNatSolver #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
module Vec (Vec,
empty, null, length, index,
cons, append, uncons, split,
UNat(..)
) where
import Prelude hiding (null, length)
import GHC.TypeLits
import Data.Proxy
import Numeric.Natural
import UNat
import qualified Data.List as L
import Control.Category (>>>) -- f >>> g == g. f
data Vec :: Nat -> * -> * where
Nil :: Vec 0 a
Cons :: a -> Vec n a -> Vec (n + 1) a
instance Show a => Show (Vec n a) where
show Nil = "[]"
show (Cons x xs) = show x ++ " : " ++ show xs
empty = Nil
cons = Cons
null :: Vec n a -> Bool
null Nil = True
null _ = False
singleton :: a -> Vec 1 a
singleton x = Cons x Nil
append :: Vec m a -> Vec n a -> Vec (m + n) a
append Nil ys = ys
append (Cons x xs) ys = Cons x (append xs ys)
uncons :: Vec (n+1) a -> (a, Vec n a)
uncons (Cons x xs) = (x, xs)
index :: (m <= n) => UNat m -> Vec (n+1) a -> a
index Z (Cons x xs) = x
index (S r) (Cons _ (Cons x xs)) = index r (Cons x xs)
split :: UNat m -> Vec (m + n) a -> (Vec m a, Vec n a)
split Z xs = (Nil, xs)
split (S n) (Cons x xs) = case split n xs of
(as,bs) -> (Cons x as, bs)
--
vecProxy :: Vec (n::Nat) a -> Proxy n
vecProxy _ = Proxy
length :: KnownNat n => Vec (n::Nat) a -> Integer
length = vecProxy >>> natVal
Precisió dels tipus bàsics
import qualified Numeric
import qualified Char
precisióSencers = "Int.MAX: " ++ (map Char.toUpper hexIntMax)
where
hexIntMax = Numeric.showHex límitSuperiorSencers "\n"
límitSuperiorSencers = maxBound::Int
precisióComaFlotant :: (RealFloat a) => a → String
precisióComaFlotant nombre = mostraPrecisióSignificand ++ "; " ++ mostraRangExponent
where
mostraPrecisióSignificand = "significand: " ++ show (floatDigits nombre) ++ " dígits"
mostraRangExponent = "exponent (min, max): " ++ show (floatRange nombre)
main = do
putStrLn precisióSencers
putStrLn $ "Double: " ++ precisióComaFlotant (1.0::Double)
putStrLn $ "Float: " ++ precisióComaFlotant (1.0::Float)
En un processador x86-64 amb GHC:
Int.MAX: 7FFFFFFFFFFFFFFF Double: significand: 53 dígits; exponent (min, max): (-1021,1024) Float: significand: 24 dígits; exponent (min, max): (-125,128)
Els sencers ofereixen, a GHC, el rang complet corresp. a la paraula de la màquina (malgrat que l'especificació de Haskell només exigeix un mínim [-2^29.. 2^29-1]),[301] 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[302] donen un valor esbiaixat respecte a la intuïció. L'intèrpret GHCI dona 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ó al component de la representació interna IEEE 754 segons l'especificació per a coma flotant del "Language Independent Arithmetic standard".
- La notació estandarditzada per a coma flotant de 1 és (en base 2): 0.1base 2 * 2^1
A ghci:
Prelude> exponent (2^0::Float) 1 -- !! en base 2: 1 == 0.1 (pel bit amagat de la mantissa) * 2^1 -- significand: signe * (bit amagat precedint la repr. mantissa IEEE 754 / 2^nombre_de_bits) Prelude> significand (2^0::Float) 0.5 -- signe * 1 / (2^1) -- en ésser potència de dos, la repr. de la mantissa són tot zeros Prelude> let r = -1 :: Float # es manté l'equació següent, fins i tot per a valors de r negatius Prelude> r == (significand r) * (2 ^^ (exponent r)) True
Relacionat: Hi ha tres operadors d'exponenciació (^), (^^), (**) segons que el domini de l'exponent sigui natural, sencer, o real en coma flotant.
Compilació de mòduls amb referències mútues (cicles)
- Vegeu[303]
Encara que sovint é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,[303] 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, busca totes les extensions de fitxer per cada nom de mòdul, trobarà l'extensió ".hs-boot" i hi aplicarà el preprocés adequat.
Referències
- ↑ mmc - Compilador Mercury de Melbourne(anglès)
- ↑ web del Compilador Haskell de York(anglès)
- ↑ web del Compilador Haskell d'Utrecht Arxivat 2014-07-22 a Wayback Machine.(anglès)
- ↑ web del C--(anglès)
- ↑ Diagrama de blocs del ghc que incorpora C--(anglès)
- ↑ El llenguatge Cmm(anglès)
- ↑ Estàndard actual del llenguatge Haskell
- ↑ La "Plataforma Haskell"(anglès)
- ↑ 9,0 9,1 9,2 API de la Platforma Haskell Arxivat 2016-12-20 a Wayback Machine. Seleccioneu-ne la versió(anglès)
- ↑ Anunci de la GHC 6.12.1(anglès)
- ↑ How to play with Stack
- ↑ How to script with Stack
- ↑ How to build with Stack
- ↑ Biblioteques i mòduls estàndard actuals
- ↑ Versions de biblioteques de les edicions de GHC(anglès)
- ↑ Libraries Version-history(anglès)
- ↑ Proposal: Remove Control.OldException(anglès)
- ↑ Biblioteques del GHC 7.4.2(anglès) Control.OldException encara hi és
Les excepcions del H98 estan suportades - ↑ Biblioteques del GHC 7.6.1(anglès) Control.OldException ja no hi és
Les excepcions del Haskell98 ja no estan suportades - ↑ The Glasgow Haskell Compiler(anglès)
- ↑ Documentació del compilador GHC
- ↑ Opcions del RunTimeSystem per controlar la mem. dinàmica i el recollidor de memòria brossa Arxivat 2010-04-02 a Wayback Machine.(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.
- ↑ mòdul System.Mem(anglès)
- ↑ Simon Peyton Jones, et. al. - Stretching the storage manager(anglès)
- ↑ mòdul System.Mem.Weak(anglès)
- ↑ Control.Concurrent.MVar.mkWeakMVar(anglès)
- ↑ Ú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)
- ↑ El paquet meta-par(anglès)
- ↑ 37,0 37,1 HaskellWiki - A Repa Tutorial(anglès)
- ↑ REgular PArallel arrays(anglès)
- ↑ Haskell - Paral·lelisme de dades
- ↑ 40,0 40,1 Nested Data Parallelism - presentació (diapos)
- ↑ Paquet dph-examples
- ↑ Guy E. Blelloch - Programming Parallel Algorithms - Nested Data Parallellism
- ↑ GHC.Prim - SIMD Vectors(anglès)
- ↑ SIMD instructions in GHC (anglès)
- ↑ Paquet simd
- ↑ Paquet primitive-simd
- ↑ 47,0 47,1 UNSW.edu.au - An Embedded Language for Accelerated Array Computations(anglès)
- ↑ Biblioteca Accelerate per al paral·lelisme a les GPU(anglès)
- ↑ biblioteca accelerate-cuda(anglès)
- ↑ El paquet accelerate-opencl(anglès)
- ↑ El paquet accelerate-repa(anglès)
- ↑ paquet OpenCLWrappers(anglès)
- ↑ OpenCL from Haskell Arxivat 2012-01-21 a Wayback Machine.(anglès)
- ↑ How to write hybrid CPU/GPU programs with Haskell(anglès)
- ↑ Hackage - paquet accelerate(anglès)
- ↑ Hackage - paquet accelerate-cuda(anglès)
- ↑ CUDA developer zone(anglès)
- ↑ accelerate-opencl de HIPERFIT(anglès)
- ↑ NVIDIA OpenCL(anglès)
- ↑ AMD APP SDK – A Complete Development Platform Arxivat 2016-02-09 a Wayback Machine.(anglès)
- ↑ accelerate amb rerefons OpenCL-Cilk(anglès) Subcarpeta icc-opencl
- ↑ accelerate-repa(anglès)
- ↑ accelerate-llvm(anglès)
- ↑ Template Haskell(anglès)
- ↑ Haskellwiki - QuasiQuotation(anglès)
- ↑ 66,0 66,1 Yesod - plantilles(anglès)
- ↑ Sintaxi dels quasiQuoters Arxivat 2013-04-14 at Archive.is(anglès)
- ↑ «QuasiQuotation». Arxivat de l'original el 2013-04-14. [Consulta: 5 octubre 2011].
- ↑ Tipus algebraics del TemplateHaskell (anglès)
- ↑ Haskellwiki - Quasi-Quotation(anglès)
- ↑ Constructor QuasiQuoter al paquet template-haskell(anglès)
- ↑ Template Haskell - stringE(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)
- ↑ 77,0 77,1 paquet Stream-fusion(anglès)
- ↑ API del GHC(anglès)
- ↑ API Contenidors(anglès)
- ↑ 80,00 80,01 80,02 80,03 80,04 80,05 80,06 80,07 80,08 80,09 80,10 80,11 80,12 Data.Set
- ↑ 81,00 81,01 81,02 81,03 81,04 81,05 81,06 81,07 81,08 81,09 81,10 81,11 81,12 81,13 Data.Map
- ↑ 82,00 82,01 82,02 82,03 82,04 82,05 82,06 82,07 82,08 82,09 82,10 82,11 Data.IntSet
- ↑ 83,00 83,01 83,02 83,03 83,04 83,05 83,06 83,07 83,08 83,09 83,10 83,11 Data.IntMap
- ↑ paquet hashmap (anglès)
- ↑ Data.HashSet del paquet hashmap(anglès)
- ↑ Data.HashMap del paquet hashmap(anglès)
- ↑ 87,0 87,1 paquet Hashable(anglès)
- ↑ El paquet unordered-containers (HashMap i HashSet que no requereixen claus ordenables)
- ↑ 89,00 89,01 89,02 89,03 89,04 89,05 89,06 89,07 89,08 89,09 89,10 89,11 Data.HashSet del paquet unordered-containers(anglès)
- ↑ 90,00 90,01 90,02 90,03 90,04 90,05 90,06 90,07 90,08 90,09 90,10 Data.HashMap.Lazy del paquet unordered-containers(anglès)
- ↑ 91,00 91,01 91,02 91,03 91,04 91,05 91,06 91,07 91,08 91,09 91,10 91,11 91,12 Data.Sequence(anglès)
- ↑ 92,0 92,1 92,2 92,3 92,4 92,5 Data.Array.Repa(anglès)
- ↑ paquet Vector(anglès)
- ↑ Haskell numèric: Guia sobre Vector(anglès)
- ↑ Famílies de tipus (anglès)
- ↑ 96,0 96,1 http://hackage.haskell.org/package/vector/docs/Data-Vector-Unboxed.html
- ↑ El paquet mono-traversable(anglès)
- ↑ 98,0 98,1 98,2 MonoPointed(anglès)
- ↑ 99,0 99,1 99,2 MonoFunctor(anglès)
- ↑ MonoZip(anglès)
- ↑ 101,0 101,1 101,2 101,3 101,4 MonoFoldable
- ↑ 102,0 102,1 MonoTraversable(anglès)
- ↑ 103,0 103,1 103,2 103,3 103,4 tipus NonNull t(anglès)
- ↑ 104,0 104,1 104,2 104,3 classe SemiSequence(anglès)
- ↑ 105,0 105,1 105,2 105,3 105,4 classe IsSequence(anglès)
- ↑ Textual(anglès)
- ↑ Utf8(anglès)
- ↑ 108,0 108,1 SetContainer(anglès)
- ↑ 109,0 109,1 109,2 109,3 109,4 IsSet(anglès)
- ↑ 110,0 110,1 110,2 110,3 110,4 IsMap(anglès)
- ↑ 111,0 111,1 111,2 111,3 111,4 111,5 111,6 111,7 111,8 paquet bytestring-trie(anglès)
- ↑ Data.DList(anglès)
- ↑ Demistifying DList's(anglès)
- ↑ 114,00 114,01 114,02 114,03 114,04 114,05 114,06 114,07 114,08 114,09 114,10 114,11 114,12 114,13 114,14 114,15 114,16 Data.Heap del paquet heap(anglès)
- ↑ paquet semigroups
- ↑ mòdul Data.Semigroup(anglès)
- ↑ 117,0 117,1 117,2 117,3 117,4 Data.List.NonEmpty(anglès)
- ↑ 118,0 118,1 118,2 118,3 118,4 118,5 118,6 118,7 118,8 Data.Maybe
- ↑ 119,0 119,1 119,2 119,3 119,4 119,5 119,6 Data.Either
- ↑ 120,00 120,01 120,02 120,03 120,04 120,05 120,06 120,07 120,08 120,09 120,10 120,11 120,12 120,13 Data.List
- ↑ 121,00 121,01 121,02 121,03 121,04 121,05 121,06 121,07 121,08 121,09 121,10 Data.ByteString(anglès)
- ↑ 122,00 122,01 122,02 122,03 122,04 122,05 122,06 122,07 122,08 122,09 122,10 122,11 122,12 Data.Text(anglès)
- ↑ 123,0 123,1 Data.Text - Tipus intern del Arxivat 2013-06-15 a Wayback Machine.(anglès)
- ↑ 124,0 124,1 124,2 124,3 124,4 Data.Monoid(anglès)
- ↑ 125,0 125,1 125,2 125,3 125,4 125,5 125,6 125,7 125,8 Data.Array
- ↑ Data.Array.Unboxed
- ↑ 127,00 127,01 127,02 127,03 127,04 127,05 127,06 127,07 127,08 127,09 Data.Vector(anglès)
- ↑ 128,0 128,1 128,2 128,3 128,4 Data.Vector.Mutable(anglès)
- ↑ 129,0 129,1 129,2 129,3 129,4 129,5 Data.Array.IArray
- ↑ 130,0 130,1 130,2 130,3 130,4 130,5 Data.Array.MArray
- ↑ 131,0 131,1 131,2 131,3 131,4 131,5 131,6 131,7 Data.Tree
- ↑ 132,0 132,1 132,2 paquet mono-traversable
- ↑ 133,0 133,1 133,2 133,3 Data.Foldable
- ↑ Data.Map.(!)(anglès)
- ↑ HaskellWiki Debugging(anglès)
- ↑ 136,0 136,1 136,2 Data.Functor
- ↑ Data.List.union
- ↑ Control.Monad.mfilter
- ↑ Data.Sequence.breakl(anglès)
- ↑ La classe Show(anglès)
- ↑ La classe Read(anglès)
- ↑ La classe Data(anglès)
- ↑ La classe Generic(anglès)
- ↑ La classe NFData(anglès)
- ↑ La classe Binary(anglès)
- ↑ paquet mono-traversable(anglès)
- ↑ Data.Traversable
- ↑ Functor-Applicative-Monad Proposal(anglès)
- ↑ HaskellWiki - MonadPlus(anglès)
- ↑ HaskellWiki - MonadPlus reform proposal(anglès)
- ↑ Data.Binary(anglès) Serialització
- ↑ Control.DeepSeq(anglès)
- ↑ 153,0 153,1 Data.Data(anglès)
- ↑ Data.Eq(anglès)
- ↑ Data.Ord(anglès)
- ↑ Text.Show(anglès)
- ↑ Text.Read(anglès)
- ↑ Data.Binary.Generic(anglès)
- ↑ Extensió NumDecimals Arxivat 2018-08-21 a Wayback Machine.(anglès)
- ↑ 160,0 160,1 160,2 Data.String
- ↑ 161,0 161,1 Extensió OverloadedStrings[Enllaç no actiu](anglès)
- ↑ 162,0 162,1 La classe IsList(anglès)
- ↑ 163,0 163,1 Extensió OverloadedLists[Enllaç no actiu](anglès)
- ↑ El paquet bytestring(anglès)
- ↑ HaskellWiki - Orphan instance(anglès)
- ↑ Opcions i variables predefinides per al CPP Arxivat 2014-02-02 a Wayback Machine.(anglès)
- ↑ Compilació condicional Arxivat 2014-02-02 a Wayback Machine.(anglès)
- ↑ GHC version numbering policy Arxivat 2016-05-09 a Wayback Machine.(anglès) Política de numeració de versions de GHC
- ↑ HaskellWiki - Compatibility modules
- ↑ Alex: A lexical analyser generator for Haskell(anglès)
- ↑ Happy: The Parser Generator for Haskell(anglès)
- ↑ hsc2hs(anglès)
- ↑ Writing Haskell interfaces to C code: hsc2hs(anglès)
- ↑ Haskell FFI tutorial example(anglès)
- ↑ GreenCard - generador de codi d'enllaç amb biblioteques en C(anglès)
- ↑ GreenCard: a foreign language interface for Haskell
- ↑ https://github.com/haskell/c2hs c2hs
- ↑ Type Holes(anglès)
- ↑ Depuració(anglès)
- ↑ 180,0 180,1 HaskellWiki - Stack overflow
- ↑ HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
- ↑ Finding the needle: Stack Traces for GHC - Tristan Allwood, Simon Peyton-Jones and Susan Eisenbach(anglès)
- ↑ HaskellWiki - Stack trace(anglès)
- ↑ errorWithStackTrace de GHC.Stack (anglès)
- ↑ GHC - RTS Configurations (anglès)
- ↑ GHC - Opcions de perfilat Arxivat 2014-07-14 a Wayback Machine. (anglès)
- ↑ Stack Traces in GHCi(anglès)
- ↑ Catching Exceptions(anglès)
- ↑ El paquet exceptions aporta excepcions no limitades a la mònada IO(anglès)
- ↑ «GHC users guide - The GHCi Debugger». Arxivat de l'original el 2011-03-03. [Consulta: 10 maig 2014].
- ↑ No more exceptions: debugging Haskell code with GHCi(anglès)
- ↑ El fitxer ".ghci" Arxivat 2014-07-25 a Wayback Machine.(anglès)
- ↑ Notes de la versió 7.10.2(anglès)
- ↑ Special implicit parameters Arxivat 2015-09-05 a Wayback Machine.(anglès) Pila de crides com a paràmetre implícit
- ↑ la biblio. file-location(anglès)
- ↑ HaskellWiki - Debugging - Locating a failure in a library function(anglès)
- ↑ La biblioteca Safe(anglès)
- ↑ GHC flag reference Arxivat 2014-09-18 a Wayback Machine.(anglès)
- ↑ La biblio classy-prelude(anglès)
- ↑ La biblio mono-traversable(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
- ↑ Mòdul Debug.Trace(anglès)
- ↑ EventLog Tracing(anglès)
- ↑ HaskellWiki EventLog(anglès)
- ↑ 206,0 206,1 GHC users guide - RTS EventLog Arxivat 2010-04-02 a Wayback Machine.(anglès)
- ↑ Variables globals(anglès)
- ↑ La mònada Reader
- ↑ HaskellWiki - Top level mutable state
- ↑ Una solució al problema de les configuracions.
- ↑ paquet Seal-module
- ↑ Implicit configurations
- ↑ 213,0 213,1 213,2 213,3 System.IO.Unsafe (anglès)
- ↑ 214,0 214,1 IO inside: The dark side of the IO Monad(anglès)
- ↑ Hack - In computer science(anglès)
- ↑ The Reader monad(anglès)
- ↑ Retainer profiling
- ↑ La biblioteca deepseq(anglès)
- ↑ El paquet deepseq-th(anglès)
- ↑ HaskellWiki - Memory leak(anglès)
- ↑ .Edward Z. Yang - Space leak zoo(anglès)
- ↑ Profiling (ajustatge)(anglès)
- ↑ An Introduction to Cabal sandboxes Arxivat 2014-09-25 a Wayback Machine.(anglès)
- ↑ HaskellWiki - Stackage(anglès)
- ↑ El servidor Stackage
- ↑ LTS Haskell: Versionar l'ecosistema(anglès)
- ↑ stackage.org - LTS actual(anglès)
- ↑ What's the point of Stackage LTS?(anglès)
- ↑ yesodweb.com - Stable, Vetted Hackage(anglès)
- ↑ FPComplete Haskell Center Arxivat 2014-07-16 a Wayback Machine.(anglès)
- ↑ haskellstack.org(anglès)
- ↑ Sooner: producing a program more quickly Arxivat 2017-08-22 a Wayback Machine.(anglès)
- ↑ Faster: producing a program that runs quicker Arxivat 2017-08-22 a Wayback Machine.(anglès)
- ↑ Smaller: producing a program that is smaller Arxivat 2017-08-22 a Wayback Machine.(anglès)
- ↑ Thriftier: producing a program that gobbles less heap space Arxivat 2017-08-22 a Wayback Machine.(anglès)
- ↑ «Tipus unboxed (allotjament directe) - Restriccions». Arxivat de l'original el 2013-08-09. [Consulta: 12 desembre 2013].
- ↑ Tipus amb allotjament directe (ang:unboxed) i primitius Arxivat 2013-08-09 a Wayback Machine. (anglès)
- ↑ «Pragma UNPACK». Arxivat de l'original el 2014-01-25. [Consulta: 12 desembre 2013].
- ↑ Notes de la versió 7.8.1[Enllaç no actiu]
- ↑ «Tuples unboxed.». Arxivat de l'original el 2013-08-09. [Consulta: 12 desembre 2013].
- ↑ Pragmes del Haskell98(anglès)
- ↑ haskellWiki - Safe Haskell(anglès)
- ↑ GHC users guide - Safe Haskell Arxivat 2016-03-14 a Wayback Machine.(anglès)
- ↑ Compilador Frege
- ↑ Referència del llenguatge Frege(anglès)
- ↑ HaskellWiki - The JavaScript Problem(anglès)
- ↑ 247,0 247,1 Haste: Running Haskell in the Browser
- ↑ Try Haste(anglès)
- ↑ Haste report - Towards a Declarative Web(anglès)
- ↑ GitHub - valderman/haste-compiler(anglès)
- ↑ GitHub - ghcjs(anglès)
- ↑ GHCJS, Concurrent Haskell in the Browser(anglès)
- ↑ GHC - Procés de generació de codi(anglès)
- ↑ Paquet hlint que genera el programa del mateix nom(anglès)
- ↑ Cabal-dev - Sandboxed development builds for Haskell Arxivat 2011-01-28 a Wayback Machine.(anglès)
- ↑ cabal-meta (anglès)
- ↑ cabal-ghci (anglès)
- ↑ yackage (anglès)
- ↑ Threadscope
- ↑ «Ajustatge fi del paral·lelisme amb ThreadScope». Arxivat de l'original el 2009-12-29. [Consulta: 7 setembre 2009].
- ↑ Haskell Program Coverage Arxivat 2009-10-25 a Wayback Machine.(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)
- ↑ Liquid Types(anglès)
- ↑ HaskellWiki - Liquid Haskell(anglès)
- ↑ LiquidHaskell README(anglès)
- ↑ Refinement types Arxivat 2014-12-05 a Wayback Machine.(anglès)
- ↑ 268,0 268,1 Refinement types for Haskell(anglès)
- ↑ L'estàndard SMT(anglès)
- ↑ Real world Liquid(anglès)
- ↑ LiquidHaskell Blog
- ↑ MathSAT(anglès)
- ↑ Guia d'Opcions de l'intèrpret d'ordres en Haskell Arxivat 2013-09-29 a Wayback Machine.(anglès)
- ↑ API System.Console.GetOpt amb exemples(anglès)
- ↑ Neil Mitchell - Intèrpret d'ordres, exemples d'arguments(anglès)
- ↑ The Exception type (anglès)
- ↑ GHC extra - Seleccions estil SQL a les llistes Arxivat 2005-11-29 a Wayback Machine.(anglès)
- ↑ paquet vector-algorithms(anglès)
- ↑ El paquet unordered-containers de la plataforma Haskell, aporta HashMap i HashSet(anglès)
- ↑ Haskell wiki - Regular expressions(anglès)
- ↑ paquet aeson(anglès)
- ↑ Data.Tree(anglès)
- ↑ Gtk2hs(anglès)
- ↑ Per què serveix el símbol ___gxx_personality_v0(anglès)
- ↑ WxHaskell(anglès)
- ↑ wxWidgets(anglès)
- ↑ Van Laarhoven - CPS Functional References(anglès)
- ↑ Eduard Kmett - Lens wiki - Overview(anglès)
- ↑ The lens library(anglès)
- ↑ HaskellWiki - Lens(anglès)
- ↑ El paquet lens(anglès)
- ↑ Haskell wiki - Dependent type (anglès)
- ↑ Haskell wiki - Type arithmetic(anglès)
- ↑ Paquet type-level-natural-number(anglès)
- ↑ Paquet numtype-tf(anglès)
- ↑ dimensional-tf(anglès)
- ↑ Haskell Trac - TypeNats(anglès)
- ↑ GHC Plugins(anglès)
- ↑ paquet type-nat-solver al GitHub(anglès)
- ↑ TypeNats example(anglès)
- ↑ Estàndard Haskell2010 - Signed integer types(anglès)
- ↑ GHC floatRange(anglès)
- ↑ 303,0 303,1 Mòduls mútuament recursius(anglès)
Vegeu també
- 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. Actualment eta-lang.org el supera, doncs no altera el llenguatge haskell (utilitza un GHC modificat anomenat eta).
- Idris (llenguatge de programació) - lleng. quasi-Haskell amb tipus dependents de valors, tipus com a objectes de primer ordre, i aportació de proves inductives (demostració de teoremes). Avaluació estricta.
- Elm (llenguatge de programació) - lleng. quasi-Haskell de programació funcional reactiva amb sortida a JavaScript, d'avaluació estricta per la generació d'entorns gràfics que varien segons els esdeveniments i canvis de valor.
Enllaços externs
- Pàgina oficial de GHC (anglès)
- GHC - Guia d'usuari (anglès)
- El Run Time System del GHC (anglès)
- Cadarache-2012 Arxivat 2012-12-25 a Wayback Machine. (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