Compilador Haskell de Glasgow

De Viquipèdia
(S'ha redirigit des de: Glasgow Haskell Compiler)
Salta a: navegació, cerca
Infotaula de programariCompilador Haskell de Glasgow
Desenvolupador(s) Universitat de Glasgow
Versió inicial 1989
Escrit en C i Haskell
Sistema operatiu GNU, GNU/Linux, FreeBSD, NetBSD, OpenBSD, Solaris, macOS i Microsoft Windows
Tipus compilador i programari lliure
Llicència 3-clause BSD License
Més informació
Lloc web Web oficial
Codi font Codi font
GitHub ghc
Modifica dades a Wikidata

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.[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 vénen 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]

Contingut

Compilar codi antic Haskell98 amb GHC[modifica]

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 utilitza un tipus Exception, unió discriminada dels diferents tipus d'excepcions
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 utilitza una classe Exception, i un tipus existencial SomeException com a excepció genèrica, alhora que permet instanciar la classe Exception en tipus definits per l'usuari que implementin Show i Typeable (podem fer que el compilador les derivi automàticament incloent-les en una clàusula deriving si esmentem l'extensió DeriveDataTypeable).
data SomeException = forall e. Exception e => SomeException 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,[11] s'hi van conservar fins a la versió 7.4.2[12] i ha estat eliminat a la versió GHC-7.6.1.[13]

Primer de tot cal descarregar[14] 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-instaŀlacio
make install

# establir variables d'entorn
GHC_VER=7.4.2
GHC_HOME=$HOME/carpeta-d-instaŀ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[modifica]

Vegeu doc.[15] GHC compila per defecte amb memòria dinàmica il·limitada. Per establir-ne límits vegeu el senyal de compilació -M.[16] 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ó.[17]

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

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.[19] després passen a la generació 0, i següents[20] tantes com s'estableixi a les opcions de l'intèrpret d'ordres per al RunTimeSystem.

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

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

Fent servir biblioteques de relligat dinàmic[modifica]

A partir de GHC >= 6.12[26][27][28] Cal que GHC s'hagi compilat amb ./configure --enable-shared

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

 ghc --make -dynamic Main.hs

Paral·lelisme[modifica]

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

Paraŀlelisme de tasques[modifica]

Primitives de paraŀlelisme - Compilació per a processadors multicor[modifica]

Vegeu-ho a Haskell concurrent#Primitives de paraŀlelisme - Compilació per a processadors multicor

Futurs[modifica]

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

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

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

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

Paral·lelisme de dades[modifica]

Regular parallel arrays[modifica]

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

Data Parallel Haskell[modifica]

GHC implementa el, més complex, paral·lelisme de dades niuat, trad. de "Nested Data Parallelism"[33][34][35] basat en treballs de Guy E. Blelloch.[36]

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

cabal install dph-examples

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

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

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

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

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

Vegeu exemple més avall.

SIMD - Single Instruction, Multiple Data - Ús de les instruccions de CPU vectoritzades[modifica]

Aprofitament de les instruccions de dades vectoritzades dels processadors (MMX, SSE, Altivec, AVX, ...) .

Tipus i operacions primitius SIMD a GHC.Prim.[37]

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

Paquets d'interfície SIMD: simd,[39] primitive-simd.[40]

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

dóna:

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]

MIMD - Multiple Instruction, Multiple Data - Paral·lelisme GPGPU o bé a CPUs multicor[modifica]

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[41][42] amb suport per a rerefons CUDA,[43] OpenCL[44] i RePa[45] (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.[41]
  • Biblio. OpenCLWrappers facilita l'ús de lligams amb biblioteques externes que implementin l'estàndard OpenCL,[46] article: OpenCL from Haskell[47]
  • Simultanejant càrregues de treball en CPU i GPU. Vegeu "How to write hybrid CPU/GPU programs with Haskell".[48]
Exemple d'ús de la biblioteca Accelerate[modifica]
  • Mòdul principal amb intèrpret de simulació del llenguatge incrustat (EDSL: "embedded domain specific language").[49]
  • Mòduls per al paral·lelisme a GPUs
  • RerefonsCUDA de NVIDIA.[50] Requereix tenir instaŀlat el compilador NVCC del ToolKit de NVIDIA.[51]
  • Rerefons OpenCL de HIPERFIT.[52] Cal tenir instaŀlat el kit de desenvolupament (SDK) del fabricant de la GPU. L'estàndard OpenCL està implementat tant per NVIDIA[53] 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.[54]
  • Rerefons OpenCL basat en el llenguatge per al paral·lelisme Icc-Cilk de Intel.[55]
  • Mòduls per al paral·lelisme a CPUs multicor
  • Rerefons biblioteca RePa (Regular Parallel Arrays).[56]
  • Rerefons via LLVM.[57]
cabal install accelerate         # instaŀla paquet base amb simulador (intèrpret del llenguatge de domini específic)

# si teniu targeta gràfica NVIDIA amb capacitat GPGPU i heu instaŀlat el kit de desenvolupament CUDA
cabal install cuda \ 
         --extra-include-dirs=/usr/local/cuda-7.0/include \ 
         --extra-lib-dirs=/usr/local/cuda-7.0/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

-- tipus de paquet de procés paraŀlel

#define PROC_PAR_CUDA 1              /* maquinari NVIDIA */
#define PROC_PAR_HIPERFIT_OPENCL 2   /* maquinari que suporta OPENCL */
#define PROC_PAR_INTEL_OPENCL 3      /* maquinari INTEL */ 
#define PROC_PAR_INTERPRET 0         /* sense maquinari de procés paraŀlel (simulació per l'intèrpret) */

#define PAQUET_PROC_PAR PROC_PAR_CUDA

#if PAQUET_PROC_PAR == PROC_PAR_CUDA
import qualified "accelerate-cuda" Data.Array.Accelerate.CUDA as ProcPar

#elif PAQUET_PROC_PAR == PROC_PAR_HIPERFIT_OPENCL
import qualified "accelerate-opencl" Data.Array.Accelerate.OpenCL as ProcPar

#elif PAQUET_PROC_PAR == PROC_PAR_INTEL_OPENCL
import qualified "accelerate-icc-opencl" Data.Array.Accelerate.OpenCL as ProcPar

#elif PAQUET_PROC_PAR == PROC_PAR_INTERPRET
-- intèrpret del llenguatge simulant un procés paraŀlel (per manca de maquinari)
import qualified "accelerate" Data.Array.Accelerate.Interpreter as ProcPar
#endif

-- 'use' carrega un paràmetre (vector o bé escalar) a la memòria del dispositiu accelerador (context 'Acc')
-- use :: a -> Acc a

-- 'run' executa el càlcul en el dispositiu accelerador, i descarrega el resultat des de la memòria del dispositiu a la principal
-- run :: Acc a -> a

-- 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 del RunTimeSystem

$ ghc --make -threaded prova.hs
$ ./prova
Array (Z) [6.0]

Concurrència[modifica]

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

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

Avaluació en temps de compilació[modifica]

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

Quasi-Quotations (Plantilles)[modifica]

Construcció[59] 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.[60]

Sintaxi
Les acotacions QuasiQuotation utilitzen una sintaxi denominada Claus d'Oxford [|... |] o bé [quasiQuoter|... |] on el nom del prefix indica el tipus que aporta el traductor del text acotat per les claus.[61]
Context
Una acotació QuasiQuotation pot descriure, segons el lloc on s'utilitza, una expressió, o bé un patró, o bé un tipus, o bé una llista de declaracions a nivell de mòdul
Tipus
Un tipus quasiQuoter es defineix com un registre amb traductors opcionals per cadascun dels 4 possibles contextos en què es pretengui utilitzar assignant als altres el valor undefined.[62]
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ó).[63][64][65]
data QuasiQuoter = QuasiQuoter {
                       quoteExp  :: String  Q Exp,  -- traductor d'una plantilla en context d'expressions (metaprogramació d'expressions)
                       quotePat  :: String  Q Pat,  -- traductor d'una plantilla en context de patrons
                       quoteType :: String  Q Type, -- traductor d'una plantilla en context de tipus
                       quoteDec  :: String  Q [Dec] -- traductor d'una plantilla en context de declaracions
                       }

Exemple senzill: document tot seguit[modifica]

Traductor per incloure en expressions, textos multilínia (estil hereDoc o document-tot-seguit), com a literals, amb la funció stringE de T.H.[66]

  • 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 #-}
module ElMeuDocTotSeguit where
  import Prelude (undefined)
  import Language.Haskell.TH (stringE)
  import Language.Haskell.TH.Quote (QuasiQuoter(..))

  docTotSeguit = QuasiQuoter stringE undefined undefined undefined     -- 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[modifica]

De les biblioteques del Yesod.[60]

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 ^{...} inclouen expressions del mateix tipus que el de la plantilla que l'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  #{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ó[modifica]

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

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

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

Stream fusion[modifica]

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

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

Les funcions com ara els filtres s'han d'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ó.[69][70]

Vegeu biblioteca Stream-fusion al rebost Hackage[71]

Referència ràpida de la biblioteca[modifica]

Vegeu l'API del GHC[72] i la de la plataforma[9] Haskell.

contenidors[modifica]

Vegeu ref.[73]

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

notes[modifica]

llistes amb optimització de fusió[modifica]
  • el paquet stream-fusion[71] aporta llistes amb funcions fusionables pel mètode stream fusion.
conjunts i diccionaris[modifica]
  • Set ε[74] i Map κ ε[75] del paquet "containers" implementen conjunts i diccionaris basats en arbres de cerca binària balancejats amb cost de cerca O(log n).
  • IntSet[76] i IntMap ε[77] 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[78] aporta una implementació de conjunts[79] i diccionaris[80] 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)[81] i desant al conjunt/diccionari o bé un sol element/parell o bé, en cas de col·lisions, un cistell dels elements/parells quines claus col·lideixen, implementat com a Set/Map del paquet containers. El paquet hashmap ha quedat desaconsellat en favor del paquet unordered-containers.
  • el paquet unordered-containers[82] implementa conjunts[83] i diccionaris[84] (HashSet i HashMap) quines claus han d'implementar una funció resum (classe Hashable) però no cal que implementin Ord (ordenable). El cistell de coŀlisions en té prou amb la implementació de Eq per distingir-les.
