Después de desarrollar varias aplicaciones de Haskell, me encontré segregando rigurosamente el código impuro y las funciones de fallas ( parciales ) de sus contrapartes puras y totales . Estos esfuerzos han reducido notablemente el costo de mantenimiento asociado con las aplicaciones. Con el tiempo me he encontrado confiando en la misma main
estructura de alto nivel para hacer cumplir esta segregación.
En general, mi main
tendrá la siguiente estructura:
import System.Environment
data ProgramParameters = P ()
data ComputationResult = I ()
main :: IO ()
main = getArgs -- Collect arguments
>>= andOrGetUserInput -- Collect user input
>>= impureOrFailableComputations -- Possible non-recoverable error(s)
>>= either -- "Branch"
putStrLn -- Print Any Failure(s)
pureNotFailableComputations -- Finish the work
andOrGetUserInput :: [String] -> IO ProgramParameters
andOrGetUserInput = undefined
impureOrFailableComputations :: ProgramParameters -> IO (Either String ComputationResult)
impureOrFailableComputations = undefined -- a composition of partial functions
-- made total by catching exceptions & input errors
-- in the short-circuiting ErrorT/EitherT monad
pureNotFailableComputations :: ComputationResult -> IO ()
pureNotFailableComputations = undefined -- a composition of total functions
El objetivo es unir cálculos parciales en una mónada, creando un cálculo monádico total.
Esto se ha convertido en un patrón en la base del código, y me gustaría recibir comentarios sobre si se trata de un patrón de diseño o un antipatrón .
¿Es esta una forma idiomática de segregar y atrapar cálculos parciales?
¿Hay inconvenientes notables en esta segregación de alto nivel?
¿Hay mejores técnicas de abstracción?
fuente