A inovação mais aparente observada por pessoas novas em Haskell é que existe uma separação entre o mundo impuro que se preocupa em se comunicar com o mundo exterior e o mundo puro da computação e algoritmos. Uma pergunta freqüente para iniciantes é "Como posso me livrar IO, ou seja, converter IO aem a?" O caminho para isso é usar mônadas (ou outras abstrações) para escrever código que execute efeitos de IO e encadeamentos. Esse código reúne dados do mundo exterior, cria um modelo, faz algumas computações, possivelmente empregando código puro, e gera o resultado.
No que diz respeito ao modelo acima, não vejo nada de errado em manipular GUIs na IOmônada. O maior problema que surge desse estilo é que os módulos não são mais compostáveis, ou seja, perco a maior parte do meu conhecimento sobre a ordem de execução global das instruções no meu programa. Para recuperá-lo, tenho de aplicar um raciocínio semelhante ao do código da GUI concorrente e imperativo. Enquanto isso, para um código não-GUI impuro, a ordem de execução é óbvia por causa da definição IOdo >==operador da mônada (pelo menos enquanto houver apenas um encadeamento). Para código puro, isso não importa, exceto em casos extremos para aumentar o desempenho ou evitar avaliações resultantes ⊥.
A maior diferença filosófica entre console e E / S gráfica é que os programas que implementam o primeiro são geralmente escritos em estilo síncrono. Isso é possível porque existe (deixando de lado os sinais e outros descritores de arquivos abertos) apenas uma fonte de eventos: o fluxo de bytes normalmente chamado stdin. As GUIs são inerentemente assíncronas e precisam reagir a eventos do teclado e cliques do mouse.
Uma filosofia popular de executar IO assíncrona de uma maneira funcional é chamada de Programação Reativa Funcional (FRP). Recentemente, houve muita tração em linguagens impuras e não funcionais, graças a bibliotecas como o ReactiveX e estruturas como o Elm. Em poucas palavras, é como visualizar elementos da GUI e outras coisas (como arquivos, relógios, alarmes, teclado, mouse) como fontes de eventos, chamadas "observáveis", que emitem fluxos de eventos. Estes eventos são combinados usando operadores conhecidos, como map, foldl, zip, filter, concat, join, etc., para produzir novos fluxos. Isso é útil porque o próprio estado do programa pode ser visto como scanl . map reactToEvents $ zipN <eventStreams>do programa, onde Né igual ao número de observáveis já considerados pelo programa.
Trabalhar com observáveis de FRP torna possível recuperar a composição porque os eventos em um fluxo são ordenados a tempo. O motivo é que a abstração do fluxo de eventos torna possível visualizar todos os observáveis como caixas pretas. Por fim, a combinação de fluxos de eventos usando operadores devolve algumas ordens locais na execução. Isso me obriga a ser muito mais honesto em relação aos invariantes em que meu programa realmente se baseia, semelhante à maneira como todas as funções no Haskell precisam ser referencialmente transparentes: se eu quiser extrair dados de outra parte do meu programa, preciso ser explícito. O anúncio declara um tipo apropriado para minhas funções. (A mônada de IO, sendo uma linguagem específica de domínio para escrever código impuro, evita isso efetivamente)