Showing posts with label memoization. Show all posts
Showing posts with label memoization. Show all posts

Monday, November 25, 2013

Abstract Nonsense: chapter 2s, Stately Statefulness

This is about continuations, really.

This is the continuing series of articles of continuing on continuations. Really.

And State. Oh, and State. Ugh.

So, the State monad is a monad on a 'stateful-like' function.

Still with me?

So I have this beautiful implementation in Java — No, really. I do. — and, so ... how do I test that it works?

Well, you know I have to use it. I have this State monad transformer example, but I want something really, really simple, so:

> inc :: State Int Int
> inc = do x <- get
>             put (x + 1)
>             return x

But the thing is, we don't have the do-notation in Java, so we have to unwind this as a set of monadic binds, so:

> inc1 = get >>= \x -> put (x + 1) >> return x

Cool! But we don't have (>>) ('then') in Java, we have (>>=) ('bind'), so one more transformation:

> inc2 = get >>= \x -> put (x + 1) >>= \_ -> return x

AND we're done! Now, an example:

> runState (inc2 >> inc2 >> inc2) 2

and we get:

(4, 5)

as expected.

Shoot, do we have to write (>>) ('then') as a convenience function? We still have to take the argument and declare the type of the function, but the function itself simplifies. Let's see the Java implementation first as:

> runState (inc2 >>= \_ -> inc2 >>= \_ -> inc2) 2

(still works...)

(Holy Chajoles, Batmans, my Java-State-thingie just frikken worked!)1

So, State is a straight transliteration from Haskell to Java. It's a monad of a function:

public class State<STATE, DATUM> implements Monad<State, DATUM> {

    protected State(Arrow<STATE, Tuple<DATUM, STATE>> f) {
state = f;
    }

// get and put are straight from the haskell standard library:

    // get = State $ \x -> (x, x)
    // so is x <- get the same as get >>= State $ \state -> (x, state1)
    public static <STATE1> State<STATE1, STATE1> get(STATE1 basis) {
return new State<STATE1, STATE1>(Tuple.dupF(basis));
    }

    // put :: a -> State a ()
    public static <STATE1> State<STATE1, Unit> put(final STATE1 s) {
Arrow<STATE1, Tuple<Unit, STATE1>> putter =
    new Function<STATE1, Tuple<Unit, STATE1>>() {
    public Tuple<Unit, STATE1> apply(STATE1 ignored) throws Failure {
return Tuple.pair(Unit.instance, s);
    }
};
return new State<STATE1, Unit>(putter);
    }

// eval/exec/run State functions are yawn-worthy:

    public DATUM evalState(STATE s) throws Failure {
return state.apply(s).fst();
    }
    public STATE execState(STATE s) throws Failure {
return state.apply(s).snd();
    }
    public Tuple<DATUM, STATE> runState(STATE s) throws Failure {
return state.apply(s);
    }

// now here comes the fun stuff. Functor:

    // Functor override --------------------
    /*
instance Functor (State s) where
    fmap f m = State $ \s -> let
        (a, s') = runState m s
        in (f a, s')
    */
    public <A, B> Arrow<Functor<A>, Functor<B>> fmap(final Arrow<A, B> fn) {
return new Function<Functor<A>, Functor<B>>() {
    public Functor<B> apply(final Functor<A> functor) throws Failure {
// TODO: implement -- uh, fmap and >>= are the same for state
// ... almost
Arrow<STATE, Tuple<B, STATE>> stater =
    new Function<STATE, Tuple<B, STATE>>() {
    public Tuple<B, STATE> apply(STATE s) throws Failure {
State<STATE, A> m = (State<STATE, A>)functor;
final Tuple<A, STATE> tup = m.runState(s);
return Tuple.pair(fn.apply(tup.fst()), tup.snd());
    }
};
return new State<STATE, B>(stater);
    }
};
    }

// and monad bind and return and then:

    // Monad overrides ---------------------
    public <X> Monad<State, X> unit(final X d) {
Arrow<STATE, Tuple<X, STATE>> lifter =
    new Function<STATE, Tuple<X, STATE>>() {
    public Tuple<X, STATE> apply(STATE st) throws Failure {
return Tuple.pair(d, st);
    }
};
return new State<STATE, X>(lifter);
    }

    // state >>= f = State $ \st ->
    //               let (x, st') = runState state st
    //               in  runState (f x) st'
    public <B> Monad<State, B> bind(final Arrow<DATUM, Monad<State, B>> fn)
throws Failure {
final State<STATE, DATUM> status = this;
Arrow<STATE, Tuple<B, STATE>> binder =
    new Function<STATE, Tuple<B, STATE>>() {
    public Tuple<B, STATE> apply(STATE st) throws Failure {
Tuple<DATUM, STATE> tup = status.runState(st);
return ((State<STATE, B>)fn.apply(tup.fst()))
.runState(tup.snd());
    }
};
return new State<STATE, B>(binder);
    }

