'How can we build a modular system in Haskell? [closed]
Below is a concept that I have applied in OOP, and I would like to apply in FP (specifically Haskell). Everything that I have read says it is possible, but I can find no good examples. I understand some of the logic is OOP based, but I can convey it easier that way.
Design Constraints:
modelscontain three types of behavior: receive data, send data, and contact some service outside of the program. Amodelcan receive and/or send data to othermodels, but there is no direct connection.- The
Engineis used to manage two of themodels behaviors: receive data, and send data. All data transfer between models is passed through theEngine. TheEngineis not passed to themodels; theEngineinspects eachmodelto find the relevant send/receive functions, which it processes and generates links between models. Data generated by amodelis available to anymodelthat wants to listen to it.
In OOP, some of this is very simple; each model is an object containing behavior and the required data to preform the calculations. The Engine is another object, and can inspect the attributes of each function in a model to determine it's purpose.
For example, I may have a system that contains a websocket datafeed, a GUI, and a email client; each is it's own model. When the websocket receives data it is processed and then passed to the Engine to be directed to subscribers. The GUI receives the data, and displays it. The email client receives the data, and will send an email once per day with an aggregation of that day's data. Eventually I may want to add a report generator that will receive data, and send out the results of a calculation to the Engine. I could then modify my email client to subscribe to this data, and include it in the daily email. This should require no code change in the Engine or any models other than the email client to add the additional functionality.
Solution 1:[1]
Here's a simple pub-sub engine:
import Control.Monad
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Concurrent.Chan
import Data.Foldable
data Engine a = Engine (Chan a) (MVar [Chan a]) ThreadId
mk :: IO (Engine a)
mk = do
c <- newChan
m <- newMVar []
t <- forkIO . forever $ do
a <- readChan c
subs <- readMVar m
traverse_ (flip writeChan a) subs
pure (Engine c m t)
pub :: Engine a -> a -> IO ()
pub (Engine c m t) a = writeChan c a
sub :: Engine a -> IO (Chan a)
sub (Engine c m t) = do
chan <- newChan
modifyMVar_ m (pure . (chan:))
pure chan
kill :: Engine a -> IO ()
kill (Engine c m t) = killThread t
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Daniel Wagner |
