module Ramus.Signal where
import Prelude hiding (filter)
import Control.Applicative ()
import Control.Monad (unless, when)
import Data.Functor ()
import Data.Semigroup
import Data.Foldable
import Data.Maybe
import Data.IORef
import System.IO.Unsafe
data Signal a = Signal
{ get :: a
, set :: a -> IO ()
, subscribe :: (a -> IO ()) -> IO ()
}
unsafeRef :: a -> IORef a
unsafeRef = unsafePerformIO . newIORef
unsafeRead :: IORef a -> a
unsafeRead = unsafePerformIO . readIORef
make :: a -> Signal a
make initial = unsafePerformIO $ do
subs <- newIORef [] :: IO (IORef [a -> IO()])
val <- newIORef initial
let _get = unsafeRead val
let _set newval = do
writeIORef val newval
forM_ (unsafeRead subs) $ \sub ->
sub newval
let _subscribe sub = do
currentSubs <- readIORef subs
_val <- readIORef val
writeIORef subs $ currentSubs <> [sub]
sub _val
return Signal
{ get = _get
, set = _set
, subscribe = _subscribe
}
constant :: a -> Signal a
constant = make
merge :: Signal a -> Signal a -> Signal a
merge sig1 sig2 = unsafePerformIO $ do
let out = constant $ get sig1
sig2 `subscribe` set out
sig1 `subscribe` set out
return out
mergeMany :: (Functor f, Foldable f) => f (Signal a) -> Maybe (Signal a)
mergeMany sigs = foldl mergeMaybe Nothing (Just <$> sigs)
where mergeMaybe a Nothing = a
mergeMaybe Nothing a = a
mergeMaybe (Just a) (Just b) = Just (merge a b)
foldp :: (a -> b -> b) -> b -> Signal a -> Signal b
foldp fun seed sig = unsafePerformIO $ do
acc <- newIORef seed
let out = make seed
sig `subscribe` \val -> do
acc' <- readIORef acc
writeIORef acc $ fun val acc'
acc'' <- readIORef acc
out `set` acc''
return out
sampleOn :: Signal a -> Signal b -> Signal b
sampleOn = undefined
dropRepeats :: (Eq a) => Signal a -> Signal a
dropRepeats sig = unsafePerformIO $ do
let val = get sig
let out = make val
sig `subscribe` \newval ->
unless (val == newval) (out `set` val)
return out
runSignal :: Signal (IO ()) -> IO ()
runSignal sig = do
sig `subscribe` \val -> val
unwrap :: Signal (IO a) -> IO (Signal a)
unwrap = undefined
filter :: (a -> Bool) -> a -> Signal a -> Signal a
filter fn seed sig = unsafePerformIO $ do
let out = make (if fn (get sig) then get sig else seed)
sig `subscribe` \val ->
when (fn val) (out `set` val)
return out
filterMap :: (a -> Maybe b) -> b -> Signal a -> Signal b
filterMap f def sig = fromMaybe def <$> filter isJust (Just def) (f <$> sig)
infixl 4 ~>
(~>) :: Signal a -> (a -> b) -> Signal b
sig ~> f = fmap f sig
instance Functor Signal where
fmap fun sig = unsafePerformIO $ do
let out = make $ fun $ get sig
sig `subscribe` \val -> out `set` fun val
return out
instance Applicative Signal where
pure = constant
fun <*> sig = unsafePerformIO $ do
let f = get fun
let out = make $ f (get sig)
let produce = const $ out `set` f (get sig)
fun `subscribe` produce
sig `subscribe` produce
return out
instance Semigroup (Signal a) where
(<>) = merge