HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Writing an infinitely running( while(true) { } ) user input function in haskell

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
whileinfinitelyuserwritingtruefunctioninputrunninghaskell

Problem

I'm trying to implement a lexer in Haskell. For easy console input and output, I've used an intermediate data type Transition Table.

type TransitionTable = [(Int, Transitions String Int)]
type Transitions a b = [(a, b)]


I want to take input from the user for all the states and transitions. I do not want to take the total number of states before hand. I want it to keep taking input for the transitions of each state until the user types "--" . If the user types "---", the current state is discarded and the input terminates.

After a lot of attempts I came up with this, which I think is horrible code.

-- |A function to emulate the while loop for easy IO functionality.
--  Defination:- while @comparator @func @start:
--      *comparator @arg: A function which returns True or False on the basis of @arg.
--          The loop stops when False is returned.
--      *func: The function which is executed repeadly.
--          It is responsible for returning the next @arg for the comparator on the basis of the current @arg.
--      *start: The starting value of @arg to pass to the comparator.
while :: (Monad m) => (a -> Bool) -> (a -> m a) -> a -> m a
while comparator func start =
    if comparator start then do
        nxt  IO ()
myPutStr str = putStr str >> hFlush stdout >> return ()


```
-- Takes input from the console to generate a TransitionTable.
inputTransitionTable :: IO TransitionTable
inputTransitionTable = do
putStrLn "Type -- for next state and --- for completing input entering."
retVal -1

-- Takes the current state number and the incomplete corrosponding transition table which is populated
-- with user input. Input ends when user enters "---". State number is set to -1 when input is over.
takeInfo (i, states) = do
putStrLn ("Adding transitions to state " ++ show i ++ ": ")
retVal return (-1, states)
False -> return (i+1, states ++ [(i, stateInfo)])

Solution

In order to repeat an action ad nauseum, you can use forever. But you don't really need to use that here, you can just do it the way you've done it so far. Though I'd think that your while function could be written with guards instead.

while :: (Monad m) => (a -> Bool) -> (a -> m a) -> a -> m a
while comparator func start | comparator start = func start >>= while comparator func
                            | otherwise        = return start


You could also look into using
Control.Monad.LoopWhile,
though I've never used it before.

Note that you don't need >> return (), since hFlush :: Handle -> IO () already returns () in the IO Monad. Actually, you never really need return (). I find it preferable to use void action or _ <- action in order to throw away the result of action.

Your inputTransitionTable function is… not very idiomatic. Threading state
yourself is unnecessary, you can use the State monad.

Instead of this interactive process, it might actually be easier for a user to
just specify the transition table as a list of ordered pairs, pretty much the
way you're printing it after the thing is done. This would probably also require
you to parse the user input properly, which is good opportunity to learn the
ropes with parsec. If you want to keep the interactive model, then I'd
recommend stacking the IO and State Monads together into an action of type
State IO TransitionTable.

You can read up on all this stuff in the corresponding chapters of
RealWorldHaskell. Chapter 16 on
Parsec, and Chapter
18 on Monad
Transformers.

Code Snippets

while :: (Monad m) => (a -> Bool) -> (a -> m a) -> a -> m a
while comparator func start | comparator start = func start >>= while comparator func
                            | otherwise        = return start

Context

StackExchange Code Review Q#15250, answer score: 2

Revisions (0)

No revisions yet.