Ho deciso di provare la programmazione funzionale e Purescript. Dopo aver letto "Learn you a Haskell for great good"
e "PureScript by Example"
e giocando un po 'con il codice penso di poter dire di aver capito le basi, ma una cosa mi preoccupa molto: il codice sembra molto accoppiato. È normale che io cambi le librerie molto spesso e in OOP posso usare l'architettura di cipolla per disaccoppiare il mio codice da quello specifico della libreria, ma non ho idea di come farlo in Purescript.Come strutturare l'app in purescript
Ho cercato di scoprire come le persone lo fanno in Haskell, ma tutto quello che ho potuto trovare erano risposte come "Nessuno ha mai creato app complesse in Haskell, quindi nessuno sa come farlo" o "Hai inserito e tu hai prodotto, tutto nel fratempo sono solo funzioni pure ". Ma in questo momento ho un'app giocattolo che usa dom virt, segnali, memoria web, librerie router e ognuno di essi ha i propri effetti e strutture dati, quindi non sembra un input e un output.
Quindi la mia domanda è come dovrei strutturare il mio codice o quale tecnica dovrei usare in modo da poter cambiare le mie librerie senza riscrivere metà della mia app?
Aggiornamento:
suggerimento di utilizzare diversi strati e mantenere gli effetti nel modulo principale è abbastanza comune troppo e capisco perché dovrei farlo.
Ecco un semplice esempio che si spera illustrare il problema che sto parlando:
btnHandler :: forall ev eff. (MouseEvent ev) => ev -> Eff (dom :: DOM, webStorage :: WebStorage, trace :: Trace | eff) Unit
btnHandler e = do
btn <- getTarget e
Just btnId <- getAttribute "id" btn
Right clicks <- (getItem localStorage btnId) >>= readNumber
let newClicks = clicks + 1
trace $ "Button #" ++ btnId ++ " has been clicked " ++ (show newClicks) ++ " times"
setText (show newClicks) btn
setItem localStorage btnId $ show newClicks
-- ... maybe some other actions
return unit
-- ... other handlers for different controllers
btnController :: forall e. Node -> _ -> Eff (dom :: DOM, webStorage :: WebStorage, trace :: Trace | e) Unit
btnController mainEl _ = do
delegateEventListener mainEl "click" "#btn1" btnHandler
delegateEventListener mainEl "click" "#btn2" btnHandler
delegateEventListener mainEl "click" "#btn3" btnHandler
-- ... render buttons
return unit
-- ... other controllers
main :: forall e. Eff (dom :: DOM, webStorage :: WebStorage, trace :: Trace, router :: Router | e) Unit
main = do
Just mainEl <- body >>= querySelector "#wrapper"
handleRoute "/" $ btnController mainEl
-- ... other routes each with it's own controller
return unit
Qui abbiamo semplice applicazione contatore con il routing, WSS, manipolazioni dom e la registrazione della console. Come puoi vedere non c'è un singolo input e una singola uscita. Possiamo ottenere input dagli ascoltatori di router o eventi e utilizzare console o dom come output, quindi diventa un po 'più complicato.
Avere tutto questo codice effectful nel modulo principale si sente male per me per due motivi:
- Se voglio continuare ad aggiungere percorsi e controllori di questo modulo si trasformerà rapidamente in un migliaio di linea di casino.
- Keeping routing, manipolazioni dom e memorizzazione dei dati nello stesso modulo viola principio di singola responsabilità (e presumo che è importante in FP troppo)
Possiamo dividere questo modulo in altri più, ad esempio un modulo per controller e creare un qualche tipo di strato efficace. Ma poi quando avrò dieci moduli controller e voglio cambiare la mia specifica lib di dom, dovrei modificarli tutti.
Entrambi questi approcci sono lontani dall'ideale, quindi la domanda è la prima che dovrei scegliere? O forse c'è un altro modo per andare?
Grazie per la risposta. Ho modificato la mia domanda e ho aggiunto un semplice esempio. – starper