seqüències de dos caps, amb accés més ràpid que les llistes[modifica]
  • Seq ε de Data.Sequence[85] 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[modifica]
  • El tipus Array shape ε del paquet Repa[31][86] 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[modifica]
  • Vector ε i MVector σ ε del paquet Vector[87] aporten vectors immutables i mudables basats en famílies de tipus amb índexos Int basats en 0, que permeten implementacions més flexibles, i, a banda, funcions fusionables (stream fusion per la composició).[88][89] Per a vectors de tipus primitius el mòdul Data.Vector.Unboxed aporta vectors immutables i mudables d'elements no encapsulats.[90]
  • hi ha paquets relacionats al Hackage 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 basada en tipus genèrics monomòrfics[modifica]

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[91] aporta un conjunt de classes genèriques, que sobrepassen la funcionalitat de les de la biblio. estàndard, però d'aplicació universal, tractant el tipus de l'element de la col·lecció mitjançant famílies de tipus.

A més a més aporta instàncies dels TADs per a les estructures més utilitzades relacionant-hi les operacions.

  • MonoPointed:[92] operacions per elevar un element a la categoria de contenidor monomòrfic amb l'element d'entrada.
  • MonoFunctor:[93] Functor sobre contenidors monomòrfics.
  • MonoZip:[94] operacions de combinació per posició (zips) de contenidors monomòrfics. Requereix MonoFunctor
  • MonoFoldable:[95] Catamorfismes sobre contenidors monomòrfics.
  • MonoTraversable:[96] Operacions en contenidors monomòrfics que es poden travessar seqüencialment d'esquerra a dreta, avaluant-ne les accions corresponents als elements designades per una funció d'efectes, o bé els mateixos elements si són efectes.
  • SemiSequence:[97] Seqüències en Semigrups (consultes i (re)composició {find, sortBy, cons, snoc, reverse, intersperse})
  • IsSequence:[98] Amplia SemiSequence, afegint les operacions que requereixen Monoide. Requereix SemiSequence, Monoid, MonoPointed, MonoTraversable
  • Textual:[99] TAD per a seqüències de caràcters {words, unwords, lines, unlines, toUpper, toLower, ...}
  • Utf8:[100] TAD per a seqüències textuals codificades en UTF-8
  • NonNull:[101] tipus per embolcallar estructures no nuŀles i accedir-hi amb la seguretat que l'estructura no serà buida.
  • SetContainer:[102] TAD amb les operacions comunes als contenidors d'elements distingits per una clau. (tipus de la clau, consultes de la clau {member, notMember, keys}, combinació {union, unions, difference, intersection})
  • IsSet:[103] TAD que amplia SetContainer amb les operacions de creació, actualització i volcat a llista, als conjunts. Requereix SetContainer.
  • IsMap:[104] TAD que amplia SetContainer amb les operacions de creació, actualització, volcat a llista i consulta de valor als 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' de llargada fixa n 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 TypeFamilies #-}
import Data.Sequences as S
import Data.MonoTraversable as M
import Data.Function ((&)) -- (&): aplicació cap enrere
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 (parteixIComprova n) seq

