Dopo aver sviluppato diverse applicazioni Haskell, mi sono trovato a separare rigorosamente le funzioni impure di codice e fessibile ( parziale ) dal loro puro & totale controparti. Questi sforzi hanno notevolmente ridotto i costi di manutenzione associati alle applicazioni. Mi sono trovato nel tempo a fare affidamento sulla stessa struttura di alto livello main
per far rispettare questa segregazione.
In generale, il mio main
avrà la seguente struttura:
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
L'obiettivo è quello di combinare calcoli parziali in una monade, creando un calcolo monadico totale.
Questo è diventato uno schema nella base di codice, e vorrei un feedback sul fatto che si tratti di un schema di progettazione o un anti-pattern .
-
È un modo idiomatico per separare e amp; prendere calcoli parziali?
-
Ci sono notevoli svantaggi in questa segregazione di alto livello?
-
Esistono tecniche di astrazione migliori?