I've implemented a small library named `break`

for breaking from loops, based off of a previous post of mine.

This library is simple: you wrap the command you want to loop with a function named `loop`

and you call `break`

when you want to exit from the loop.

```
import Control.Break
import Control.Monad.State
import Prelude hiding (break)
example :: State Int ()
example = loop (do
n <- lift get -- Inside a `loop`, wrap commands in `lift`
if n < 10
then lift (put (n + 1)) -- You keep looping by default
else break () ) -- Use `break` to exit from the `loop`
```

The above loop increments `n`

until `n`

is 10 and then exits from the loop. We can verify this by running the `State`

action:

```
>>> execState example 0
10
```

By default, you wrap commands other than `break`

with `lift`

. However, for some effects (like `State`

) you can omit the `lift`

:

```
example :: State Int ()
example = loop (do
n <- get
if n < 10
then put (n + 1)
else break () )
```

This library uses a `Break`

type which is implemented as `ExceptT`

under the hood:

`newtype Break r m a = Break { unBreak :: ExceptT r m a }`

The `break`

function just "throws an exception":

```
break :: Monad m => r -> Break r m a
break r = Break (throwE r)
```

Here "throwing an exception" doesn't use any sort of out-of-band feature built into the Haskell language. "Exceptions" are just ordinary values implemented within the language:

```
throwE :: Monad m => e -> ExceptT e m a
throwE = ExceptT (return (Left e))
```

... and `ExceptT`

's implementation of `(>>=)`

short-circuits when encountering a `Left`

internally, skipping subsequent commands.

`loop`

is just an ordinary function that repeats the loop body indefinitely and only stops when you `break`

from the loop:

```
loop :: Monad m => Break r m () -> m r
loop m = do
x <- runExceptT (unBreak (forever m))
case x of
Left r -> return r
Right r -> return r
```

Conceptually we just "catch" the "exceptional value" and return the value. I use quotes because there's no reason to restrict this value to exceptions. You can return any old value from the loop this way:

```
example :: State Int Bool
example = loop (do
n <- get
if n < 10
then put (n + 1)
else break True )
>>> runState example 0
(True,10)
```

Notice how I don't have to define a special variation on the `forever`

function that integrates with `ExceptT`

or `break`

to terminate correctly. Instead, `break`

interacts correctly with `forever`

out-of-the-box thanks to Haskell's laziness: `forever`

only lazily evaluates as many actions as necessary and once the internal `ExceptT`

short-circuits the `forever`

function doesn't demand any further command repetitions.

This library also showcases one of Haskell's nifty features: the `GeneralizedNewtypeDeriving`

language extension. Even though I wrap `ExceptT`

in a `Break`

newtype, I can still configure `Break`

to reuse many of the interfaces that were originally implemented for `ExceptT`

, like this:

```
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Break r m a = Break { unBreak :: ExceptT r m a }
deriving
( Functor
, Applicative
, Monad
, MonadTrans
, MonadIO
, MonadCont
, MonadState s
, MonadWriter w
)
```

The `GeneralizedNewtypeDeriving`

extension is clever and knows how to wrap and unwrap the newtype to transparently lift all of these type classes to work on `Break`

.

In fact, the library implementation is remarkably small. Here's the entire implementation if you omit the documentation:

```
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Applicative (Applicative)
import Control.Monad (forever)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE)
import Control.Monad.Cont (MonadCont )
import Control.Monad.State (MonadState )
import Control.Monad.Writer (MonadWriter)
import Prelude hiding (break)
newtype Break r m a = Break { unBreak :: ExceptT r m a }
deriving
( Functor
, Applicative
, Monad
, MonadTrans
, MonadIO
, MonadCont
, MonadState s
, MonadWriter w
)
break :: Monad m => r -> Break r m a
break r = Break (throwE r)
loop :: Monad m => Break r m () -> m r
loop m = do
x <- runExceptT (unBreak (forever m))
case x of
Left r -> return r
Right r -> return r
```

Newtypes are probably one of the Haskell features I miss the most when programming in other languages. They are free performance-wise (especially with the recent work on `Data.Coerce`

) and you pay very little code to transport interfaces across newtype boundaries. You get all of the benefits of abstraction and precise types at very little cost.

If you would like to use the `break`

library you can find the library on Hackage or Github.