    // join() ???
    public Monad<State, ?> join() throws Failure {
      throw new Failure("Okay, why are you joining on state?");
    }
    public <A, B> Arrow<A, Monad<State, B>> liftM(Arrow<A, B> fn) {
return Monad.Binder.lifter(this, fn);
    }
    public <B> Monad<State, B> liftThenBind(Arrow<DATUM, B> fn)
throws Failure {
return bind(liftM(fn));
    }

    public <T> Monad<State, T> then(final Monad<State, T> m) throws Failure {
return bind(new Function<DATUM, Monad<State, T>>() {
public Monad<State, T> apply(DATUM ignored) throws Failure {
    return m;
}
    });
    }

    // just capture types here -----
    private final Arrow<STATE, Tuple<DATUM, STATE>> state;
}

... and with that we have (functional) state!

Now, to test it with our inc2 function(al):

    // So we need to test the hell out of this ...
    public static void main(String args[]) throws Failure {
// TODO: test me! well a simple test is monadic succ, so ...
// inc2 :: State Int Int
// inc2  = get >>= \x -> put (x + 1) >> return x
Monad<State, Integer> inc2 =
    State.get(0).bind(new Function<Integer, Monad<State, Integer>>() {
    public Monad<State, Integer> apply(final Integer x)
    throws Failure {
return State.put(x + 1)
            .then(new State<Integer, Integer>(
Tuple.mkWithFst(x, 0)));
    }
});
System.out.println("runState (inc2 >> inc2 >> inc2) 2 is (4,5): "
  + ((State<Integer, Integer>)
      (inc2.then(inc2).then(inc2))).runState(2));
System.out.println("runState (inc2 >> inc2 >> inc2) 17 is (19, 20): "
  + ((State<Integer, Integer>)
      (inc2.then(inc2).then(inc2))).runState(17));
    }

And the results output are as expected. YAY!

You see that I did implement the (>>) ('then') operator in Java. It was very easy to do: just a bind-ignore-return.

Now.

Now.

Now. (Now, I'm going to stop saying 'now.' Really.)

Now that we have state, we can use it along with continuations to create a threading or coroutining model of computation without having to use the (dreaded/revered) call/cc.2

Tune in next week, Ladies and Gentlemen, for another thrilling chapter on our continuing saga of continuations!

-----

(1) 'Thingie' is a technical term ... 'frikken' is ... not so technical.

(2) "Oh, you code with call/cc?" the awed neophyte whispers reverentially, then bows down and worships.3

(3) Thus spake the Zen Master: "Why are you in awe of those who use call/cc and similar parlor tricks? The real miracle is that when I wish to sum, I use (+), and when I wish to 'continue' I hit the carriage return." Then he concluded his lesson with the cryptic utterance: "One bowl of rice" and departed their presence for Nirvana (he likes Soundgarden, as well).

Chapter Postlude

The problem that this chapter addresses is what I've had in my java categories library for some time now. I've had this thing called 'MutableBoxedType<T>' with a very easy implementation:

@Effectual public class MutableBoxedType<T>
    implements Functor<T>, Copointed<T> {

    public MutableBoxedType(T val) {
this.value = val;
    }

    @Effectual public void put(T newval) {
this.value = newval;
    }

    @Effectual public final Effect<T> putF = new Effect<T>() {
@Override protected void execute(T obj) throws Failure {
    put(obj);
}
    };

    // Functor implementation -----------------------------------
    public <X, Y> Arrow<Functor<X>, Functor<Y>>
fmap(final Arrow<X, Y> fn) {
return new Function<Functor<X>, Functor<Y>>() {
    public Functor<Y> apply(Functor<X> obj)
throws Failure {
MutableBoxedType<X> mbt =
    (MutableBoxedType<X>)obj;
return new MutableBoxedType<Y>(fn.apply(mbt.extract()));
    }
};
    }

    // Copointed implementation --------------------------------
    public T extract() {
return value;
    }

    private T value;
}

Simple enough, we just put a box around a mutable type. The problem is all those @Effectual annotations and the very mutability-ness of the type itself. Once we make the guts of an instance mutable we lose all provability as 'equals' and '==' now have been reduced to meaningless, vis:

MutableBoxedType<Integer> five = new MutableBoxedType<Integer>(5);
oldFive= five;
five.put(12);
five.equals(oldFive); ... is true

Yes, five and oldFive are equal, but their shapes have changed during the course of the program and we need deep copy semantics here to sniff that out.

The (functional) state monad doesn't 'change' ... not via mutation, anyway. What happens instead is that when the state gets updated, we have a new state. It just makes sense (provably so) and gives us our beloved 'mutation' (by all appearances) even in the functional domain.

Wednesday, May 14, 2008

No "fib"bing; getting into the monad groove

The title is groan-inducing, as all my "jokes" are. I guarantee it: once you've finished reading this entry, reread the title, and you won't be able to stop from groaning.

The Fibonacci series goes as follows: 0,1,1,2,3,5,8,13..., or put another way, the current number is obtained by adding the previous two numbers. It is useful for many things, as its limit is the golden ratio, found in nature (the spiral of some crustaceans and the population growth of rabbits [they must be stopped!]) and in artifice (windows, painting, doors, buildings follow the width and height of this ratio)).