-- desplega equival a 'unfoldr' a les llistes
desplega :: (IsSequence t, Element t ~ a) => (b -> Maybe (a, b)) -> b -> t
desplega f estat = case f estat of
                Just (x, estat') -> S.cons x $ desplega f estat'
                Nothing -> mempty        -- IsSequence implica Monoid

parteixIComprova :: IsSequence t => Index t -> t -> Maybe (t, t)
parteixIComprova n seq = S.splitAt n seq
                            & Just
                            & mfilter (not. M.onull. fst) -- si el trosset era buit 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[modifica]
  • el paquet bytestring-trie[105] aporta una implementació de diccionaris com a arbres de prefix (Tries) per a claus de tipus ByteString
  • el paquet dlist[106] aporta llistes per diferència que redueixen el cost de concatenació en cas d'aniuament d'expressions Vegeu ref.[107]
  • el paquet heap[108] 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,[109] a partir de GHC 8.0 queda integrat al paquet base. Aporta la classe Semigroup[110] i Data.List.NonEmpty per a llistes no buides.[111]
module Data.List.NonEmpty
...
data NonEmpty a = a :| [a]     -- infixr 5

-- afegeix pel davant
(<|), cons :: a -> NonEmpty a -> NonEmpty a
...

generació[modifica]

  • Contenidors d'opcionalitat
opcionals - generació
paquet tipus context sense valor
o bé error
amb valor de llista o funció generadora
base Maybe ε[112] Nothing Just x listToMaybe llista
Either tipError ε[113] 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 [ε][114] [ ] [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 ε[111] 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 ε[85] empty singleton x fromList llista
bytestring ByteString[115]
-- UArray Word8
empty singleton x pack llistaDeBytes
text
-- ∈ H. Platform[9]
Text[116]
-- ByteString codif. UTF16
indexat com [Word16][117]
empty singleton ch pack string
classes interfície
base Monoid[118] 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 ι ε[119]
-- immutable amb
allotj. indirecte dels elements
(Ix ι) listArray (iMin, iMax) llista
array (iMin, iMax) llistaD'Associacions
UArray ι ε[120]
-- immutable amb
allotj. directe
-- via interfície IArray
vector
(tipus Índex = Int)
Vector ε[121]
-- immutable amb
allotj. directe
empty singleton x fromList llista
replicate n x
generate llarg (f :: Índex → ε)
MVector σ ε[122]
-- mudable amb
allotj. directe
Vector.thaw ivector -- descongela immutable
replicate n x
replicateM n acció
clone mvector
repa multidim.
Array sh ε[86]
(Shape sh,
Elt ε)
singleton x
  :: Array DIM0 ε
fromList shape llista
classes interfícies
array IArray α ε[123]
-- 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[124]
-- 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[76] empty singleton x fromList llista fromAscList llista
IntMap ε[77] empty singleton clau x fromList llistaD'Associacions fromAscList llistaD'Associacions
containers Set ε[74] (Ord ε) empty singleton x fromList llista fromAscList llista
Map κ ε[75] (Ord κ) empty singleton clau x fromList llistaD'Associacions fromAscList llistaD'Associacions
unordered-containers HashSet ε[83] (Hashable ε, Eq ε) empty singleton x fromList llista
HashMap κ ε[84] (Hashable κ, Eq κ) empty singleton clau x fromList llistaD'Associacions
bytestring-trie Trie ε[105] empty singleton bytestringClau x fromList llistaD'Associacions
containers Tree ε[125] Node x [ ] unfoldTree f llavor
heap[108] {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 genèrics de mono-traversable
classes genèriques de mono-traversable -- generació
paquet tipus/classe context amb un elem. de llista o funció generadora
mono-traversable[126]
ε ~ Element t
SemiSequence t[97] singleton ε
IsSequence t[98] fromList
NonNull t[101] fromNullable --resultat opcional
MonoPointed t[92] opoint ε

consulta[modifica]

opcionals - consulta
(en vermell si parcialment definides, criden a error)
tipus context és
buit?
mida pertinença obtenir elem components altres
Maybe ε[112] isNothing fromEnum . isJust (==). Just -- encaix
Nothing | Just x
maybeToList
fromJust
fromMaybe default
--retornant elements
catMaybes llistaDeMaybes
Either tipError ε[113] isLeft fromEnum . isRight (==). Right -- encaix
Left error | Right x
--retornant elements
lefts llistaDeEithers
rights llistaDeEithers
  • fromJust dispara error si la Maybe era buida ; fromMaybe és una versió segura (amb valor per defecte per als casos sense valor) de fromJust
seqüències - consulta
(en vermell si parcialment definides, criden a error)
tipus context és
buit?
mida pertinença obtenir elem components altres
[ε][114] null length elem x find predicat:: Maybe ε
-- encaix
[ ] | (x : xs)
head
tail
subsequences
permutations
init
last
NonEmpty ε[111] length elem x uncons xs :: (ε, Maybe (NonEmpty ε))
-- encaix
(x :| llista)
head
tail
init
last
Seq ε[85] null length (=/ Nothing). (elemIndex{L|R} x) -- encaix de viewl seq
EmptyL | (x :< xseq)
-- encaix de viewr seq
EmptyR | (x :> xseq)
-- via Foldable
ByteString[115]
-- 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[116]
-- ByteString codif. UTF16
indexat com [Word16][117]
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[127] 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).[128][129]

vectors - consulta
tipus context és
buit?
mida pertinença obtenir elem components altres
Array ι ε[119] (Ix ι) bounds—límits de l'índex
indices
elems
assocs—parells (índex, valor)
Vector ε[121] (Eq ε) null length elem x find predicat toList
head
tail
init
last
MVector σ ε[122] null length Vector.freeze --cap a Vector
multidim. repa
Array sh ε[86]
(Shape sh,
Elt ε)
size . extent toList
toScalar -- cas de shape DIM0
extent
  ::(Shape sh)
classes interfícies
IArray α ε[123]
-- immutables
bounds
indices
elems
assocs
MArray α ε m[124]
-- mudables
(Monad m) getBounds
getElems
getAssocs
estructures - consulta
tipus context és
buit?
mida pertinença obtenir elem components altres
Set ε[74]
IntSet[76]
null size elems
(Ord ε) member x -- cerca aproximació
lookup{LT,GT,LE,GE} x
toAscList
HashSet ε[83] (Hashable ε, Eq ε) null size member x toList
Map κ ε[75]
IntMap ε[77]
null size keys
keysSet
elems
assocs
(Ord κ) member clau (! clau)
lookup clau -- :: Maybe ε
-- cerca aproximació
lookup{LT,GT,LE,GE} clau
toAscList
HashMap κ ε[84] (Hashable κ, Eq κ) null size member clau (! clau)
lookup clau -- :: Maybe ε
lookupDefault default clau -- :: ε
keys
elems
toList
Trie ε[105] null size member bytestringClau lookup bstrClau keys
elems
toList
submap bstrClau
Tree ε[125] rootLabel
subForest
-- llista en pre-ordre
flatten
-- elems per nivells
levels
{Min|Max}Heap ε[108] Ord ε null size -- encaix de view heap
Nothing | Just (x, heap)
:: Maybe (ε, {Min|Max}Heap ε)
toList
toDescList
toAscList
{Min|Max}PrioHeap prio val[108] 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
classes genèriques de mono-traversable -- consulta
tipus/classe context és
buit?
mida pertinença obtenir elem altres
MonoFoldable t[95] onull olength oelem ε headMay
lastMay
SemiSequence t[97] find predicat -- retorna :: Maybe (Element t)
NonNull t[101] MonoFoldable t head
last
IsSequence t tail
init

actualització / transformació[modifica]

opcionals - actualitza/transforma
tipus aplicació (map)
als elems.
altres
Maybe ε[112] -- via Functor maybe default (f::ε→b)
Either tipError ε[113] -- via Functor either (f::tipError→b) (g::ε→b)
classes interfícies
Functor[130] fmap (f::a → b)
seqüències - actualitza/transforma
transforma
tipus context afegeix elimina aplicació (map)
als elems.
altres transforma'n
una llista
[ε][114] (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 ε[85] (x <|) -- a l'esquerra
(|> x)—a la dreta
mapWithIndex (f:: Int → ε → b) reverse
ByteString[115] cons x -- al davant
snoc x -- al darrere
map (f:: Word8 → Word8) reverse
intersperse byte
transpose
intercalate bstr
Text[116] 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[130] fmap (f::a → b)
  • Els tipus monomòrfics (ByteString, Text i també IntSet) no implementen Functor, que requereix tipus amb kind * -> * (aritat del tipus == 1)
vectors - actualitza/transforma
transforma
tipus afegeix actualitza aplicació (map) altres
Array ι ε[119] (// llistaD'Associacions) -- via Functor
Vector ε[121] cons x -- pel davant
snoc x -- pel darrere
map (f:: a → b)
-- amb l'índex
imap (f:: Int → a → b)
reverse
MVector σ ε[122] (`set` x)
clear—desvincula refs.
-- copy dst src
(`copy` mvectorOrigen)
(`move` mvectorOrigen)
(`grow` n)
multidim. repa
Array sh ε[86]
map (f:: a → b) reshape shape
transpose
classes interfícies
IArray α ε[123] (// llistaD'Associacions) amap (f::a→b)
MArray α ε m[124] mapArray (f::a → b)
estructures - actualitza/transforma
transforma
tipus context afegeix elimina actualitza aplicació (map)
als elems.
Set ε[74]
IntSet[76]
(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 ε[83] (Hashable ε, Eq ε) insert x delete x insert x -- subst. l'existent map (f:: a → b)
Map κ ε[75]
IntMap ε[77]
(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_coŀlisions::a->a->a) (f::k1 -> k2)
HashMap κ ε[84] (Hashable κ, Eq κ) insert clau x delete clau adjust (f::a→a) clau map (f:: a → b)
mapWithKey (f:: κ → a → b)
Trie ε[105] insert bytestringClau delete bstrClau adjust f bstrClau mapBy (f:: ByteString → a → Maybe b)
filterMap (f:: a → Maybe b)
Tree ε[125] -- via Functor
{Min|Max}Heap ε[108] (Ord ε) insert x
{Min|Max}PrioHeap prio val[108] (Ord prio) insert (pri, v) -- via Functor
classes genèriques de mono-traversable -- actualitza/transforma
tipus/classe context afegeix elimina aplicació (map)
als elems.
altres
(ε = Element t)
SemiSequence t[97] cons -- pel davant
snoc -- pel darrera
intersperse
reverse
sortBy (f::ε->ε->Ordering)
IsSequence t[98] Eq (Element t) delete ε
Ord (Element t) sort
MonoFunctor t[93] omap (f::ε->ε)

Nota per les funcions d'actualització als diccionaris (pròpiament taules associatives) Data.Map i Data.IntMap:

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

seqüències - accés indexat
en color si generen error, en marró si és
previsible: domini acotat i continu: length O(1) i índex ∈ [0,length)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
:: Maybe Int
cerca índexs
::[Int]
elimina actualitza
[ε][114] (!! índx) elemIndex x
findIndex predicat
elemIndices x
findIndices predicat
Seq ε[85] (`índex` índx) elemIndexL x -- per l'esquerra
findIndexL predicat
elemIndexR x --per la dreta
findIndexR predicat
elemIndices{L|R} x
findIndices{L|R} predicat
adjust f índx
update índx x
ByteString[115] (`índex` índx) elemIndex x -- pel davant
elemIndexEnd x -- pel darrera
findIndex predicat
elemIndices x
findIndices predicat
Text[116] (`índex` índx) findIndex predicat
vectors - accés indexat
en color si generen error, en marró si és
previsible: domini acotat i continu: length O(1) i índex ∈ [0,length)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
:: Maybe Int
cerca índexs actualitza
Array ι ε[119] (! índx)
Vector ε[121] (! índx)
(!? índx) -- :: Maybe ε
elemIndex x
findIndex predicat
-- retornen :: Vector Int
elemIndices x
findIndices predicat
MVector σ ε[122] (`read` indx) (`write` indx x)
(`swap` i j)
multidim. repa
Array sh ε[86]
(Shape sh,
Elt ε)
(! índxMultiDim)
(`índex` índxMultiDim)
(`safeIndex` índxMultiDim)
classes interfícies
IArray α ε[123] (! índx)
MArray α ε m[124] (Monad m) (`readArray` indx) (`writeArray` indx x)
estructures - accés indexat (índex basat en zero sobre seq. ordenada)
en color si generen error, en marró si és
previsible: domini acotat i continu: size O(1) i índex ∈ [0,size)
tipus context elem. a
l'índex
cerca l'índex
de l'elem.
elimina actualitza
Set ε[74] (Ord ε) elemAt índx
retorna :: ε
findIndex valor -- :: Int
lookupIndex valor -- :: Maybe Int
deleteAt índx
Map κ ε[75] (Ord κ) elemAt índx
retorna :: (κ,ε)
findIndex clau -- :: Int
lookupIndex clau -- :: Maybe Int
deleteAt índx updateAt f índx
  • classes genèriques de mono-traversable[126] -- accés indexat
-- Del mòdul Data.Sequences del paquet ''mono-traversable''
-- les funcions parcials (poden disparar ''error'') duen el sufix Ex
-- els tipus de l'índex i de l'element es defineixen com a famílies de sinònims de tipus

indexEx :: IsSequence seq => seq -> Index seq -> Element seq   -- funció parcial !!

index :: IsSequence seq => seq -> Index seq -> Maybe (Element seq) -- resultat opcional

combinació[modifica]

combina (monoides i zips)
tipus context compon compon-ne
una llista
combina per posició
(zips)
zip = zipWith (,)
desacobla llista de tuples
(unzips)
Maybe ε[112] -- via Monoid -- via Monoid
[ε][114] (++) concat (`zip` llista)
(`zip{3..7}` llista...)
zipWith (f::a→b→c) llista1 llista2
zipWith{3..7} f llista...
unzip
unzip{3..7}
Seq ε[85] (><) (`zip` seq)
(`zip{3..4}` seq...)
zipWith (f::a→b→c) seq1 seq2
zipWith{3..4} f seq...
ByteString[115] append concat (`zip` byteStr)
zipWith (f::Word8→Word8→a) byteStr1 byteStr2
unzip
Text[116] append concat (`zip` txt)
zipWith (f::Char→Char→Char) txt1 txt2
Set ε[74]
IntSet[76]
(Ord ε) union unions
HashSet ε[83] (Hashable ε, Eq ε) union unions
Map κ ε[75]
IntMap ε[77]
(Ord κ) union
-- amb func. combinació
unionWith (f::ε -> ε -> ε)
unionWithKey (f::κ -> ε -> ε -> ε)
unions
-- amb func. combinació
unionsWith (f::ε -> ε -> ε)
HashMap κ ε[84] (Hashable κ, Eq κ) union
-- amb func. combinació
unionWith (f::ε -> ε -> ε)
unions
Trie ε[105] union{L|R}
mergeBy (f::ε -> ε -> Maybe ε)
-- via Monoid
Tree ε[125]
{Min|Max}Heap ε[108] (Ord ε) union unions
{Min|Max}PrioHeap prio val[108] (Ord prio) union unions
Array ι ε[119]
Vector ε[121] (++) concat (`zip` vect)
(`zip{3..6}` vect)
zipWith (f::a→b→c) vect1 vect2
zipWith{3..6} f vect...
-- amb l'índex a la funció
izipWith (f::Int→a→b→c) vect1 vect2
izipWith{3..6} f vect...
unzip
unzip{3..6}
multidim. repa
Array sh ε[86]
(++)
append
classes interfícies
Monoid[118] mappend mconcat
IArray α ε[123]
MArray α ε m[124]
combina com a conjunt o bé multiconjunt
tipus context diferència unió uneix-ne
una llista
intersecció està contingut contingut
estrictament
obtenir
distints
[ε][114] (\\) -- multi union—vegeu ref.[131] intersect—multi (`isPrefixOf` llista)
(`isSuffixOf` llista)
(`isInfixOf` llista)
nub
Seq ε[85]
ByteString[115] (`isPrefixOf` byteStr)
(`isSuffixOf` byteStr)
(`isInfixOf` byteStr)
Text[116] (`isPrefixOf` txt)
(`isSuffixOf` txt)
(`isInfixOf` txt)
Set ε[74]
IntSet[76]
(Ord ε) (\\)
difference
union unions intersection isSubsetOf isProperSubsetOf
HashSet ε[83] (Hashable ε, Eq ε) difference union unions intersection
Map κ ε[75]
IntMap ε[77]
(Ord κ) (\\)
difference
union unions intersection isSubmapOf isProperSubmapOf
HashMap κ ε[75] (Hashable κ, Eq κ) difference union
unionWith (f::ε -> ε -> ε)
unions intersection
intersectionWith (f::ε -> ε -> ε)
Trie ε[105] unionL
unionR
{Min|Max}Heap ε[108] (Ord ε) union unions
{Min|Max}PrioHeap prio val[108] (Ord prio) union unions
  • classes genèriques de mono-traversable[126] -- 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))
elMeuZip = 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 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ó[modifica]

plegats
(en color si parcialment definides, criden a error)
tipus reducció successió de plegats parcials
per l'esquerra per la dreta per l'esquerra per la dreta
[ε][114] foldl (f::a→ε→a) inicial
foldl' f ini -- estricte
foldl1 (g::ε→ε→ε) -- sobre el primer elem.
foldr f ini
foldr1 g
scanl f ini
scanl1 g
scanr f ini
scanr1 g
NonEmpty ε[111] via Foldable via Foldable scanl f ini
scanl1 g
scanr f ini
scanr1 g
Seq ε[85] via Foldable via Foldable scanl f ini
scanl1 g
scanr f ini
scanr1 g
classes
Foldable t[127] 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.
opcionals - plegats especials
tipus llista de
contenidors
retornant [a] retornant ([a], [b])
Maybe ε[112] catMaybes
Either tipError ε[113] lefts
rights
-- (lefts, rights):
partitionEithers
plegats especials
tipus sobre
booleans
s/. predicat s/. ordre numèrics contenidor llista de
contenidors
contenidor
de llistes
índex del
menor/major
ε ~ Bool (Ord ε / κ) (Num ε) (Ord ε)
[ε][114] and
or
all predicat
any predicat
minimum
maximum
sum
product
concatMap (f::a→[b]) concat
ByteString[115] all predicat
any predicat
minimum
maximum
concatMap (f::Word8→ByteString) concat
Text[116] all predicat
any predicat
minimum
maximum
concatMap (f::Char→Text) concat
Vector ε[121] and
or
all predicat
any predicat
minimum
maximum
sum
product
concatMap (f::a → Vector b) concat minIndex
maxIndex
Set ε[74] -- via Foldable -- via Foldable findMin
findMax
-- via Foldable -- via Foldable -- via Monoid
IntSet[76] findMin
findMax
-- via Monoid
HashSet ε[83] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
Map κ ε[75]
IntMap ε[77]
-- via Foldable -- via Foldable -- min/max de la clau
findMin
findMax
-- via Foldable -- via Foldable -- via Monoid
HashMap κ ε[84] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
{Min|Max}PrioHeap prio val[108] -- via Foldable -- via Foldable -- via Foldable -- via Foldable -- via Monoid
classes
(Monoid m)
Foldable t[127] and
or
all predicat
any predicat
minimum
maximum
sum
product
foldMap (f::a→m)
concatMap (f::a→[b])
concat
Monoid t[118] 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 Kind (* -> *) (aritat del tipus == 1)
classes genèriques de mono-traversable -- plegats
(les funcions parcials que disparen error duen el sufix Ex)
tipus/classes reducció
ε = Element t per l'esquerra per la dreta
MonoFoldable t[95] ofoldl' (f::a→ε→a) ini -- estricte
ofoldl1Ex' (g::ε→ε→ε) -- sobre el primer elem.
ofoldr f ini
ofoldr1Ex g -- sobre el primer elem.
NonNull t[101] ofoldl1' (g::ε→ε→ε) -- sobre el primer elem. ofoldr1 g
classes genèriques de mono-traversable -- plegats especials
(funcions parcials amb sufix Ex disparen error
amb sufix May: resultat opcional)
tipus/classes sobre
booleans
s/. predicat s/. ordre numèrics contenidor contenidor
de monoides
ε = Element t ε ~ Bool (Ord ε) (Num ε) (Monoid m) (Monoid ε)
MonoFoldable t[95] oand
oor
oall predicat
oany predicat
minimum{May|Ex}
maximum{May|Ex}
osum
oproduct
oconcatMap (f::ε -> m) oconcat
NonNull t[101] minimum
maximum

partició[modifica]

opcionals - partició
tipus context filtre sobre llista de contenidors
retornant llista de valors
Maybe ε[112] via MonadPlus catMaybes llistaDeMaybes
Either tipError ε[113] lefts llistaDeEithers
rights llistaDeEithers
partitionEithers llistaDeEithers --retornant parell de llistes
classes
MonadPlus t mfilter[132]
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 (==)
[ε][114] 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 ε[85] 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[115] 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[116] 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))[133]
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 ι ε[119] ixmap (iMin, iMax) (f::i → j)
Vector ε[121] take n
drop n
slice índx n
splitAt índx filter predicat
takeWhile predicat
dropWhile predicat
partition predicat
span predicat
break predicat
MVector σ ε[122] take n
drop n
slice índx n
splitAt índx
classes
IArray α ε[123] ixmap (iMin, iMax) (f::i → j)
MArray α ε m[124] mapIndices (iMin, iMax) (f::i → j)
estructures - partició
tipus context a l'índex en inferiors
i superiors
(estrictament)
s/.
predicat
pel prefix
Set ε[74]
IntSet[76]
(Ord ε) split pivot partition predicat
filter predicat
HashSet ε[83] (Hashable ε, Eq ε) filter predicat
Map κ ε[75]
IntMap ε[77]
(Ord κ) split clauPivot partition predicat
filter predicat
HashMap κ ε[84] (Hashable κ, Eq κ) filter predicat
filterWithKey (f::clau->predicat)
Trie ε[105] submap bytestrPrefixClau
Tree ε[125]
{Min|Max}Heap ε[108] (Ord ε) take n
drop n
splitAt n
filter predicat
partition predicat
takeWhile predicat
dropWhile predicat
span predicat
break predicat
{Min|Max}PrioHeap prio val[108] (Ord prio) take n
drop n
splitAt n
filter predicat
partition predicat
takeWhile predicat
dropWhile predicat
span predicat
break predicat
classes genèriques 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[98] 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[modifica]

classes de la biblioteca GHC que les col·leccions implementen:

  • control seqüencial: Applicative, Monad
  • composició no-seqüencial: Monoid
  • composició seqüencial: Alternative, MonadPlus
  • mapejat no-seqüencial: Functor
  • mapejat seqüencial: Traversable
  • reducció: Foldable
  • comparació de les col·leccions: Eq, Ord
  • textualitzacio de les col·leccions: Show, Read
  • travessament genèric de l'estructura de les col·leccions: Data, Generic
  • avaluació en profunditat (a forma normal) de les col·leccions: NFData ( contracció de Normal Form Data)
  • serialització de les col·leccions: Binary
excepcions i crítiques[modifica]
  • els tipus monomòrfics (ByteString,[115] Text[116] i IntSet[76] no poden implementar les classes Functor i Foldable, Applicative, Monad, Alternative, MonadPlus, Traversable (quines funcions estan definides per a tipus amb un paràmetre). En intentar-ho, dóna l'error: Kind mis-match (l'aritat del tipus no és l'esperada).
  • no implementen Functor:
alternatives
    • El paquet mono-traversable[134] 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[modifica]

Perquè no mantenen la regla de la composició de morfismes dels Functors: per a tot morfisme i .[135]

implementacions[modifica]
implementa classes d'avaluació seqüencial dels elements
tipus Applicative
(combinador
de resultats)
Alternative
(applicative
amb monoide)
-- requereix Applicative
Monad
(encadenament
de resultats)
MonadPlus
(mònada
amb monoide)
--requereix Monad
Traversable[136]
(resultats de col·lecció
avaluant seqüencialment
els elements si són efectes
o les imatges d'una funció amb efectes)
-- requereix Functor, Foldable
Maybe ε[112]
Either tipError ε[113] No No des de ghc 7.8
[ε][114]
Seq ε[85] des de ghc 7.8
Array ι ε[119] No No No No No
Vector ε[121]
Set ε[74] No No No No No
HashSet ε[83] No No No No No
Map κ ε[75]
IntMap ε[77]
No No No No
HashMap κ ε[84] No No No No
Trie ε[105] No No
Tree ε[125] No No
  • Al GHC 7.10 està previst que Applicative esdevingui superclasse de Monad, i que Alternative ho sigui de MonadPlus.[137]
  • Alternative i MonadPlus inclouen implementacions que obeeixen regles diferents. Hi ha una proposta per separar cadascuna d'elles en dues classes.[138][139]
    • La regla Left Catch: Maybe l'obeeix, però no la implem. de la llista. Vegeu implementacions al mòdul Control.Monad.
      -- La regla ''Left Catch''
        mplus (return x) k  return x
        (<|>) (pure x) k  pure x
      
      -- Maybe l'obeeix
      instance MonadPlus Maybe where
         mzero = Nothing
      
         Nothing `mplus` ys  = ys
         Just x  `mplus` _ = Just x
      
      mplus (return x) k == Just x   -- == return x
      
      -- la llista no:
      instance MonadPlus [] where
         mzero = []
         mplus = (++)
      
      mplus (return x) k == [x] ++ k   -- /= return x
      
implementa classes
tipus Monoid[118]
(componible:
mempty, mappend)
Functor[130]
(mapejable:
fmap)
Foldable[127]
(reduïble:
foldl, foldr)
-- requereix Functor
Binary[140]
(serialitzable:
put, get)
NFData[141]
(avaluable a
Forma Normal:
rnf, deepseq)
Data[142]
(que se'n pot
recórrer l'estructura
genèricament:
gfoldl, gmapX)
Maybe ε[112] (Monoid ε)
compon els elements
(Binary ε) (NFData ε) (Data ε)
Either tipError ε[113] No des de ghc 7.8 (Binary tipError,
Binary ε)
(NFData tipError,
NFData ε)
(Data tipError,
Data ε)
[ε][114] mappend = (++) (Binary ε) (NFData ε) (Data ε)
Seq ε[85] mappend = (><) (Binary ε) (NFData ε) (Data ε)
ByteString[115] mappend = append N/A:
l'aritat del tipus
no concorda
(Kind mis-match)
N/A:
l'aritat del tipus
no concorda
Text[116] mappend = append N/A:
l'aritat del tipus
no concorda
(Kind mis-match)
N/A:
l'aritat del tipus
no concorda
(-- implementable
via "binary-generic")
Array ι ε[119] No No (Binary ι,
Binary ε)
(NFData ι,
NFData ε)
No
Vector ε[121] mappend = (++) (Binary ε)
-- instàncies al paquet
vector-binary-instances
(NFData ε) (Data ε)
Set ε[74] (Ord ε)
mappend = union
No #perquè els conjunts no implementen Functor (Binary ε) (NFData ε) (Data ε,
Ord ε)
HashSet ε[83]
mappend = union
No #perquè els conjunts no implementen Functor (-- implementable
via "binary-generic")
(NFData ε) (Data ε)
IntSet[76] mappend = union #perquè els conjunts no implementen Functor N/A:
l'aritat del tipus
no concorda
Map κ ε[75]
IntMap ε[77]
(Ord κ)
mappend = union
cas de claus coincidents
es pren el valor
del primer operand
(Binary ε) (NFData κ,
NFData ε)
(Data κ,
Data ε,
Ord κ)
HashMap κ ε[84]
mappend = union
(-- implementable
via "binary-generic")
(NFData κ,
NFData ε)
(Data κ,
Data ε)
Trie ε[105] (Monoid ε)
mappend = mergeBy...
cas de claus coincidents
en compon els valors
(Binary ε) No No
Tree ε[125] No (Binary ε) (NFData ε) (Data ε)
{Min|Max}Heap ε[108]
mappend = union
No No No No No
{Min|Max}PrioHeap prio val[108]
mappend = union
No No No
classe genèrica implica classes
classe genèrica Semigroup
(semigrup:
<>)
Monoid[118]
(componible:
mempty, mappend)
MonoFunctor[93]
(mapejable:
omap)
MonoFoldable[95]
(reduïble:
ofoldl, ofoldr)
MonoTraversable[96]
(travessable:
otraverse, omapM)
MonoPointed[92]
(generable
des d'un valor:

opoint
-- com pure de Applicative)
SemiSequence[97] No No No No
IsSequence[98]
SetContainer[102] No No No
IsSet[103] No No No
IsMap[104] No

Altres classes habituals:

  • Eq t: Igualable, contenidors iguals comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista.
  • Ord t: Ordenable, compara contenidors comparant els elements segons l'ordre seqüencial o bé mitjançant la conversió a llista ascendent.
  • Show t: Textualitzable a String 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: Llegeix la sortida de (Show t).
implementa classes
tipus Eq[143]
(Igualable)
Ord[144]
(Ordenable)
Show[145]
(Textualitzable)
--només ASCII
altrament codis numèrics
Read[146]
(Llegible)
--només ASCII
altrament codis numèrics
--monomòrfics
ByteString[115]
Text[116]
IntSet[76]
--tipus d'aritat 1
Maybe ε[112]
[ε][114]
Seq ε[85]
Vector ε[121]
Set ε[74]
IntMap ε[77]
(Eq ε) (Ord ε) (Show ε) (Read ε)
--tipus d'aritat 2
Map κ ε[75]
Either κ ε[113]
(Eq κ, Eq ε) (Ord κ, Ord ε) (Show κ, Show ε) (Read κ, Read ε)
--tipus amb particularitats
(Ix ι) => Array ι ε[119] (Eq ε) (Ord ε) (Show ι, Show ε) No
Tree ε[125] (Eq ε) No (Show ε) (Read ε)
(Hashable ε, Eq ε) => HashSet ε[83] No (Show ε) (Read ε)
(Hashable κ, Eq κ) => HashMap κ ε[84] (Eq ε) No (Show κ, Show ε) (Read κ, Read ε)
(Ord ε) => {Min|Max}Heap ε[108]
(Ord prio) => {Min|Max}PrioHeap prio val[108]
Implementació de Binary (serialitzable) amb binary-generic[modifica]

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

{-# 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[modifica]

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 classe funció decodificadora
o de conversió
extensió de llenguatge
999 (sense punt decimal) Num fromInteger
999[.99] [E[±]99] Fractional fromRational
999[.99] [E[±]99] Num fromInteger NumDecimals[148]
"tira de caràcters" IsString[149] fromString OverloadedStrings[150]
[enumeració de valors] IsList[151] fromList OverloadedLists[152]
Sobrecàrrega de literals String[modifica]

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.[149] Cal especificar l'extensió de llenguatge OverloadedStrings.[150]

{-# 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[149]
[Char][114]
ByteString[153]
Text[116]

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[modifica]
  • 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ó.[152]
implementa classes
tipus IsList[151] Item (tipus associat
element del literal)
[ε][114] ε
NonEmpty ε[111] ε
Text[116] Char
Seq ε[85] ε
IntSet[76] Int
Set ε[74] ε
IntMap ε[77] (Int, ε)
Map κ ε[75] (κ, ε)
HashSet ε[83] ε
HashMap κ ε[84] (κ, ε)
  • 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.[154]
  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

main = do
        print provaConjunt
        print provaDicc

Preprocessadors[modifica]

Compilació condicional amb codi CPP[modifica]

Inclusió de codi de preprocessador de C (CPP).[155] Compilació condicional.[156]

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.*.[157]

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[158]

altres preprocessadors[modifica]

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

preprocessadors de generació de codi d'enllaç amb biblioteques d'altres llenguatges[modifica]

Vegeu HaskellWiki - Foreign Function Interface

  • cas de document FFI amb extensió ".hsc" "Haskell amb incrustacions de C" d'enllaç amb biblioteques en llenguatge C el programa hsc2hs genera el mòdul haskell i els fitxers .c/.h .[159][160][161]
  • cas de document FFI amb extensió ".gc" "GreenCard" d'enllaç amb biblioteques en llenguatge C, el programa GreenCard genera el mòdul de codi.[162]
  • c2hs és un programa que llegeix fitxers d'interfície de llenguatge C (.h) i en genera un mòdul haskell amb crides FFI[163]

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

É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ó.[164]

$> ghci
GHCi, version 7.7.20140111:...
Prelude> :set -XTypeHoles
Prelude> :{
Prelude| data Free f a
Prelude|   = Pure a
Prelude|   | Free (f (Free f a))
Prelude|
Prelude| instance Functor f => Monad (Free f) where
Prelude|   return a     = Pure a
Prelude|   Pure a >>= f = f a
Prelude|   Free f >>= g = Free _
Prelude| :}

<interactive>:32:23:
    Found hole _ with type: f (Free f b)
    Where: f is a rigid type variable bound by
               the instance declaration at <interactive>:29:10
           b is a rigid type variable bound by
               the type signature for
                 (>>=) :: Free f a -> (a -> Free f b) -> Free f b
               at <interactive>:31:10
    Relevant bindings include
      g :: a -> Free f b (bound at <interactive>:32:14)
      f :: f (Free f a) (bound at <interactive>:32:8)
      (>>=) :: Free f a -> (a -> Free f b) -> Free f b
        (bound at <interactive>:31:3)
    In the first argument of Free, namely _
    In the expression: Free _
    In an equation for >>=: (Free f) >>= g = Free _
Prelude>

Depuració[modifica]

Pistes per la depuració aquí.[165]

El problema de la manca d'informació de situació en les petades[modifica]

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), dóna l'error "Map.find: element not in the map". Cap pista (a GHC) de la situació de la crida a error, ni de la rutina de procedència, ni 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.[166] 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ó.[167][168] Cal afegir +RTS -xc al llançament del programa.[169]
  • 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.[170]
  • 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 especials (?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 de la restricció en les crides intermèdies fins a la crida a error (l'àmbit de l'implícit no es propaga). 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 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.[171] Per fer proves amb perfilat cal disposar d'una compilació amb -prof de totes les biblioteques que relliguem.[172]
Per evitar l'error de manca de biblioteca de perfilat en les dependències, cal generar versions amb perfilat de les biblioteques que s'instaŀlin. Per això, per al desenvolupament en aïllament (sandbox), convé afegir l'opció de perfilat per defecte (library-profiling: True) a l'arxiu de configuració de cabal específic del projecte (cabal.config) 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[modifica]

Des de GHC 8.0.1 (excepte per a Windows): Vegeu ref.[173]

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 '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)[modifica]

Desavantatge: Les clàusules catch no es poden utilitzar dins el codi funcional, sinó només en codi de la mònada IO.[174] El paquet exceptions[175] 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[modifica]

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[modifica]

Amb GHCi debugger,[176] Vegeu l'article "No more exceptions: debugging Haskell code with GHCi".[177] 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"[178] 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[modifica]

Des de la versió 7.10.2[179] 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.[180]

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

Traslladant la petada de les funcions parcials a la rutina que la crida amb paràmetres no vàlids[modifica]

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 dóna l'inesperat, la petada de l'encaix dóna informació de la situació.[182]
  • 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[modifica]

La biblioteca Safe[183] 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[modifica]

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[modifica]

Vegeu ref.[184]

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

Un Prelude alternatiu sense funcions parcials[modifica]

El paquet classy-prelude[185] 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[186] 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[modifica]

Afegint anotacions de "tipus refinats amb predicats".

Vegeu #Verificació Formal estàtica amb la biblio LiquidHaskell

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

El paquet loch-th[187] 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[modifica]

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

force del paquet "deepseq"[188] 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

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

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

Traçabilitat[modifica]

Vegeu ref.[189]

En el codi funcional[modifica]

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

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

En el codi monàdic[modifica]

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

EventLog Tracing[modifica]

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

  • Cal compilar amb l'opció -eventLog.[191]
  • per a mostrar la traça textualment, cal compilar a més a més amb -debug i executar amb +RTS -v[flags].[192] 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].[192]

Exemple:

import Control.Monad
import Debug.Trace

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

Més problemàtiques[modifica]

El problema de les configuracions[modifica]

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

  • Amb la mònada Reader[194] (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").[195] Desavantatge: hi ha el risc que les ops. no s'efectuïn en l'ordre correcte si no se'n controla bé l'avaluació.
  • Altres sistemes més sofisticats:
    • Segons Joachim-Breitner[196][197]
    • Segons Oleg Kiseliov i Chung-chieh Shan[198]

Una solució: El hack unsafePerformIO[modifica]

unsafePerformIO[199] É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

Trenca el principi de transparència referencial (constància del resultat d'una funció amb les mateixes entrades) de les funcions que les inclouen.

L'ordre de les ops. dependrà del moment que se n'avaluï el thunk corresponent. (Vegeu "IO inside: The dark side of the IO Monad".)[200]

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

unsafePerformIO[199] és la porta del darrere de la mònada IO.[200]

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).[199]
{-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}  -- recomanació de System.IO.Unsafe
import System.IO.Unsafe
import Data.IORef

-- variable global, a l'estil del llenguatge ML, però fora de la mònada IO,
-- trenca la transparència referencial de les expressions de codi pur on s'utilitzi

{-# NOINLINE intRef #-} -- recomanació de System.IO.Unsafe
intRef :: IORef Int                     
intRef = unsafePerformIO $ newIORef 0     -- variable global fora de la mònada IO

-- funció de ''unit'' () per evitar la ''memoització'' de les crides funcionals sense paràmetres
{-# NOINLINE llegeix #-}
llegeix :: () -> Int
llegeix _ = unsafePerformIO $ readIORef intRef

main = do
        -- cal que l'escriptura sigui avaluada
        --    que no sigui part d'un càlcul amb avaluació diferida (''thunk'')
        writeIORef intRef 42
        print $ llegeix ()
        writeIORef intRef 30
        print $ llegeix ()

Aquesta solució és incompatible amb la certificació Safe Haskell.

El sistema més correcte és tractar les preferències com un entorn canviant i passar-lo com a paràmetre, fent servir la mònada Reader o bé el transformador de mònades corresponent ReaderT.[202]

Tractament d'un entorn canviant, amb la mònada Reader[modifica]

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[modifica]

L'ús del "plegat per l'esquerra estricte" foldl' no garanteix que es treballi a espai constant de la pila.

En cas de funcions amb recursivitat final,

  • les expressions (o variables definides localment) en la crida recursiva generen thunks pendents d'avaluar, que s'apilen mentre no s'arriba al cas no recursiu.
  • l'avaluació estricta (amb '!' BangPatterns (a WHNF)) en els paràmetres acumuladors tampoc ho garanteix, si l'avaluació es topa amb un constructor.

Vegeu "Stack overflow" al HaskellWiki[166] 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"[203]

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[204] aporta funcions d'avaluació a Forma Normal que sobrepassen les limitacions de seq. El paquet deepseq-th[205] 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[modifica]

Vegeu enllaços "Memory leaks","[206] Space leak zoo"[207]

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

El maleït problema de les dependències de biblioteques (ang: cabal dependency hell)[modifica]

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,[209] 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[modifica]

Stackage[210] vol ser una solució al problema de les dependències, oferint imatges congelades (ang: snapshots) d'un conjunt de biblioteques compatibles i promovent que tots els autors s'actualitzin a les modificacions de biblioteques dependents per evitar-ne la multiplicitat de versions en una imatge del conjunt.

Stackage notifica a l'autor d'una biblioteca del seu rebost en el moment que l'actualització d'una dependència (biblio. de la que depèn) trenca la compatibilitat, i fins que no l'actualitzi queda exclosa de les noves imatges congelades del rebost, mantenint les existents.[211]

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.[212][213][214]

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:

# fitxer cabal.config del projecte
remote-repo: stackage-lts-6.27:http://www.stackage.org/lts-6.27

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

Programació de nivell baix[modifica]

Vegeu

  • "Més aviat: com fer que un programa compili més de pressa".[218]
  • "Més ràpid: com fer que un programa corri més".[219]
  • "Més petit: com fer que un programa ocupi menys".[220]
  • "Més auster: com fer que un programa engoleixi menys memòria dinàmica".[221]

Optimitzacions d'allotjament[modifica]

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

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.

Cal especificar els operadors, tipus i literals afegint un símbol '#' al darrere, extensió de sintaxi que requereix la pragma {-# LANGUAGE MagicHash #-}:

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

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

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[223] 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[modifica]

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

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

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

Les tuples unboxed[226]

(# a, b #)

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

-- fitxer tuples-no-encapsulades.hs
{-# OPTIONS_GHC -fobject-code #-}
{-# LANGUAGE UnboxedTuples #-}

f x y = (# x+1, y-1 #)  -- retorn amb tupla ''no encapsulada''

g x = case f x x of

    (# a, b #)  a + b  -- obrim la tupla per encaix del patró ''(#... #)''

main = do
     let y = g 5
     putStrLn $ "y = " ++ show y

Compilació i exec.

ghc —make tuples-no-encapsulades.hs

./tuples-no-encapsulades

Especialització d'operacions sobrecarregades[modifica]

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

{-# LANGUAGE BangPatterns #-}

factorial :: (Num a, Ord a) => a  a
factorial 0 = 1
factorial n
 | n > 0 = factorial_rf 1 n
 | otherwise = error "n no pot ser negatiu"
 where
        factorial_rf !acum !n  -- aval. estricta als paràmetres formals
              | n == 1 = acum
              | n > 1  = factorial_rf (acum * n) (n-1)

{-# SPECIALIZE factorial :: Int → Int #-}
{-# SPECIALIZE factorial :: Integer → Integer #-}

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

Safe Haskell[modifica]

Vegeu ref.[228][229] És una extensió de llenguatge per restringir l'ús de diverses construccions, com ara unsafePerformIO[199] 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[modifica]

  • eta-lang.org és una modificació de GHC per generar 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 GHC admetent sintaxi FFI (Foreign Function Interface) ampliada d'enllaç amb Java per a l'importació / exportació.
  • etlas: és una adaptació del programa cabal.
  • Frege[230] é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, ...)[231]

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

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

Haste[233][234][235][236] i també GhcJs[237][238] són modificacions de GHC amb generació de codi JavaScript partint de la sortida STG del compilador.[239]

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[233] 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

Eines[modifica]

hlint
Aquí[240] analitzador de codi amb suggeriments de millora. Avisa de construccions redundants i proposa alternatives al codi.
cabalg
descarrega i instaŀla des del GitHub
cabalg https://github.com/usuari-gh/projecte-gh   # descarrega ('git clone') a la carpeta 'projecte-gh' i instaŀ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.[241]
  • 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.
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.[242]
.- 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[243]
  • 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.[244]
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).[245] Estudi "Ajustatge fi del paral·lelisme amb ThreadScope".[246]
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.[247]
  1. compilar amb l'opció -fhpc
  2. executar diverses vegades amb diferents entrades
  3. invocar: hpc markup nom_executable (genera hpc_index.html i altres html)
  4. obrir hpc_index.html amb un navegador
  5. per recomençar l'estudi, i també després de recompilar, cal eliminar el fitxer nom_executable.tix on guarda les traces d'execució.
hp2any (obtenció del perfil d'ús de memòria)
  • hp2any-graph mostra en temps-real un gràfic de l'evolució de l'ocup. de memòria.[248]
  • hp2any-manager mostra gràfics de fitxers de perfil (.hp) corresp. a execucions ja finalitzades.
més eines de desenvolupament al HaskellWiki

Vegeu ref.[249]

Verificació Formal estàtica amb la biblio LiquidHaskell[modifica]

"LiQuiD Types" és un nom adaptat de la contracció de "Logically Qualified Data Types".[250]

La biblioteca LiquidHaskell[251][252] permet la verificació mitjançant anotacions de "tipus refinats amb predicats"[253][254] (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)[255] 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".[254] Explicació més planera aquí.[256] Vegeu bloc.[257]

É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:.[258]

$ liquid --smtsolver=mathsat Main.hs
**** DONE:  fixpoint ***********************************************************
**** DONE:  solve **************************************************************
**** DONE:  annotate ***********************************************************
**** SAFE **********************************************************************

Exemples[modifica]

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

Arrencada amb opcions[modifica]

Per afegir opcions a l'intèrpret d'ordres vegeu l'enllaç.[259][260][261]

-- per una lectura correcta dels caràcters no anglosaxons a l'intèrpret de comandes a Linux (''shell'')
--  cal esmentar "System.Environment.UTF8" del paquet utf8-string
--, en lloc de "System.Environment"

import System.Environment (getProgName, getArgs)   -- a Linux System.Environment.UTF8
import System.Exit (exitSuccess, exitWith, ExitCode(..))
import System.Console.GetOpt

import Data.Maybe (fromMaybe)
import Text.Printf (printf)

-- tipus per a les opcions de l'intèrpret d'ordres (anomenades també senyals, en ang.: ''flags'')
data Flag = FVersio | FEntrada String | FSortida String | FArgLliure String
              deriving (Eq, Show)

opcions :: [OptDescr Flag]
opcions = [
          -- opció curta, opció llarga, descriptor, missatge d'ajuda
          -- amb paràmetres obligatoris (ReqArg), opcionals (OptArg), o sense (NoArg)
  Option ['V'] ["versió"]  (NoArg FVersio) "mostra versió",
  Option ['i'] ["entrada"] (ReqArg FEntrada "FILE") "entrada requereix un nom de fitxer",
  Option ['o'] ["sortida"] (OptArg creaFlagSortidaAmbValorPerOmissió "FILE") "sortida"
 ]

-- fromMaybe valorPerOmissió valorOpcional
--     retorna el contingut de l'opcional o, en cas de Nothing, el valorPerOmissió

creaFlagSortidaAmbValorPerOmissió :: Maybe String -> Flag
creaFlagSortidaAmbValorPerOmissió ms = FSortida ( fromMaybe "sortida_per_defecte" ms )

-- embolcalla els arg. lliures com a ''flags'' per un tractament unificat
argQueNoEsOpció :: String -> Flag
argQueNoEsOpció arg = FArgLliure arg

msg_com = "Ús: nom_prog [OPCIÓ...]"

processa flags = do
                  print flags
                  exitSuccess

error_als_args nom_prog msgs_error = do
    printf "%s error als paràmetres\n" nom_prog
    putStrLn $ concat msgs_error ++ usageInfo msg_com opcions
    exitWith $ ExitFailure 1  -- sortir amb el codi de finalització

main = do
 nom_prog <- getProgName
 args <- getArgs
 case getOpt (ReturnInOrder argQueNoEsOpció) opcions args of
   -- cas de manca d'errors (llista buida al 3r de la tupla)
   (flagsCorrectes, [],   [])  -> processa flagsCorrectes
   -- altrament
   (_,   _,    msgs_error)  -> error_als_args nom_prog msgs_error

Excepcions de tipus definits per l'usuari[modifica]

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

 -- 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[modifica]

Incorporant transformacions a les llistes per comprensió amb la clàusula then.[263] 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

dóna

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

vectors a la mònada ST[modifica]

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

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.[90] Avantatge: implementa força més classes i disposa d'algoritmes específics al paquet vector-algorithms.[264]

{-# 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[modifica]

Hashable[81] 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.[265]

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[modifica]

Tots els paquets d'expressions regulars implementen (=~) que dóna èxit i coincidències segons el tipus del resultat. Vegeu ref.[266]

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

Amb l'ús de l'extensió DeriveAnyClass, és tan fàcil com afegir les classes {Generic, ToJSON, FromJSON} a la clàusula deriving. Requereix el paquet aeson.[267]

{-# 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

dóna:

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[modifica]

Vegeu ref.[268]

{-# 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 coŀ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

dóna el resultat:

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

Combinació de filtres[modifica]

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[modifica]

Vegeu #Data Parallel Haskell i ref.[34]

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 paraŀlel qsortVect als subvectors (Nested Data Parallellism) !!
         vectors = mapP qsortVect [: menors, majors :]
     in
       (vectors !: 0)  +:+  iguals  +:+  (vectors !: 1)

2. Mòdul d'operacions NO-vectoritzades

{- Main.hs -}
{-# LANGUAGE PackageImports #-}
import System.Environment
import Text.Printf
import 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[modifica]

Hola món amb GTK[modifica]

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

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

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

Hola món amb WxHaskell[modifica]

WxHaskell[271] és una implementació de l'entorn gràfic Wx[272] 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[modifica]

Una lent és un mecanisme componible que permet enfocar una part d'una estructura per ser manipulada en tant que integrant de la mateixa. Vegeu refs.[273][274][275][276] Exemple amb el paquet lens (genera les lents mitjançant meta-programació amb la funció makeLenses sobre un registre).[277]

{-# 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, prefixats per '_', amb 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
      partSencera = floor v
      partFracció = v - fromIntegral partSencera
      (mins, secs) = truncate (partFracció * 3600) `divMod` 60

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   -- dóna: 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

Extreure informació d'un document XML[modifica]

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

Operacions als tipus - Tipus dependents de valors[modifica]

Vegeu ref.[278][279]

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[280] o definicions inductives més sofisticades.[281]

  • Remarcable: El paquet dimensional-tf incorpora als tipus les dimensions de les unitats de la física.[282]
{-# 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 dóna:

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

Naturals als tipus[modifica]

La proposta TypeNats[283] ("Type level natural numbers"), parcialment implementada a GHC 7.6.1.

Cal fer servir l'endollable (ang:plugin) de GHC[284] type-nat-solver[285] 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.[286] Compilat amb GHC 7.10.1.

# instal·lar el paquet 'cabalg' que instaŀ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 instaŀla

Mòdul de nombres de Peano de tipus singulars (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[modifica]

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]),[287] 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[288] donen un valor esbiaixat respecte a la intuïció. L'intèrpret GHCI dóna correctament

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

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

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

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

A ghci:

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

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

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

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(),[289] creant un subconjunt del mòdul com a Mòdul.hs-boot, i referir-s'hi des dels altres mòduls afegint la pragma SOURCE a la clàusula import.

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

Referències[modifica]

  1. mmc - Compilador Mercury de Melbourne(anglès)
  2. web del Compilador Haskell de York(anglès)
  3. web del Compilador Haskell d'Utrecht(anglès)
  4. Diagrama de blocs del ghc que incorpora C--(anglès)
  5. web del C--(anglès)
  6. El llenguatge Cmm(anglès)
  7. Estàndard actual del llenguatge Haskell
  8. La "Plataforma Haskell"(anglès)
  9. 9,0 9,1 9,2 API de la Platforma Haskell Seleccioneu-ne la versió(anglès)
  10. Anunci de la GHC 6.12.1(anglès)
  11. Proposal: Remove Control.OldException(anglès)
  12. Biblioteques del GHC 7.4.2(anglès) Control.OldException encara hi és
    Les excepcions del H98 estan suportades
  13. Biblioteques del GHC 7.6.1(anglès) Control.OldException ja no hi és
    Les excepcions del Haskell98 ja no estan suportades
  14. The Glasgow Haskell Compiler(anglès)
  15. Documentació del compilador GHC
  16. Opcions del RunTimeSystem per controlar la mem. dinàmica i el recollidor de memòria brossa(anglès)
  17. RTS - Envelliment(anglès)
  18. RTS - Allotjament en Blocs
  19. RTS - El planificador(anglès)
  20. El recollidor de brossa(anglès)
  21. RTS - El recollidor de brossa.
  22. mòdul System.Mem(anglès)
  23. Simon Peyton Jones, et. al. - Stretching the storage manager(anglès)
  24. mòdul System.Mem.Weak(anglès)
  25. Control.Concurrent.MVar.mkWeakMVar(anglès)
  26. Ús de biblioteques d'enllaç dinàmic (anglès)
  27. Suport d'enllaç dinàmic a les diferents plataformes (anglès)
  28. Building and using Win32 DLLs(anglès)
  29. Parallel Haskell Digest(anglès)
  30. El paquet meta-par(anglès)
  31. 31,0 31,1 HaskellWiki - A Repa Tutorial(anglès)
  32. REgular PArallel arrays(anglès)
  33. Haskell - Paral·lelisme de dades
  34. 34,0 34,1 Nested Data Parallelism - presentació (diapos)
  35. Paquet dph-examples
  36. Guy E. Blelloch - Programming Parallel Algorithms - Nested Data Parallellism
  37. GHC.Prim - SIMD Vectors(anglès)
  38. SIMD instructions in GHC (anglès)
  39. Paquet simd
  40. Paquet primitive-simd
  41. 41,0 41,1 UNSW.edu.au - An Embedded Language for Accelerated Array Computations(anglès)
  42. Biblioteca Accelerate per al paral·lelisme a les GPU(anglès)
  43. biblioteca accelerate-cuda(anglès)
  44. El paquet accelerate-opencl(anglès)
  45. El paquet accelerate-repa(anglès)
  46. paquet OpenCLWrappers(anglès)
  47. OpenCL from Haskell(anglès)
  48. How to write hybrid CPU/GPU programs with Haskell(anglès)
  49. Hackage - paquet accelerate(anglès)
  50. Hackage - paquet accelerate-cuda(anglès)
  51. CUDA developer zone(anglès)
  52. accelerate-opencl de HIPERFIT(anglès)
  53. NVIDIA OpenCL(anglès)
  54. AMD APP SDK – A Complete Development Platform(anglès)
  55. accelerate amb rerefons OpenCL-Cilk(anglès) Subcarpeta icc-opencl
  56. accelerate-repa(anglès)
  57. accelerate-llvm(anglès)
  58. Template Haskell(anglès)
  59. Haskellwiki - QuasiQuotation(anglès)
  60. 60,0 60,1 Yesod - plantilles(anglès)
  61. Sintaxi dels quasiQuoters(anglès)
  62. QuasiQuotation
  63. Tipus algebraics del TemplateHaskell (anglès)
  64. Haskellwiki - Quasi-Quotation(anglès)
  65. Constructor QuasiQuoter al paquet template-haskell(anglès)
  66. Template Haskell - stringE(anglès)
  67. Fusionant successius Map: Fent que Haskell sigui un 225% més ràpid (anglès)
  68. From lists to streams to nothing at all(anglès)
  69. Loop fusion (anglès)
  70. Stream fusion for Haskell Arrays (anglès)
  71. 71,0 71,1 paquet Stream-fusion(anglès)
  72. API del GHC(anglès)
  73. API Contenidors(anglès)
  74. 74,00 74,01 74,02 74,03 74,04 74,05 74,06 74,07 74,08 74,09 74,10 74,11 74,12 Data.Set
  75. 75,00 75,01 75,02 75,03 75,04 75,05 75,06 75,07 75,08 75,09 75,10 75,11 75,12 75,13 Data.Map
  76. 76,00 76,01 76,02 76,03 76,04 76,05 76,06 76,07 76,08 76,09 76,10 76,11 Data.IntSet
  77. 77,00 77,01 77,02 77,03 77,04 77,05 77,06 77,07 77,08 77,09 77,10 77,11 Data.IntMap
  78. paquet hashmap (anglès)
  79. Data.HashSet del paquet hashmap(anglès)
  80. Data.HashMap del paquet hashmap(anglès)
  81. 81,0 81,1 paquet Hashable(anglès)
  82. El paquet unordered-containers (HashMap i HashSet que no requereixen claus ordenables)
  83. 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.HashSet del paquet unordered-containers(anglès)
  84. 84,00 84,01 84,02 84,03 84,04 84,05 84,06 84,07 84,08 84,09 84,10 Data.HashMap.Lazy del paquet unordered-containers(anglès)
  85. 85,00 85,01 85,02 85,03 85,04 85,05 85,06 85,07 85,08 85,09 85,10 85,11 85,12 Data.Sequence(anglès)
  86. 86,0 86,1 86,2 86,3 86,4 86,5 Data.Array.Repa(anglès)
  87. paquet Vector(anglès)
  88. Haskell numèric: Guia sobre Vector(anglès)
  89. Famílies de tipus (anglès)
  90. 90,0 90,1 http://hackage.haskell.org/package/vector/docs/Data-Vector-Unboxed.html
  91. El paquet mono-traversable(anglès)
  92. 92,0 92,1 92,2 MonoPointed(anglès)
  93. 93,0 93,1 93,2 MonoFunctor(anglès)
  94. MonoZip(anglès)
  95. 95,0 95,1 95,2 95,3 95,4 MonoFoldable
  96. 96,0 96,1 MonoTraversable(anglès)
  97. 97,0 97,1 97,2 97,3 97,4 classe SemiSequence(anglès)
  98. 98,0 98,1 98,2 98,3 98,4 classe IsSequence(anglès)
  99. Textual(anglès)
  100. Utf8(anglès)
  101. 101,0 101,1 101,2 101,3 101,4 tipus NonNull t(anglès)
  102. 102,0 102,1 SetContainer(anglès)
  103. 103,0 103,1 IsSet(anglès)
  104. 104,0 104,1 IsMap(anglès)
  105. 105,0 105,1 105,2 105,3 105,4 105,5 105,6 105,7 105,8 paquet bytestring-trie(anglès)
  106. Data.DList(anglès)
  107. Demistifying DList's(anglès)
  108. 108,00 108,01 108,02 108,03 108,04 108,05 108,06 108,07 108,08 108,09 108,10 108,11 108,12 108,13 108,14 108,15 108,16 Data.Heap del paquet heap(anglès)
  109. paquet semigroups
  110. mòdul Data.Semigroup(anglès)
  111. 111,0 111,1 111,2 111,3 111,4 Data.List.NonEmpty(anglès)
  112. 112,0 112,1 112,2 112,3 112,4 112,5 112,6 112,7 112,8 Data.Maybe
  113. 113,0 113,1 113,2 113,3 113,4 113,5 113,6 113,7 Data.Either
  114. 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 Data.List
  115. 115,00 115,01 115,02 115,03 115,04 115,05 115,06 115,07 115,08 115,09 115,10 Data.ByteString(anglès)
  116. 116,00 116,01 116,02 116,03 116,04 116,05 116,06 116,07 116,08 116,09 116,10 116,11 116,12 Data.Text(anglès)
  117. 117,0 117,1 Data.Text - Tipus intern del(anglès)
  118. 118,0 118,1 118,2 118,3 118,4 Data.Monoid(anglès)
  119. 119,0 119,1 119,2 119,3 119,4 119,5 119,6 119,7 119,8 Data.Array
  120. Data.Array.Unboxed
  121. 121,0 121,1 121,2 121,3 121,4 121,5 121,6 121,7 121,8 121,9 Data.Vector(anglès)
  122. 122,0 122,1 122,2 122,3 122,4 Data.Vector.Mutable(anglès)
  123. 123,0 123,1 123,2 123,3 123,4 123,5 Data.Array.IArray
  124. 124,0 124,1 124,2 124,3 124,4 124,5 Data.Array.MArray
  125. 125,0 125,1 125,2 125,3 125,4 125,5 125,6 125,7 Data.Tree
  126. 126,0 126,1 126,2 paquet mono-traversable
  127. 127,0 127,1 127,2 127,3 Data.Foldable
  128. Data.Map.(!)(anglès)
  129. HaskellWiki Debugging(anglès)
  130. 130,0 130,1 130,2 Data.Functor
  131. Data.List.union
  132. Control.Monad.mfilter
  133. Data.Sequence.breakl(anglès)
  134. paquet mono-traversable(anglès)
  135. Set is not a Functor(anglès)
  136. Data.Traversable
  137. Functor-Applicative-Monad Proposal(anglès)
  138. HaskellWiki - MonadPlus(anglès)
  139. HaskellWiki - MonadPlus reform proposal(anglès)
  140. Data.Binary(anglès) Serialització
  141. Control.DeepSeq(anglès)
  142. 142,0 142,1 Data.Data(anglès)
  143. Data.Eq(anglès)
  144. Data.Ord(anglès)
  145. Text.Show(anglès)
  146. Text.Read(anglès)
  147. Data.Binary.Generic(anglès)
  148. Extensió NumDecimals(anglès)
  149. 149,0 149,1 149,2 Data.String
  150. 150,0 150,1 Extensió OverloadedStrings(anglès)
  151. 151,0 151,1 La classe IsList(anglès)
  152. 152,0 152,1 Extensió OverloadedLists(anglès)
  153. El paquet bytestring(anglès)
  154. HaskellWiki - Orphan instance(anglès)
  155. Opcions i variables predefinides per al CPP(anglès)
  156. Compilació condicional(anglès)
  157. GHC version numbering policy(anglès) Política de numeració de versions de GHC
  158. HaskellWiki - Compatibility modules
  159. hsc2hs(anglès)
  160. Writing Haskell interfaces to C code: hsc2hs(anglès)
  161. Haskell FFI tutorial example(anglès)
  162. GreenCard - generador de codi d'enllaç amb biblioteques en C(anglès)
  163. https://github.com/haskell/c2hs c2hs
  164. Type Holes(anglès)
  165. Depuració(anglès)
  166. 166,0 166,1 HaskellWiki - Stack overflow
  167. HIW 2012. Simon Marlow: Why can't I get a stack trace?(anglès)
  168. Finding the needle: Stack Traces for GHC - Tristan Allwood, Simon Peyton-Jones and Susan Eisenbach(anglès)
  169. HaskellWiki - Stack trace(anglès)
  170. errorWithStackTrace de GHC.Stack (anglès)
  171. GHC - RTS Configurations (anglès)
  172. GHC - Opcions de perfilat (anglès)
  173. Stack Traces in GHCi(anglès)
  174. Catching Exceptions(anglès)
  175. El paquet exceptions aporta excepcions no limitades a la mònada IO(anglès)
  176. GHC users guide - The GHCi Debugger
  177. No more exceptions: debugging Haskell code with GHCi(anglès)
  178. El fitxer ".ghci"(anglès)
  179. Notes de la versió 7.10.2(anglès)
  180. Special implicit parameters(anglès) Pila de crides com a paràmetre implícit
  181. la biblio. file-location(anglès)
  182. HaskellWiki - Debugging - Locating a failure in a library function(anglès)
  183. La bilioteca Safe(anglès)
  184. GHC flag reference(anglès)
  185. La biblio classy-prelude(anglès)
  186. La biblio mono-traversable(anglès)
  187. El paquet loch-th aporta un mecanisme per informar de les crides a les assercions fallides(anglès)
  188. force del paquet deepseq permet avaluar els components d'una dada
  189. Mòdul Debug.Trace(anglès)
  190. EventLog Tracing(anglès)
  191. HaskellWiki EventLog(anglès)
  192. 192,0 192,1 GHC users guide - RTS EventLog(anglès)
  193. Variables globals(anglès)
  194. La mònada Reader
  195. HaskellWiki - Top level mutable state
  196. Una solució al problema de les configuracions.
  197. paquet Seal-module
  198. Implicit configurations
  199. 199,0 199,1 199,2 199,3 System.IO.Unsafe (anglès)
  200. 200,0 200,1 IO inside: The dark side of the IO Monad(anglès)
  201. Hack - In computer science(anglès)
  202. The Reader monad(anglès)
  203. Retainer profiling
  204. La bibloteca deepseq(anglès)
  205. El paquet deepseq-th(anglès)
  206. HaskellWiki - Memory leak(anglès)
  207. .Edward Z. Yang - Space leak zoo(anglès)
  208. Profiling memory usage(anglès)
  209. An Introduction to Cabal sandboxes(anglès)
  210. HaskellWiki - Stackage(anglès)
  211. El servidor Stackage
  212. LTS Haskell: Versionar l'ecosistema(anglès)
  213. stackage.org - LTS actual(anglès)
  214. What's the point of Stackage LTS?(anglès)
  215. yesodweb.com - Stable, Vetted Hackage(anglès)
  216. FPComplete Haskell Center(anglès)
  217. haskellstack.org(anglès)
  218. Sooner: producing a program more quickly(anglès)
  219. Faster: producing a program that runs quicker(anglès)
  220. Smaller: producing a program that is smaller(anglès)
  221. Thriftier: producing a program that gobbles less heap space(anglès)
  222. Tipus unboxed (allotjament directe) - Restriccions
  223. Tipus amb allotjament directe (ang:unboxed) i primitius (anglès)
  224. Pragma UNPACK
  225. Notes de la versió 7.8.1
  226. Tuples unboxed.
  227. Pragmes del Haskell98(anglès)
  228. haskellWiki - Safe Haskell(anglès)
  229. GHC users guide - Safe Haskell(anglès)
  230. Compilador Frege
  231. Referència del llenguatge Frege(anglès)
  232. HaskellWiki - The JavaScript Problem(anglès)
  233. 233,0 233,1 Haste: Running Haskell in the Browser
  234. Try Haste(anglès)
  235. Haste report - Towards a Declarative Web(anglès)
  236. GitHub - valderman/haste-compiler(anglès)
  237. GitHub - ghcjs(anglès)
  238. GHCJS, Concurrent Haskell in the Browser(anglès)
  239. GHC - Procés de generació de codi(anglès)
  240. Paquet hlint que genera el programa del mateix nom(anglès)
  241. Cabal-dev - Sandboxed development builds for Haskell(anglès)
  242. cabal-meta (anglès)
  243. cabal-ghci (anglès)
  244. yackage (anglès)
  245. Threadscope
  246. Ajustatge fi del paral·lelisme amb ThreadScope
  247. Haskell Program Coverage(anglès) eina que mostra quin codi no s'ha executat mai i condicions sempre certes o sempre falses
  248. Hp2any - Obtenció del perfil d'ús de memòria(anglès)
  249. Eines de desenvolupament de programes(anglès)
  250. Liquid Types(anglès)
  251. HaskellWiki - Liquid Haskell(anglès)
  252. LiquidHaskell README(anglès)
  253. Refinement types(anglès)
  254. 254,0 254,1 Refinement types for Haskell(anglès)
  255. L'estàndard SMT(anglès)
  256. Real world Liquid(anglès)
  257. LiquidHaskell Blog
  258. MathSAT(anglès)
  259. Guia d'Opcions de l'intèrpret d'ordres en Haskell(anglès)
  260. API System.Console.GetOpt amb exemples(anglès)
  261. Neil Mitchell - Intèrpret d'ordres, exemples d'arguments(anglès)
  262. The Exception type (anglès)
  263. GHC extra - Seleccions estil SQL a les llistes(anglès)
  264. paquet vector-algorithms(anglès)
  265. El paquet unordered-containers de la plataforma Haskell, aporta HashMap i HashSet(anglès)
  266. Haskell wiki - Regular expressions(anglès)
  267. paquet aeson(anglès)
  268. Data.Tree(anglès)
  269. Gtk2hs(anglès)
  270. Per què serveix el símbol ___gxx_personality_v0(anglès)
  271. WxHaskell(anglès)
  272. wxWidgets(anglès)
  273. Van Laarhoven - CPS Functional References(anglès)
  274. Eduard Kmett - Lens wiki - Overview(anglès)
  275. The lens library(anglès)
  276. HaskellWiki - Lens(anglès)
  277. El paquet lens(anglès)
  278. Haskell wiki - Dependent type (anglès)
  279. Haskell wiki - Type arithmetic(anglès)
  280. Paquet type-level-natural-number(anglès)
  281. Paquet numtype-tf(anglès)
  282. dimensional-tf(anglès)
  283. Haskell Trac - TypeNats(anglès)
  284. GHC Plugins(anglès)
  285. paquet type-nat-solver al GitHub(anglès)
  286. TypeNats example(anglès)
  287. Estàndard Haskell2010 - Signed integer types(anglès)
  288. GHC floatRange(anglès)
  289. 289,0 289,1 Mòduls mútuament recursius(anglès)

Vegeu també[modifica]

Enllaços externs[modifica]