Coming from Odessa, Ukraine
Doing Haskell for 6+ years
Now work for
Student counseling through automated text messages
Messaging campaigns are written in an in-house DSL
How do you test them?
You create a simulator!
Have the server and simulator use the same code
Effect | Simulator | Server |
---|---|---|
Logging | Yes | Yes |
Current time | Yes | Yes |
SMS send/recv | Yes | Yes |
Console I/O | Yes | No |
MQ | No | Yes |
handleMessage
:: ( MonadWriter LogEntry m
, MonadWriter MessageText m
, MonadState Image m
, MonadReader Messages m
, MonadReader Now m
, MonadReader SQL.Connection m
, MonadExcept DynamicTypeError m
, MonadExcept EvalError m
, MonadExcept ProgramError m
) => m ()
handleMessage = ...
data Nat = Zero | Suc Nat
class MonadReaderN (n :: Nat) r m where
askN :: Proxy n -> m r
instance MonadReaderN Zero e (ReaderT e m) where
askN _ = ask
instance (MonadTrans t,
MonadReaderN n r m)
=> MonadReaderN (Suc n) r (t m)
where
askN _ = lift $ askN (Proxy :: Proxy n)
type family
Find
(t :: (* -> *) -> (* -> *))
(m :: * -> *)
:: Nat where
Find t (t m) = Zero
Find t (p m) = Suc (Find t m)
type MonadReader r m =
MonadReaderN (Find (ReaderT r) m) r m
ask :: forall r m. MonadReader r m => m r
ask = askN (Proxy :: Proxy (Find (ReaderT r) m))
type family
CanDo
(t :: (* -> *))
(eff :: k)
:: Bool
data EffState s
data EffReader e
data EffWriter w
type family Find eff (m :: * -> *) :: Nat
foo :: (MonadState User m, ...) => m ()
bar :: (MonadReader User m, ...) => m ()
baz = do
foo
bar
type instance CanDo (StateT s m) eff = StateCanDo s eff
type family StateCanDo s eff where
StateCanDo s (EffState s) = True
StateCanDo s (EffReader s) = True
StateCanDo s eff = False
instance MonadReaderN Zero r (StateT r m) where
askN _ = get
newtype ZoomT big small m a = ZoomT (...)
deriving (Functor, Applicative, Monad, ...)
instance
MonadReader big m =>
MonadReaderN Zero small (ZoomT big small m)
runZoom
:: Lens big small
-> ZoomT big small m a
-> m a
runZoom l a = ...
x = runState get 0
No instance for (MonadStateN
(FindTrue
'[monad-classes-0.1:Control.Monad.Classes.State.StateCanDo
s0 (EffState a0),
CanDo Data.Functor.Identity.Identity (EffState a0)])
a0
(Control.Monad.Trans.State.Lazy.StateT
s0 Data.Functor.Identity.Identity))
arising from a use of ‘it’
The type variables ‘s0’, ‘a0’ are ambiguous
Note: there are several potential instances:
instance (Monad (t m), Control.Monad.Trans.Class.MonadTrans t,
MonadStateN n s m, Monad m) =>
MonadStateN ('Suc n) s (t m)
-- Defined in ‘monad-classes-0.1:Control.Monad.Classes.State’
instance Monad m =>
MonadStateN 'Zero s (Control.Monad.Trans.State.Lazy.StateT s m)
-- Defined in ‘monad-classes-0.1:Control.Monad.Classes.State’
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it