@iris: :help
iris: - help
- load_metric
...
@iris: load_metrics
iris: loading now...
iris: 12345
dispatch :: ChannelId -> T.Text -> SlackAction ()
dispatch cid msg =
case T.parse irisCmd "" msg of
Left err -> sendMessage cid (T.pack "uncomprehensible command")
Right cmd -> liftIO $ run cid cmddata IrisCommand next = Help
| LoadMetric
deriving (Eq, Show)run :: ChannelId -> IrisCommand -> SlackAction ()
run cid (Help) = sendMessage cid (T.pack ":help")
run cid (LoadMetric) = do
sendMessage cid (T.pack "loading now...")
l <- liftIO getDailyMetric
let resp = T.pack $ show l
sendMessage cid respsendMessage, which results to a call to Slack API.expect a method call.It fallback to IO Monad. Is there an alternative before we use the last resort?
How about.. test it semantically? With the structure of AST, we know how the thing would happen.
String, and decoding them into FunctionFunctorvar commands_in_str = ["ReplyLoading", "LoadMetrics"];
//after parsing
replyLoading = function() {
console.log("in replayLoading");
};
loadMetrics = function() {
console.log("in loadMetrics");
};
// functor
var commands = [replyLoading, loadMetrics];
commands.map(function(f) { f.call(this); });
//cheating by using window
commands_in_str.map(function(fname) { window[fname].call(this); });Functor, there exists an implicit Structure for Freesequence compose the evaluation order.// monad for free, visitor pattern
function node(type, next) {
this.type = type;
this.next = next ? next : null;
}
node.prototype.accept = function(visitorObj) {
visitorObj.visit(this);
if (this.next) this.next.accept(visitorObj);
};Functor as Abstract Syntax Tree, and that stucture is actually a Monad. It's visitor pattern in OO term.function visitor() {
var that = this;
this.visit = function(tgt) {
if (tgt.type == "replyLoading") {
console.log("replyLoading");
}
if (tgt.type == "loadMetrics") {
console.log("loadMetrics");
}
};
this.walk = function(tgt) {
tgt.accept(that);
};
}
var head = new node("replyLoading", (new node("loadMetrics")));
(new visitor()).walk(head);data IrisCommand next = Help'
| LoadMetric'
| ReplyLoading' next
deriving (Functor, Eq, Show)
type IrisCommandM = Free IrisCommand
makeFree ''IrisCommanddata [a] = [] | a : [a]data Free f a = Pure a | Roll (f (Free f a))runIris :: ChannelId -> IrisCommandM () -> SlackAction ()
runIris cid = iterM run
where
run :: IrisCommand (SlackAction ()) -> SlackAction ()
run (Help') = sendMessage cid (T.pack ":help")
run (LoadMetric')= do
l <- liftIO getDailyMetric
let resp = T.pack $ show l
sendMessage cid resp
run (ReplyLoading' n) = do
sendMessage cid (T.pack "loading now...")
ntype FakeResponse = [T.Text]
runIrisTest :: MonadState FakeResponse m => IrisCommandM () -> m ()
runIrisTest = iterM run
where
run :: MonadState FakeResponse m => IrisCommand (m ()) -> m ()
run (Help') = modify (λl -> T.pack ":help" : l)
run (LoadMetric') = modify (λl -> T.pack "123456" : l)
run (ReplyLoading' n) = do
modify (λl -> T.pack "loading now..." : l)
nspec :: Spec
spec = do
describe "dispatch" $ do•
context "receive help" $ do
it "return help result" $ do
(runState (I.runIrisTest I.help') []) `shouldBe` ((), [":help"])
context "receive load_metric" $ do
it "return load_metric result" $ do
(runState (I.runIrisTest (I.replyLoading' >> I.loadMetric')) []) `shouldBe` ((), ["123456", "loading now..."])