Eu tenho um programa como este,
start :: [Q] -> R -> IO R
start qs = fix $ \recurse r -> do
q <- select qs
(r', exit) <- askQ q r
(if exit
then return
else recurse) r'
que pega uma lista de Q
perguntas, uma R
eport, e retorna uma nova R
eport, na IO
mônada porque select
precisa escolher uma pergunta aleatoriamente (e também porque askQ
vai esperar pela entrada do teclado do usuário); no entanto, o usuário não escolheu exit
durante a execução askQ
, start
irá chamar a si mesmo recursivamente. ( fix $ \recurse
é o truque para escrever um lambda recursivo. )
O código acima se parece muito com algumas coisas:
- a
State
mônada , ou, mais apropriadamente, oStateT
transformador de mônada , porqueR
está evoluindo durantestart
a recursão de; - o
forever
combinador de aplicativos, porquestart
é recursivo e pode potencialmente ser executado para sempre, se o usuário desejar (ou seja, se ele nunca pedir para sair); - o
MaybeT
transformador mônada , porqueMaybe
implementaMonadPlus
que deve poder entrar em curto-circuitoforever
, com base no que li antesforever
da implementação .
Mas não é possível dizer se alguma ou mais dessas abstrações podem ser usadas para escrever o código acima de uma forma mais idiomática, evitando o mais importante a recursão explícita.
Bem, o resultado seria algo como:
Isso pressupõe que
select
easkQ
será reescrito para ser executado naM
mônada em vez deIO
:O resultado é muito... sucinto.
Um exemplo independente, para a posteridade...
Parece funcionar:
Como tenho brincado com esquemas de recursão ultimamente, pensei em tentar um hilomorfismo monádico
É quase certo que há espaço para melhorias aqui. (Pensando bem, tenho quase certeza de que a lista de relatórios ruins não está "fundida".)