[Haskell-cafe] Channel9 Interview: Software Composability andthe Future of Languages

Chris Kuklewicz haskell at list.mightyreason.com
Fri Feb 2 05:28:55 EST 2007



Claus Reinke wrote:
>>>> while (hGetBuf h buf bufsize == bufsize)
>>>>   crc := updateCrc crc buf bufsize
>>>>   break if crc==0
>>>>   print crc
>>
>>>     inContT $ callCC $ \break -> do
>>>         flip execStateT 0 $ do
>>>             whileM (liftM (== bufsize) (hGetBuf h buf bufsize)) $ do
>>>                 modifyM (updateCrc buf bufsize)
>>>                 crc <- get
>>>                 when (crc == 0) (lift (break crc))
>>>                 print crc
>>
>> first. it's longer than original. 
> 

The above version required passing break explicitly.  I can pack that into a
Reader.  The actual semantics of the while loop from 'c' are then more closely
followed.

This allows:

> run_ = runner_ testWhile_
> runner_ m = runRWS (runContT m return) NoExit_ (17::Int)
> testWhile_ = while_ (liftM (>10) get) innerWhile_
> innerWhile_ = do
>   v <- get
>   tell_ [show v]
>   when' (v==20) (tell_ ["breaking"] >> breakW_)
>   if v == 15 
>     then put 30 >> continueW_
>     else modify pred

The result is
> ((),20,["17","16","15","30","29","28","27","26","25","24","23","22","21","20","breaking"])

Where there is the benefit over C of putting the break or continue in a
sub-function.

The full code (for two versions) is:

-- By Chris Kuklewicz, BSD-3 license, February 2007
-- Example of pure "while" and "repeat until" looping constructs using
-- the monad transformer library.  Works for me in GHC 6.6
--
-- The underscore version is ContT of RWS and this works more
-- correctly than the non-underscore version of RWST of Cont.
--
-- Perhaps "Monad Cont done right" from the wiki would help?
import Control.Monad.Cont
import Control.Monad.RWS
import Control.Monad.Error
import Control.Monad.ST
import System.IO.Unsafe
import Data.STRef

-- Note that all run* values are the same Type
main = mapM_ print [run,run2,run_,run2_]

run,run_,run2,run2_ :: MyRet ()
run = runner testWhile
run2 = runner testRepeatUntil
run_ = runner_ testWhile_
run2_ = runner_ testRepeatUntil_

runner_ m = runRWS (runContT m return) NoExit_ (17::Int)
runner m = (flip runCont) id (runRWST m NoExit (17))

testRepeatUntil_ = repeatUntil_ (liftM (==17) get) innerRepeatUntil_
testRepeatUntil = repeatUntil (liftM (==17) get) innerRepeatUntil

innerRepeatUntil_ = tell_ ["I ran"] >> breakW_
innerRepeatUntil = tell ["I ran"] >> breakW

testWhile_ = while_ (liftM (>10) get) innerWhile_
testWhile = while (liftM (>10) get) innerWhile

-- innerWhile_ :: ContT () (T_ (Exit_ () Bool Bool)) ()
innerWhile_ = do
  v <- get
  tell_ [show v]
  when' (v==20) (tell_ ["breaking"] >> breakW_)
  if v == 15
    then put 30 >> continueW_
    else modify pred

innerWhile = do
  v <- get
  tell [show v]
  when' (v==20) (tell ["breaking"] >> breakW)
  if v == 15
    then put 30 >> continueW
    else modify pred

-- The Monoid restictions means I can't write an instance, so use tell_
tell_ = lift . tell

-- Generic defintions
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))

when' :: (Monad m) => Bool -> m a -> m ()
when' b m = if b then (m >> return ()) else return ()

-- Common types
type MyState = Int
type MyWriter = [String]
type MyRet a = (a,MyState,MyWriter)
-- RWST of Cont Types
type T r = RWST r MyWriter MyState
type Foo r a = T (Exit (MyRet r) a a) (Cont (MyRet r))
type WhileFunc = Foo () Bool
type ExitFoo r a = Foo r a a --  (Exit r a a)  (Cont r) a
type ExitType r a = T (Exit r a a)  (Cont r) a
data Exit r a b = Exit (a -> ExitType r b) | NoExit
-- ContT of RWS Types
type T_ r = RWS r MyWriter MyState
type ExitType_ r a = ContT r (T_ (Exit_ r a a)) a
data Exit_ r a b = Exit_ (a -> ExitType_ r b) | NoExit_

-- Smart destructor for Exit* types
getExit (Exit loop) = loop
getExit NoExit = (\ _ -> return (error "NoExit"))
getExit_ (Exit_ loop) = loop
getExit_ NoExit_ = (\ _ -> return (error "NoExit"))

-- I cannot see how to lift withRWS, so use local
-- Perhaps "Monad Cont done right" from the wiki would help?
withLoop_ loop = local (\r -> Exit_ loop)
-- withRWST can change the reader Type
withLoop loop =  withRWST (\r s -> (Exit loop,s))

-- The condition is never run in the scope of the (withLoop loop)
-- continuation.  I could have invoked (loop True) for normal looping
-- but I decided a tail call works as well.  This decision has
-- implication for the non-underscore version, since the writer/state
-- can get lost if you call (loop _).
while_ mCondition mBody = do
  (proceed,loop) <- getCC' True
  let go = do check <-mCondition
              when' check (withLoop_ loop mBody >> go)
  when' proceed go

while mCondition mBody = do
  (proceed,loop) <- getCC' True
  let go = do check <-mCondition
              when' check (withLoop loop mBody >> go)
  when' proceed go

repeatUntil_ mCondition mBody = do
  (proceed,loop) <- getCC' True
  let go = do withLoop_ loop mBody
              check <- mCondition
              when' (not check) go
  when' proceed go

repeatUntil mCondition mBody = do
  (proceed,loop) <- getCC' True
  let go = do withLoop loop mBody
              check <- mCondition
              when' (not check) go
  when' proceed go

-- breakW :: WhileFunc a
breakW_ =  ask >>= \e -> getExit_ e False >> return undefined
breakW =  ask >>= \e -> getExit e False >> return undefined
-- continueW :: WhileFunc a
continueW_ =  ask >>= \e -> getExit_ e True >> return undefined
continueW =  ask >>= \e -> getExit e True >> return undefined


More information about the Haskell-Cafe mailing list