UnaIntroduccinagradableaHaskell
anteriorsiguienteinicio
7 Entrada/Salida
Elsistemadeentrada/salida(E/S)deHaskellesfuncionalpuro,yadems,proporcionatodalaexpresividad
quepresentanloslenguajesdeprogramacinimperativosconvencionales.Enloslenguajesimperativos,los
programasestnformadosporaccionesqueexaminanymodificanelestadoactualdelsistema.Algunas
accionestpicassonlalecturaymodificacindevariablesglobales,laescrituraenficheros,lalecturadedatos
yelmanejodeventanasenentornosgrficos.Haskellpermiteutilizaraccionesdeestetipo,aunqueestn
claramenteseparadasdelncleopuramentefuncionaldellenguaje.
ElsistemadeE/SdeHaskellestbasadoenunfundamentomatemticoquepuedeasustaraprimeravista:
lasmnadas.Sinembargo,noesnecesarioconocerlateorademnadassubyacenteparaprogramar
usandoelsistemadeE/S.Msbien,lasmnadassonsimplementeunaestructuraconceptualenlasquelas
E/Sencaja.NoesnecesarioconocerlateorademnadasparatrabajarconoperacionesdeE/SenHaskell
delmismomodoquenoesnecesarioconocerlateoradegruposparatrabajarconoperacionesaritmticas
simples.Unaexplicacinenprofundidaddelasmnadaspuedeencontrarseenlaseccin9.
LosoperadoresmondicosenlosqueelsistemadeE/Sestbasadosonusadostambinparaotros
propsitosprofundizaremosenlasmnadasposteriormente.Porahora,evitaremoseltrminomnadaynos
concentraremosenelusodelsistemadeE/S.EsmejorpensarenlamnadadeE/Scomountipoabstracto.
Lasaccionessondefinidas,peronoinvocadas,alniveldelamquinadeevaluacinquedirigelaejecucin
deunprogramaHaskell.Laevaluacindeladefinicindeunaaccinnohacequelaaccinsearealizada.
Msbien,laejecucindeaccionesesefectuadaenunniveldistintoalaevaluacindeexpresionesque
hemosconsideradohastaestemomento.
Lasaccionessonatmicas,comoelcasodelasdefinidascomoprimitivasdelsistema,oseobtienendela
composicinsecuencialdeotrasacciones.LamnadadeE/Scontieneprimitivasquepermitenconstruir
accionescompuestas,unprocesosimilaralquerealizanalgunoslenguajesimperativosalunirsentenciasdeun
modosecuencialusando";".Estamnadaactacomounpegamentoqueunelasaccionesqueformanparte
deunprograma.
7.1 OperacionesdeE/Sbsicas
CadaaccindeE/Sdevuelveunvalor.Aniveldelsistemadetipos,elvalordevueltoesanotadoconuntipo
IO.Estodistinguelasaccionesdeotrosvalores.Porejemplo,eltipodelafuncingetChares:
getChar :: IO Char
EltipoIO CharindicaquegetChar,cuandoseaejecutado,realizarunaaccinquedevolveruncarcter.
Lasaccionescuyovalordevueltonoesinteresanteusaneltipounitario().Porejemplo,lafuncinputChar:
tomauncarctercomoargumentoperonodevuelvenadatil.Eltipounitarioessimilaraltipovoidenotros
lenguajes.
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 1/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
Lasaccionessoncombinadassecuencialmenteusandounoperadorcuyonombreesuntantocrptico:>>=(o
"bind").Envezdeusaresteoperadordirectamente,usaremosunasintxismsclara,lanotacindo,para
ocultarelusodelosoperadoresdesecuenciacingraciasalusodeunasintaxisquerecuerdaaladelos
lenguajesimperativosconvencionales.Lanotacindopuedesertraducida,deunmodotrivial,afunciones
Haskellnormales,comosedescribeen3.14.
Lapalabraclavedoiniciaunasecuenciadesentenciasquesernejecutadasenorden.Unasentenciaeso
bienunaaccin,ounpatrnasociadoalresultadodeunaaccinusando<-,ounadefinicinlocaldefinida
mediantelet.Lanotacindousalaindentacindelprogramadelmismomodoquelasdefinicioneslocales
introducidasconletowhere,demodoquepodemosomitirlasllavesylospuntosycomausandoun
sangradoadecuado.Elsiguienteejemploesunprogramaqueleeyescribeuncarcter:
main :: IO ()
main = do c <- getChar
putChar c
Elusodelnombremainesimportante:maineslaexpresinprincipaldeunprogramaHaskell(deunmodo
similaralafuncinmaindeunprogramaenC),ydebeteneruntipoIOm,usualmenteIO ().(Elnombre
mainesespecialtansoloenelmduloMainhablaremosdelosmdulosposteriormente).Esteprograma
ejecutadosaccionesensecuencia:primeroleeuncarcterdelteclado,ligandoelresultadoconlavariablec,
yacontinuacinimprimeelcarcter.Adiferenciadelasexpresionesletparalasquelasvariables
introducidasestnenelmbitodetodaslasdefiniciones,lasvariablesdefinidascon<-soloestnenel
mbitodelassentenciasposteriores.
Todavafaltaalgo.Podemosejecutaraccionesyexaminarsusresultadosusandolanotacindo,perocmo
devolvemosunvalordesdeunasecuenciadeacciones?Porejemplo,considreselafuncinreadyquelee
uncarcterydevuelveTruesidichocarcteres`y':
ready :: IO Bool
ready = do c <- getChar
c == 'y' -- Mal !!!
Ladefinicinanteriornoescorrectaporquelasegundasentenciaesunvalorbooleano,ynounaaccin.Es
necesariocrearunaaccinquedevuelvaunbooleanocomoresultado.Lafuncinreturn hace
precisamenteesto:
return :: a -> IO a
Lafuncinreturncompletaelconjuntodeprimitivas.Laltimalneadeladefinicindereadydeberaser
return (c == 'y').
AhorapodemosverejemplosdeE/Smscomplicados.Enprimerlugar,lafuncingetLine:
getLine :: IO String
getLine = do c <- getChar
if c == '\n'
then return ""
else do l <- getLine
return (c:l)
Obsrveseelsegundodoenlaparteelse.Cadadoiniciaunasecuenciadesentencias.Cualquierotra
construccinqueformepartedelafuncin,comoifenelejemplo,debeusarunnuevodoparainiciarotras
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 2/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
secuenciasdeacciones.
LafuncinreturndaentradaaunvaloreneldominiodelasaccionesdeE/S.Pero,qupasaconla
direccininversa?esposibleinvocaralgunaaccindeE/Sdesdeunaexpresinnormal?Porejemplo,
cmopodemosexpresarx + print yenunaexpresindemodoqueyseaimprimidaalevaluarla
expresin?Larespuestaesqueestonoesposible.Noesposibleadentrarseeneluniversoimperativoen
mitaddecdigofuncionalpuro.Cualquiervalor`infectado'poraccionesimperativasdebeseretiquetado
comotal(usandoeltipoIO).Unafuncincomo
nopuederealizarningunaE/SyaqueIOnoformapartedeltipodevuelto.Estehechoeshabitualmente
desconsoladorparaprogramadoresacostumbradosacolocarsentenciastipoprintliberalmentealolargodel
cdigodeunprogramadurantelafasededepuracin.Realmente,existenfuncionespeligrosas(rompenel
carcterpurodellenguaje)paraestoscasos,aunqueesmejorreservarelusodestasaprogramadores
avanzados.Lospaquetesdedepuracinsuelenhacerunusoliberaldeestas"funcionesprohibidas"deun
modototalmenteseguro.
7.2 Programandoconacciones
LasaccionesdeE/SsonvaloresnormalesenHaskell:puedenserpasadoscomoargumentosafunciones,
puedenformarpartedeestructuras,yengeneral,serusadoscomocualquierotrovalor.Considreseesta
listadeacciones:
Estalistanoejecutaningunaaccinsimplementelasalmacena.Paraenlazarestasaccionesydarlugarauna
nicaaccin,esnecesariaunafuncincomosequence_:
Lanotacindoestilperoenestecasoeloperadormondicosubyacente,>>,esmsapropiado.El
conocimientodelosoperadoresutilizadosparatraducirlanotacindopuedesermuytilparael
programador.
Lafuncinsequence_puedeserusadaparaconstruirputStrapartirdeputChar:
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 3/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
UnadelasdiferenciasentreHaskellylaprogramacinimperativaconvencionalpuedeverseenladefinicin
deputStr.Enunlenguajeimperativo,laaplicacindeunaversinimperativadeputCharsobreunacadena
decaracteresdaralugaraquestaseimprimiese.EnHaskell,lafuncinmapnoproduceefectoalguno.
Simplementecreaunalistadeacciones,unaporcadacarcterenlacadena.Laoperacindeplegadoenla
funcinsequence usaeloperador>>paracombinartodaslasaccionesindividualesdandolugarauna
accinnica.Laexpresinreturn ()usadaestotalmentenecesariafoldrnecesitaunaaccinnulaal
finaldelacadenadeaccionesquecrea(sobretodosilacadenadecaracteresestvaca!).
ElPreludeylasbibliotecasdellenguajecontienenvariasfuncionestilesparacombinaraccionesdeE/S.La
mayoradeestasfuncionesestngenrizadasamnadasarbitrariascualquierfuncinqueincluyaelcontexto
Monad m => ensutipopuedeserusadaconeltipoIO(yaquesteesunainstanciadelaclaseMonad).
7.3 Tratamientodeexcepciones
Hastaahora,hemosevitadoeltratamientodeexcepcionesenlasoperacionesdeE/S.Pero,quocurrirasi
getCharseencontraseelfinaldeunfichero?(Usaremoseltrminoerrorparadenotarelvalor_|_:unerror
norecuperablecomolanoterminacinounerrordepatrones.Lasexcepciones,porotrolado,puedenser
capturadasytratadasdentrodelamnadadeE/S.)Unmecanismodetratamiento,parecidoaldeStandard
ML,esusadoparatratarexcepcionestalescomo"ficheronoexistente".Noseusaningunasintaxiso
semnticaespecialeltratamientodeexcepcionesespartedeladefinicindelasoperacionesdeE/S.
LoserroressoncodificadosusandountipodedatosdenominadoIOError.Estetiporepresentatodaslas
posiblesexcepcionesquepuedenocurriralejecutaroperacionesdelamnadadeE/S.Eltipoesabstracto:
losconstructoresnoestndisponiblesparaelusuario.Algunospredicadospermiteninspeccionardatosdel
tipoIOError.Porejemplo,lafuncin
determinasielerrorquetomacomoargumentofuecausadoporunacondicindefinaldefichero.Alserel
tipoabstracto,losdiseadoresdellenguajepuedenaadirnuevostiposdeerroresalsistemasinnotofocarel
cambioenlaimplementacindeltipo.
Losargumentosdecatchsonunaaccinyelcorrespondientemanejador.Silaaccinconcluyesinerror,el
resultadodestaesdevueltosininvocaralmanejador.Enotrocaso,siseproduceunerror,steespasado
almanejadorcomounvalordetipoIOErrorylaaccinasociadaconelmanejadoresinvocada.Por
ejemplo,lasiguienteversindegetChardevuelveelcarctercorrespondientealsaltodelneacuandose
produceunerror:
getChar' :: IO Char
getChar' = getChar `catch` (\e -> return '\n')
Estoesbastantetoscoyaqueelmanejadortratacualquiererrordelmismomodo.Sisolosequierentratar
loserroresprovocadosporelfinaldeunfichero,elerrordebeserexaminado:
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 4/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
getChar' :: IO Char
getChar' = getChar `catch` eofHandler where
eofHandler e = if isEofError e then return '\n' else ioError e
LafuncinioErrorelevaunaexcepcin,quepodrsertratadaporotromanejador.EltipodeioErrores
Esparecidaareturnperohacequeelcontrolsetransfieraalmanejadordeexcepcionesenvezdeala
siguienteoperacindeE/S.Elusoanidadodecatchestpermitido,ydalugaramanejadoresde
excepcionesanidados.
UsandogetChar',podemosredefinirgetLinecomoejemplodelusodemanejadoresanidados:
getLine' :: IO String
getLine' = catch getLine'' (\err -> return ("Error: " ++ show err)) where
getLine'' = do c <- getChar'
if c == '\n' then return ""
else do l <- getLine'
return (c:l)
ElmanejadordeexcepcionesanidadopermitequegetChar'trateloserroresdefindefichero,mientrasque
otroserroreshacenquegetLine'devuelvaunacadenadecaracterescomenzadopor"Error: ".
Haskelldefineunmanejadordeexcepcionespordefectoenelnivelmsexternodeunprogramacuyo
comportamientoconsisteenimprimirunmensajeconlaexcepcinproducidayfinalizarelprograma.
7.4 Ficheros,CanalesyManejadores
ApartedelusodelamnadadeE/Sydelmecanismodeexcepciones,elconjuntodeoperacionesdeE/S
proporcionadasporHaskellesmuyparecidoaldeotroslenguajes.Muchasdeestasoperacionesestn
definidasenlabibliotecaIOyporellodebenimportarseexplcitamenteparaserusadas(losmdulosyla
importacindeelementossonestudiadosenlaseccin11).Adems,muchasdeestasfuncionesson
descritasenelinformedelasbibliotecasdellenguajeenvezdeenelinformedellenguaje.
Laaperturadeunficheropermiteobtenerunmanejador(handle),contipoHandle,quepuedeserusado
paraoperacionesdeE/S.Alcerrarelmanejador,secierraelficheroasociado:
Losmanejadorestambinpuedenserasociadosconcanales(channels):puertosdecomunicacinqueno
estnconectadosdirectamenteconficheros.Algunosmanejadoresdeficherosestnpredefinidos,comopor
ejemplostdin(laentradaestndar),stdout(lasalidaestndar),ystderr(elcanaldeerroresestndar).
DosoperacionesdeE/SaniveldecaracteressonhGetCharyhPutChar,quetomanunmanejadorcomo
argumento.LafuncingetCharusadapreviamentepuedeserdefinidadelsiguientemodo:
Haskelltambinpermiteleerelcontenidocompletodeunficheroocanalcomounacadenadecaracteres:
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 5/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
getContents :: Handle -> String
PuedeparecerquegetContentsdebeleerinmediatamentetodoelcontenidodelficheroocanal,dando
lugaraunrendimientopobredesdeelpuntodevistadelespaciodememoriaytiempoutilizados.Sin
embargo,estonoesloqueocurre.EstosedebeaquegetContentsdevuelveunalistadecaracteres
"perezosa"(recurdesequelascadenasdecaracteressonlistasdecaracteresenHaskell),cuyoselementos
sonledosdelfichero"bajodemanda"(delmismomodoquesongeneradaslaslistasnormales).Puede
esperarsequelasimplementacionesdeestafuncinleanunoporunoloscaracteresdelficherosegnson
necesitadosparaproseguirelcmputorealizadoconlacadena.
Enelsiguienteejemplo,secopiaelcontenidodeunficheroenotro:
AlusarlafuncinperezosagetContents,noesnecesarioleertodoelcontenidodelficheroenmemoriade
unavez.SihPutStrutilizaseunbufferparaescribirlacadenaenbloquesdetamaofijo,solounodeestos
bloquesdecaractereshadepermanecerenmemoriasimultneamente.Elficherodeentradaescerradode
modoimplcitocuandoelltimocarcteresledo.
7.5 Haskellylaprogramacinimperativa
LaprogramacindeoperacionesdeE/Spuededarlugaralasiguientereflexin:elestiloutilizadoseparece
sospechosamentealdelaprogramacinimperativa.Porejemplo,lafuncingetLine:
guardaunasimilitudestrechaconelsiguientecdigoimperativo(quenoestescritoenningnlenguaje
concreto):
function getLine() {
c := getChar();
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 6/7
29/11/2014 UnaintroduccinagradableaHaskell:Entrada/Salida
As,despusdetodo,hareinventadoHaskellsimplementelaruedaimperativa?
Enciertosentido,larespuestaess.LamnadadeE/Sconstituyeunpequeosublenguajeimperativo
dentrodeHaskell,demodoquelacomponentedeE/Sdeunprogramapuedeparecersimilaralcdigode
unlenguajeimperativohabitual.Peroexisteunadiferenciaimportante:noesnecesariaunasemnticaespecial
paraello.Concretamente,elrazonamientoecuacionalnodejadeservlido.Laaparienciaimperativadel
cdigomondiconodenigraelaspectofuncionaldeHaskell.Unprogramadorfuncionalexpertodebeser
capazdeminimizarlacomponenteimperativadeunprograma,usandonicamentelamnadadeE/Senuna
partemnimadelprograma.Lamnadaclaramenteseparalascomponentesimperativayfuncionaldel
programa.Porelcontrario,loslenguajesimperativosconsubconjuntosfuncionalesnoestablecenunabarrera
biendefinidaentrelapartefuncionalpuraylaparteimperativadelprograma.
UnaIntroduccinagradableaHaskell
anteriorsiguienteinicio
http://www.lcc.uma.es/~blas/pfHaskell/gentle/io.html 7/7