Any fibonacci number can be easily computed from the following formula ...

fib :: Integer → Integer
fib n | n ≡ 0 = 0
| n ≡ 1 = 1
| otherwise = fib (n-1) + fib (n-2)


... that is, easily computed if this world were purely mathematical, including the caveat that any computable function could be instantly computed. Run on a computer, fib 25 slows noticeably and fib 50 may as well describe the halting problem, because I wasn't going to wait around for it to terminate.

Note the similarity between the computation of the Fibonacci series to the computation of the Ackermann table. They are not the same kind of problem, mind you, as the Ackermann is not primitively recursive; the Fibonacci is "only" doubly (branching) recursive. But they are similar enough in that they can be solved in similar ways. Given that the current Fibonacci number is the sum of the previous two Fibonacci numbers, we need only a (reversed) list to memoize the previous results, so the above sequence becomes:

[..., 13, 8, 5, 3, 2, 1, 1, 0]


and the "next" Fibonacci number would be simply the first two elements of this list (21, in this case). But how do we know where we are in the sequence? Easy: the length of this list tells us where we are, and in this case, the list has 8 elements, meaning the "next" Fib is 9th in the sequence.

So, turning to monads with this list structure for memoization, the code falls out as follows:

fib :: Int → FibS
fib n = get >>= λmem . maybe (fib' n >>= update)
return
(gimme mem n)


So, fib is now simply a decision: "Do I have the fib requested in the list?" "Yes: gimme it and return it" or "No: compute it and then update the list"

The list is a very slight variation on a regular list type, as we choose to carry around its length (as opposed to recomputing it at each iteration), and we lift this new data type into the State monad (as we did with the Map data type for our Ackermann monad optimization):

data Mem = Mem [Integer] Int
type FibS = State Mem Integer


The actual computation function is lifted into the monadic form with very little variation ...

fib' :: Int → FibS
fib' n | n ≡ 0 = return 0
| n ≡ 1 = return 1
| otherwise = liftM2 (+) (fib (n - 1)) (fib (n - 2))


... where monadic addition, liftM2 (+), replaces addition in the usual sense (recall the spot of bother we had "adding" Thing One to Thing Two), and where the plain numbers are lifted into the monad with return. In brief, the substance is now monadic but the structure is the same as our original, plain, fib.

The other housekeeping functions are new for the monadic solution, but what one would expect. The update follows in the same vein as the one for the ackermann monad:

update :: Integer → FibS
update n = do (Mem lst len) ← get
put (Mem (n:lst) (len + 1))
return n


An interpretation of the update function is that it is the identity function with the side-effect that it remembers the result that it returns.

The only other function is the self-describing gimme which retrieves a previously-computed fibonacci number from memory:

gimme :: Mem → Int → Maybe Integer
gimme (Mem (h:t) len) n | len ≡ n = Just h
| len > n = let x = (len - 1) - n
in Just (t !! x)
| otherwise = Nothing


This gimme function uses the Maybe monad and its functionality, saying "If I have the value already computed [If the list length is equal to or greater than the requested index], then return Just that value; otherwise I've got Nothing, so you need to compute that value."

In summary, we've decorated the naïve fibonacci algorithm with some memory and three new functions (one manager and two support functions). What is the payoff?

Ready> fib 1500
[no delay]
135511256685631019516369368671484083777860107124184972421335
431532214873108735287506122593540357172653003737788143473202
576992570823565500453499141029242495959974839822286992875272
419318113250950996424476212422002092544399201969604653214384
983053458933789325853933815390935494792961948008381459961871
22583354898000

Ready>
fib 50000
[4 second delay]
[10615 digits]

Ready> fib 60000
[2.5 second delay]
*** Exception: stack overflow


These results are a great step forward over the naïve fib implementation (which essentially froze at fib 50) and even memoized implementation reported elsewhere (which ran out of memory after fib 1450).

Huzzah! then for efficient program representation in Haskell and monadic code.

Oh! my acking monad!

So, we just saw, after reams of paper on proving in inscrutable detail the three monadic laws, that after all that, we see that using them is pretty easy. Fancy that, Hedda!

Haskell does provide several different kinds of monads for various uses, but sometimes one must roll one's own to fit the current need. I was exploring the ackermann function, curious to know what A4,2 looked like [oh, yes, 19730 digits in all its (gory) glory -- I went there!] (other than the cop-out of 265536-3 that is listed in Wikipedia), and whether my computer could calculate and display that value [obviously it could, but it took some work (see below) to arrive at that point]. The ackermann function written in Haskell looks very much like its representation in standard mathematical notation:

a :: Integer → Integer → Integer
a m n | m ≡ 0 = n + 1
| m > 0 ∧ n ≡ 0 = a (m - 1) 1
| m > 0 ∧ n > 0 = a (m - 1) (a m (n - 1))



The "problem" with the Ackermann function is that even though it is easy to write, the recursion required to arrive at a solution from even small (single digit) inputs is staggering. My computer staggered and then killed the computation in mid-process (too bad Redjak didn't have that option). The Ackermann function, being not primitively recursive, is also highly redundantly recursive, so it would be nice to provide "hints" to the program, something like: "Oh, you've already computed A3,1 to be 13, so you don't need to recompute that entire branch any more."

This "hinting" has a name in computer science; it's called "memoization", and Haskell provides the State monad that can be used to cover that functionality quite nicely. As we're computing the values that build up the solution, we put the smaller-indexed solutions into a dictionary, something like:




A3,1=13
A2,2=7
A2,1=5
... and so forth ...


Such a dictionary in Haskell is called a Map, because we are mapping from the "word" (the indices of the Ackermann function) to the "definition" (the solution)...

type Index = (Integer, Integer)
type AckMap = Map Index Integer


... and we wrap that Map with the State monad ...

type AckMapS = State AckMap Integer


... where AckMapS uses the AckMap dictionary to provide the preexisting (partial) solution, or, given there's none yet, populates the solution calculated (the Integer) into the dictionary at the current Index. Simple, yes? So, all we need is an utility function that does the lookup or updates the state ...

ackify :: Integer → Integer → AckMapS
ackify m n = get >>= λma . maybe (a' m n >>= update m n)
return
(Map.lookup (m, n) ma)


... the update function that actually does the housekeeping ...

update :: Integer → Integer → Integer → AckMapS
update m n ans = do mappo ← get
put (Map.insert (m, n) ans mappo)
return ans


... so that our monadic version of the ackermann function is as follows:

a' :: Integer → Integer → AckMapS
a' m n | m ≡ 0 = return (n + 1)
| m > 0 ∧ n ≡ 0 = ackify (m - 1) 1
| m > 0 ∧ n > 0 = ackify m (n - 1) >>= ackify (m - 1)


which looks very much the same as the original function, with calls to a being replaced by calls to the lookup-or-update ackify function and function composition (e.g. a (m - 1) (a m (n - 1))) being replaced by the monadic composer, >>=. So, from the above demonstration we see that monads are not only easy to use, but also easy to "roll your own" and integrate into preexisting non-monadic code without much fuss.

Critique

Although the incorporation of the State monad dramatically speeds processing solutions and makes some solutions computable that were unreachable under the naïve approach, there are at least two better generalizations:


  1. Use the fixpoint (or the Y-combinator) of the ackermann function so that one can now decorate the engine of computation without altering its structure at all! Incredible! So instead of using a monad to abstract stateful decoration of computations, the fixpont can be used to abstract any decoration of computation!

  2. Use the compiler to change the code, instead of having it programmed in. Many functional language compilers have the option to memoize computations automagically. So, instead of writing code that memorizes partial results, the compiler intercepts the computation, replacing computation with previously calculated values (or running the computation if no such value exists and then storing that result into the runtime). No extra coding; no "troublesome" monads.



Critique on the critique

On the other hand ("there are five fingers", as my favorite Mother-in-law enjoys rejoining),


  1. The fixpoint solution is more general and more elegant, but being more general suffers in a slight decrease in efficiency: it more than likely will run more slowly than the specialized monadic state solution

  2. With an explicit encoding of state, the programmer has direct say into what is memoized and what is not, but with a compiler directive, everything is memoized. Since the Ackermann function can be represented by simple functions for m < 4, the map can be replaced by a faux-map for those values, reducing the memory footprint of memoization to only a couple of cells for m == 4 (as opposed to over 150,000 cells for m == 4 and n == 1 using the naïve memoization scheme).



... so the above described solution may be "good enough" for the task at hand, after all. Put another way, when I said I was wrong, I might have been wrong. Caveat programmer.