1

I'm trying to run a state transform repeatedly until the state doesn't change. I've searched Hoogle for

Eq s => State s () -> State s ()

but found nothing that looks appropriate.

How can I run a state transform repeatedly until it doesn't change?

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165

5 Answers5

5

You can do this in State, but there's no particular benefit. Why not:

idempotently :: Eq a => (a -> a) -> a -> a
idempotently f a = if a' == a then a else idempotently f a' where a' = f a

idempotentlyM :: Eq s => (s -> s) -> State s ()
idempotentlyM f = modify (idempotently f)

If you start with a State s (), you can execState it to get your s -> s out.

Of course, there's no guarantee that f doesn't diverge, so idempotently f may never terminate.

Rein Henrichs
  • 15,437
  • 1
  • 45
  • 55
4

You are computing the fix-point of your state transform. But we can't use fix since we are in a monadic context. So let's use a monadic fix-point combinator instead. Enter mfix:

import Control.Monad (unless)
import Control.Monad.State (MonadState, StateT, get, put)
import Control.Monad.Fix (mfix)
import Control.Monad.IO.Class (liftIO)

untilStable :: MonadState s m => (s -> s -> Bool) -> m a -> m ()
untilStable p = mfix $ \f st -> p <$> get <* st <*> get >>= (`unless` f)

I also took the liberty of generalizing your function to so that you can provide a user supplied binary predicate.

Using ghci runState (untilStable (==) $ modify (+1)) 2 will never terminate. But with:

comp :: StateT Int IO ()
comp = do
  s1 <- (+1) <$> get
  liftIO $ print s1
  let s2 = if s1 >= 3 then 3 else s1
  put s2

You get:

> runStateT (untilStable (==) comp) 0
1
2
3
4
((),3)

This untilStable can be generalized further into:

untilStable :: MonadState s m => (s -> s -> Bool) -> m a -> m a
untilStable p = mfix $ \f st -> do
  before <- get
  a <- st
  after  <- get
  if p before after then pure a else f

Now we have freed up what types the computations can result in.

Fix you want to implement idempotently with fix, you can do it like so:

import Data.Function (fix)

idempotently :: Eq a => (a -> a) -> a -> a
idempotently = fix $ \i f a ->
  let a' = f a
  in if a' == a then a else i f a'
Centril
  • 2,549
  • 1
  • 22
  • 30
  • Would `fix` even be applicable if not in a monadic context? I'm suspecting it's not quite what would be needed, as it doesn't seem to have any concept of comparing values of successive iterations. – Michal Charemza Jun 06 '17 at 07:41
  • `fix`, i.e, the non-monadic variant of `mfix` can be used to implement `idempotently`. I will update my answer to implement `idempotently` with it. By values, do you mean the value of the state, or the value of the computation? I also have to ask, why do you want to compute state-transforms until a fixed point? Is your only goal to live within `State` and not some `StateT s m a, m /= Identity`? If you want to normalize terms, consider a normalization monad based on `Writer Any` since it is inefficient to use `(==)` at every step. – Centril Jun 06 '17 at 17:58
  • `Writer` is only efficient for lazy monoids. What are you trying to do? – dfeuer Jun 06 '17 at 18:10
  • @dfeuer `(<>)` for `Any` should be lazy in the second argument if the first argument is `True` but strict in all arguments if the first is `False`. `Writer [a]` will be inefficient since `(++)` has `O(n)` complexity in the first argument. But for `Any`, the complexity is always `O(1)`. We can use `Any` with `True` representing a change, and `False` representing unique normal form, we can then keep applying the rule until we get `False`. – Centril Jun 06 '17 at 18:34
  • @Centril I see how `fix` can be used... thanks! My specific case: I'm changing the stopping condition of the Sudoku solver at https://codereview.stackexchange.com/a/164380/34049. The state is a matrix of potential values for each cell, and when the potential values don't change between iterations, I want to stop iterating. As far as I can tell, I do just want to live in `State`. – Michal Charemza Jun 06 '17 at 19:00
  • OK, left you some comments there. The main idea behind `Writer Any` is that the part of your transformation that changes values knows if a change occurred or not. So why not let inform you of change instead of using `(==)`? We used this mechanism in our Bachelors thesis, see: https://github.com/DATX02-17-26/DATX02-17-26/blob/dev/libsrc/Norm/CompAssignment.hs and: https://github.com/DATX02-17-26/DATX02-17-26/blob/dev/libsrc/Norm/NormM.hs – Centril Jun 06 '17 at 20:40
2

You can roll-your-own higher level recursive transform, using get and unless

untilStable :: Eq s => State s () -> State s ()
untilStable stateTransform = do
  before <- get
  stateTransform
  after <- get
  unless (before == after) $ untilStable stateTransform
Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
1

Taking inspiration from https://stackoverflow.com/a/44378096/1319998 and https://stackoverflow.com/a/23924238/1319998, you can do this out of State but using some function monad magic...

untilStable :: Eq a => (a -> a) -> a -> a
untilStable = until =<< ((==) =<<)

... extracting the s -> s from a state using execState

untilStable (execState stateTransformToRunRepeatedly) initialState
Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
1

Expanding on the answer from https://stackoverflow.com/a/44381834/1319998, so keeping the iteration out of State, but avoiding the function monad magic with a clearer use of until

untilStable :: Eq a => (a -> a) -> a -> a
untilStable f a = until (\w -> f w == w) f a

... and similarly extracting the s -> s from a state using execState

untilStable (execState stateTransformToRunRepeatedly) initialState

Point-free style isn't used, but to me it is clearer what's going on.

Also, I wonder if this reveals an inefficiency of both this and https://stackoverflow.com/a/44381834/1319998, where f a may be computed twice: once privately inside until, and once in the predicate used to test whether to stop.

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165