Tengo algo de experiencia escribiendo herramientas pequeñas en Haskell y me parece muy intuitivo de usar, especialmente para escribir filtros (que usan interact
) que procesan su entrada estándar y la canalizan a la salida estándar.
Recientemente intenté usar uno de esos filtros en un archivo que era aproximadamente 10 veces más grande de lo habitual y recibí un Stack space overflow
error.
Después de leer un poco (por ejemplo, aquí y aquí ) he identificado dos pautas para ahorrar espacio en la pila (Haskellers experimentados, corríjanme si escribo algo que no es correcto):
- Evite las llamadas de función recursivas que no son recursivas de cola (esto es válido para todos los lenguajes funcionales que admiten la optimización de llamada de cola).
- Presente
seq
para forzar la evaluación temprana de las subexpresiones para que las expresiones no crezcan demasiado antes de que se reduzcan (esto es específico para Haskell, o al menos para los idiomas que usan una evaluación diferida).
Después de introducir cinco o seis seq
llamadas en mi código, mi herramienta vuelve a funcionar sin problemas (también en los datos más grandes). Sin embargo, creo que el código original era un poco más legible.
Como no soy un programador experimentado de Haskell, quería preguntar si presentarlo seq
de esta manera es una práctica común y con qué frecuencia se verá normalmente seq
en el código de producción de Haskell. ¿O hay alguna técnica que permita evitar usarlo con seq
demasiada frecuencia y aún así usar poco espacio en la pila?
Respuestas:
Desafortunadamente, hay casos en los que uno tiene que usar
seq
para obtener un programa eficiente / que funcione bien para datos grandes. Entonces, en muchos casos, no puede prescindir de él en el código de producción. Puede encontrar más información en Real World Haskell, Capítulo 25. Creación de perfiles y optimización .Sin embargo, hay posibilidades de cómo evitar el uso
seq
directo. Esto puede hacer que el código sea más limpio y robusto. Algunas ideas:interact
. Se sabe que Lazy IO tiene problemas con la administración de recursos (no solo la memoria) y los iteratees están diseñados exactamente para superar esto. (Sugeriría evitar la E / S diferida por completo sin importar cuán grandes sean sus datos; consulte El problema con la E / S diferida ).seq
directamente (o diseñar sus propios) combinadores como foldl ' o foldr' o versiones estrictas de bibliotecas (como Data.Map.Strict o Control.Monad.State.Strict ) que están diseñadas para cálculos estrictos.seq
con estricta coincidencia de patrones. Declarar campos de constructor estrictos también podría ser útil en algunos casos.rseq
) o NF completo (rdeepseq
). Existen muchos métodos de utilidad para trabajar con colecciones, combinar estrategias, etc.fuente
ByteString
.