<div dir="ltr">Thanks for the reply. That is an interesting alternative I hadn't considered. What I end up with applying it to flow charts is something like the following<div><br></div><div> data FlowChart m a where</div><div> Decision :: m Bool -> FlowChart m a -> FlowChart m a -> FlowChart m a</div><div> Done :: m a -> FlowChart m a</div><div><br></div><div> runFlowChart :: FlowChart m a -> m a</div><div> runFlowChart (Decision cond falsePath truePath) = cond >>= \c -> runFlowChart (if c truePath else falsePath)</div><div> runFlowChart (Done a) = a</div><div><br></div><div> s0 = Decision testS0 s1 s2</div><div> s1 = Done s1Result</div><div> s2 = Done s2Result</div><div><br></div><div>This is simpler than what I was trying to do. It should have all the same benefits as well. I'm going to give this a spin and see how it goes. Thanks!</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Dec 26, 2014 at 1:39 AM, Stephen Tetley <span dir="ltr"><<a href="mailto:stephen.tetley@gmail.com" target="_blank">stephen.tetley@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Richard<br>
<br>
It might be worth looking at Wyatt Allan and Martin Erwig's Surveyor<br>
DSEL for alternative design ideas. The authors directly encode<br>
questions rather than states which, without looking closely, seems<br>
more natural to me - I think you could envisage flow diagrams as<br>
specializations of "surveys".<br>
<br>
The paper is available from Martin's website:<br>
<br>
<a href="http://web.engr.oregonstate.edu/~erwig/papers/abstracts.html" target="_blank">http://web.engr.oregonstate.edu/~erwig/papers/abstracts.html</a><br>
<br>
Best wishes<br>
<br>
Stephen<br>
<br>
On 25 December 2014 at 23:34, Richard Wallace<br>
<div><div class="h5"><<a href="mailto:rwallace@thewallacepack.net">rwallace@thewallacepack.net</a>> wrote:<br>
> Hello,<br>
><br>
> I've been working to encode a fairly complex flow chart. At first I took the<br>
> simple approach of having a single data type for the possible states<br>
><br>
> data S = S0 | S1 | S2<br>
><br>
> and then a simple recursive function to step through it<br>
><br>
> loop :: Monad m => S -> StateT s m Result<br>
> loop S0 = testS0 >>= \r -> if r then loop S1 else loop S2<br>
> loop S1 = s1Result<br>
> loop S2 = s2Result<br>
><br>
> This works well enough, but with the size of the flow chart I'm working with<br>
> (50+ decision points) it quickly becomes clear that it's probably not the<br>
> most best way to do it. One problem that I'd like to solve is being able to<br>
> test each decision point in isolation, making sure that decision point `m`<br>
> will go to decision point `n` when it is supposed to, without having to run<br>
> through the whole rest of the flow chart that would otherwise follow.<br>
> Something else I'd like to achieve is being able to organize the decisions<br>
> into separate modules - much better than having just the one big function<br>
> above.<br>
><br>
> Here's what I've come up with as an alternative so far.<br>
><br>
> data Step a b where<br>
> TrueFlow :: Decision a d e => a -> Step a b<br>
> FalseFlow :: Decision b f g => b -> Step a b<br>
> Done :: Result -> Step a b<br>
><br>
> class Decision a b c | a -> b, a -> c where<br>
> decide :: (Functor m, Monad m) => a -> StateT s m (Step b c)<br>
><br>
> data S0 = S0 deriving (Show, Eq)<br>
> instance Decision S0 S1 S2 where<br>
> decide _ = bool (FalseFlow S2) (TrueFlow S1) <$> testS0<br>
><br>
> data S1 = S1 deriving (Show, Eq)<br>
> instance Decision S1 () () where<br>
> decide _ = s1Result<br>
><br>
> data S2 = S2 deriving (Show, Eq)<br>
> instance Decision S2 () () where<br>
> decide _ = s2Result<br>
><br>
> decision :: (Functor m, Monad m, Decision a b c)<br>
> => a<br>
> -> StateT s m Result<br>
> decision s = decide s >>= \case<br>
> TrueFlow b -> decision b<br>
> FalseFlow c -> decision c<br>
> Done s -> return s<br>
><br>
> With this implementation it is easy to test a single decision point, - just<br>
> use `evalStateT (decide S0) s` and compare the resulting Step to make sure<br>
> it is either S1 or S2. The logic can also be easily separated and organized<br>
> into modules.<br>
><br>
> The biggest drawback of this new approach is the need to create a new `Step`<br>
> record at every decision point. In the end, this may be worth it for the<br>
> added design benefits, but would love to be able to get rid of it and find a<br>
> solution that gives the added benefits without incurring any performance<br>
> penalties.<br>
><br>
> Any suggestions anyone can give on improvements would be greatly<br>
> appreciated.<br>
><br>
> Thanks!<br>
> Rich<br>
><br>
</div></div>> _______________________________________________<br>
> Haskell-Cafe mailing list<br>
> <a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
> <a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
><br>
</blockquote></div><br></div>