Justin Bailey

Back to Profile: Justin Bailey

Comments

  • Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx)

    Pretty cool work. I think your implementation points out a few issues that this approach has to deal with.

     

    1) SpotAgent allows many subscribers; RiskAgent only one -- That says the Subscribe method isn't giving enough information (and the IObservable interface isn't rich enough). I wonder how you could indicate that a given Observable allows many observers vs. one?

     

    2) One problem that Erik points out with IEnumerable is the "subscriber" which calls GetNext() has no control over how long the "publisher" take to return the next value. I think IObservable reverses this problem. That is, the "publisher" (IObservable object) has no control over how long given "subscriber's" (IObserver) OnNext method will take to complete. You can see this issue in the Tick() methods for SpotAgent and RiskAgent.

     

    Does the composobility of IObservable/IObserve help solve this problem?

     

    Looking at your code definitely exposes some issues that I'm sure Erik's team has faced when developing their library.

  • Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx)

    Great video! Really fascinating. I can't wait to see the code - just push it to me Smiley

    A question: The scope of the  IDisposable object returned by Subscribe() was unclear to me. Erik implies you can put it in a using statement, like a normal IDisposable. But the lexical scope of the IDisposable returned seems much different than the lifetime ("runtime scope"?) of the IObserver you pass in. For example, the using statemetn with IDisposable for files is pretty obvious:

      using(fs = new Stream("somefile.txt"))   
      {     
        // ... do work on fs ...   
      } // fs is closed and disposed. 

    But in the IObserver case, I don't think the using statement's scope is appropriate for indicating you don't care about events anymore. Using the autocomplete example above:

    using(q.Subscribe(suggestions => ... ))
    {
    // ???
    }

    What happens in the body of the using statement? All the work seems to occur in the lambda passed to Subscribe. If I understood correctly, as soon as we leave the scope of the using statement, my lambda stops receiving events. Hmm, I bet the object implementing IDisposable has the secret sauce for the reactive framework. Maybe it has a method for testing for conditions and will "yield" until the condition is met? For example if I had another observer which looked for ENTER or TAB on the textbox and indicated that a selection was made, maybe it would let me write this code:

    class KeyObserver :  IObserver<...>
    {
    publi c bool SelectionMade()
    }

    KeyObserver key = ...;

    using(cntrl = q.Subscribe(suggestions => ... ))
    {
    if(! key.SelectionMade())
    sleep;
    }
    // selection has been made and we stop listening to suggest events

    Hopefully that makes some sense. It looks like the beginnings of a way to do reasonable event based programming. Apologies for answering my own question but glad to hear if I'm on track ...

    Two other notes:

     

    Great stuff, get me this code! Smiley

    Justin

  • RiSE at the ​Internation​al Conference on Software Engineering

    Codebook sounds so cool! It would be even better if it could index legacy code in foreign source control providers. For example, my employer has an application which uses  Pick Basic, PHP, C#, C, SQL, and more. Indexing and tracking all of that would be very cool.

  • Expert to Expert - Erik Meijer and Lars Bak: Inside V8 - A Javascript Virtual Machine

    The discussion at the end about the irrelevance of intermediate languages was interesting. I've been dabbling in this area recently and intermediate languages seem to help make compiler/interpreter implementation simpler, but they can lack details you'd like to have in order to produce faster/smaller/safer code.

    On the flip side, at one point Lars said why not compile F# to C#, instead of MSIL. I say you are then hampered by the semantics of C#. Quite a few functional languages compile to C and they all suffer from the tailcall problem and have to generate really unnatural C code (the well-known "trampoline" solution). One advantage of an IL is it can expose features that higher-level languages do not. C# has no concept of a "tailcall", but IL allows it.

    Another argument is that an IL is really an "API ", just a weird one. Programs on either side of the API can change, but as long as the API and its behavior is stable, they can change independently. The same would be true if you generated C# but I would argue that is the kind of API I don't want to code to.

    Anyways, great discussion as always!

    p.s. Erik how can you say only Haskell compilers are written in pure Haskell? For shame!
  • Expert to Expert: Amitabh Srivastava - Windows Azure and Cloud Computing

    Charles -

    Thanks for following up! Looking forward to it ...
  • Expert to Expert: Amitabh Srivastava - Windows Azure and Cloud Computing

    Charles - have you seen Code Canvas frmo Kael Rowan at MSR? It looks SOOO cool and the only info out there is an old video on his blog. Will you consider talking to him? I want to know more! Check out the video at http://blogs.msdn.com/kaelr/archive/2009/03/26/code-canvas.aspx.

    Sorry to hijack here, but I'm not sure how else to get in touch with Charles.

    Can't wait to watch this E2E - thanks again to you and Erik. When are we going to see Erik's lectures???
  • Erik Meijer and Matthew Podwysocki - Perspectives on Functional Programming

    I ran out and got a copy of Intro to Functional Programming after watching this. I can't wait to see Erik's lectures. Always interesting!
  • Brian Beckman: The Zen of Stateless State - The State Monad - Part 1

    Here is Exercise 3. All the code is available for download at

    http://github.com/m4dc4p/statemonad/tree/exercise3

    I added some tags so I can push updates and not break the exercises. 

    The M class is abstracted over the contents of the monad, but it feels like it should be abstracted over the specific monad AND the contents. In Haskell the monad class is declared as:

      class Monad m where
        (>>=) :: m a -> (a -> m b) -> m b -- bind
        return :: a -> m a

    where "m" is abstractd, but I could not get that working in C#. I think that is because C# lacks "higher-kinded" types but I'm not sure. This shows up in my implementation as casts in the overriden bind and @return methods.

    First my M class:

    namespace StateMonad
    {
        namespace Exercise3
        {
            public abstract class M<Contents1>
            {
                public delegate M<Contents2> Maker<Contents2>(Contents1 c);

                public abstract M<Contents2> @bind<Contents2>(M<Contents1> input, Maker<Contents2> maker);

                public abstract M<Contents1> @return(Contents1 contents);
            }

            public class Scp<State, Contents> // State-Content Pair
            {
                public State label { get; set; }
                public Contents lcpContents { get; set; } // New name; don't confuse
                // with the old "contents"
                public override string ToString()
                {
                    return String.Format("Label: {0}, Contents: {1}", label, lcpContents);
                }
            }

            public delegate Scp<State, Contents> S2Scp<State, Contents>(State state);

            public class SM<State, Contents1> : M<Contents1>
            {
                public S2Scp<State, Contents1> s2scp { get; set; }

                public override M<Contents2> bind<Contents2>(M<Contents1> input, Maker<Contents2> maker)
                {
                    // These casts are UGLY but I can't seem to get
                    // rid of them ...
                    return (M<Contents2>) new SM<State, Contents2>
                    {
                        // The new instance of the state monad is a
                        // function from state to state-contents pair,
                        // here realized as a C# lambda expression:

                        s2scp = (st0 =>
                        {
                            // Deconstruct the result of calling the input
                            // monad on the state parameter (done by
                            // pattern-matching in Haskell, by hand here):

                            Scp<State, Contents1> lcp1 = ((SM<State, Contents1>) input).s2scp(st0);
                            State state1 = lcp1.label;
                            Contents1 contents1 = lcp1.lcpContents;

                            // Call the input maker on the contents from
                            // above and apply the resulting monad
                            // instance on the state from above:
                            var m = maker(contents1);
                            var r = ((SM<State, Contents2>)m).s2scp(state1);
                            return r;
                        })
                    };
                }

                public override M<Contents1> @return(Contents1 contents)
                {
                    return new SM<State, Contents1>
                    {
                        s2scp = (st => new Scp<State, Contents1>
                        {
                            label = st,
                            lcpContents = contents
                        })
                    };
                }
            }
       }
    }

    and the ConstrainedTree example I implemetned in Excercise2 (exactly the same except I added some casts). This is about the most painful C# I've ever had to write!  Fortunately teh compiler helps a lot by telling me which types don't match:

    namespace StateMonad
    {
        namespace Exercise3
        {
            public class Size
            {
                public Size(int w, int h) { Height = h; Width = w; }

                public int Width { get; private set; }
                public int Height { get; private set; }

                public override string ToString()
                {
                    return Width.ToString() + ", " + Height.ToString();
                }
            }

            public class ConstrainTree
            {
                public delegate Exercise3.SM<Size, Size> Updater();

                public static Exercise3.SM<Size, Size> RightUpdateState()
                {
                    return new Exercise3.SM<Size, Size>
                    {
                        s2scp = (size => new Exercise3.Scp<Size, Size>
                        {
                            label = new Size(size.Width * 2, size.Height * 2),
                            lcpContents = size
                        })
                    };
                }

                public static Exercise3.SM<Size, Size> LeftUpdateState()
                {
                    return new Exercise3.SM<Size, Size>
                    {
                        s2scp = (size => new Exercise3.Scp<Size, Size>
                        {
                            label = new Size(size.Width / 2, size.Height / 2),
                            lcpContents = new Size(size.Width / 2, size.Height / 2)
                        })
                    };
                }

                public static Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>> Mk<A>(Program.Tr<A> tree, Updater update)
                {
                    if (tree is Program.Lf<A>)
                    {
                        var leaf = (Program.Lf<A>)tree;
                        return (Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>)
                            (new Exercise3.SM<Size, Size>()).bind<Program.Tr<Exercise3.Scp<Size, Size>>>(update(),
                            (contents1 =>
                                (new Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>()).@return(
                                new Program.Lf<Exercise3.Scp<Size, Size>>
                                {
                                    contents = new Exercise3.Scp<Size, Size>
                                    {
                                        label = contents1,
                                        lcpContents = contents1
                                    }
                                })));
                    }
                    else
                    {
                        var branch = (Program.Br<A>)tree;
                        var left = branch.left;
                        var right = branch.right;

                        return (Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>)
                            (new Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>()).bind<Program.Tr<Exercise3.Scp<Size, Size>>>(
                            Mk<A>(left, new Updater(LeftUpdateState)),
                            (newLeft => (new Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>()).bind<Program.Tr<Exercise3.Scp<Size, Size>>>(
                                Mk<A>(right, new Updater(RightUpdateState)),
                                (newRight => (new Exercise3.SM<Size, Program.Tr<Exercise3.Scp<Size, Size>>>()).@return(
                                    new Program.Br<Exercise3.Scp<Size, Size>>
                                    {
                                        left = newLeft,
                                        right = newRight
                                    }
                                    ))
                                    )));
                    }
                }

                /// <summary>
                /// After traversal the width and height on each node will represent the maximum
                /// bounding box size necessary to cover all children of the node, assuming a binary
                /// tree laid out "down". A bounding box of 1 x 1 will over the node itself.
                /// </summary>
                /// <typeparam name="Contents1"></typeparam>
                /// <param name="tree"></param>
                /// <returns></returns>
                public static Program.Tr<Exercise3.Scp<Size, Size>> Bound<A>(Program.Tr<A> tree, Size containerSize)
                {
                    return Mk<A>(tree, new Updater(LeftUpdateState)).s2scp(containerSize).lcpContents;
                }
            }

        }
    }


  • Brian Beckman: The Zen of Stateless State - The State Monad - Part 1

    I (think) I solved exercise 1 & 2. These are really ugly in C# - so much nicer in Haskell. Exercise #2 was terrible, until I realized I needed to change my state updating function depending on if I was going "left" or "right." The single code file is below but I have all the code on github:

      http://github.com/m4dc4p/statemonad/tree/master


    If I continue to work on the exercises, commits will show up there. 

    Exercise1.cs:

        namespace Exercise1
        {
            // Exercise 1: generalize over the type of the state, from int
            // to <S>, say, so that the SM type can handle any kind of
            // state object. Start with Scp<T> --> Scp<S, T>, from
            // "label-content pair" to "state-content pair".

            public class Scp<State, Contents> // State-Content Pair
            {
                public State label { get; set; }
                public Contents lcpContents { get; set; } // New name; don't confuse
                // with the old "contents"
                public override string ToString()
                {
                    return String.Format("Label: {0}, Contents: {1}", label, lcpContents);
                }
            }

            public delegate Scp<State, Contents> S2Scp<State, Contents>(State state);

            public delegate SM<State, Contents2> Maker<State, Contents1, Contents2>(Contents1 input);

            public class SM<State, Contents>
            {
                public S2Scp<State, Contents> s2scp { get; set; }

                public static SM<State, Contents> @return(Contents contents)
                {
                    return new SM<State, Contents>
                    {
                        s2scp = (st => new Scp<State, Contents>
                        {
                            label = st,
                            lcpContents = contents
                        })
                    };
                }

                public static SM<State, Contents2> @bind<Contents2>(SM<State, Contents> inputMonad, Maker<State, Contents, Contents2> inputMaker)
                {
                    return new SM<State, Contents2>
                    {
                        // The new instance of the state monad is a
                        // function from state to state-contents pair,
                        // here realized as a C# lambda expression:

                        s2scp = (st0 =>
                        {
                            // Deconstruct the result of calling the input
                            // monad on the state parameter (done by
                            // pattern-matching in Haskell, by hand here):

                            var lcp1 = inputMonad.s2scp(st0);
                            var state1 = lcp1.label;
                            var contents1 = lcp1.lcpContents;

                            // Call the input maker on the contents from
                            // above and apply the resulting monad
                            // instance on the state from above:

                            return inputMaker(contents1).s2scp(state1);
                        })
                    };
                }
            }
        }

    Exercise2.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace StateMonad
    {
        namespace Exercise2
        {
            // Exercise 2: go from labeling a tree to doing a constrained
            // container computation, as in WPF. Give everything a
            // bounding box, and size subtrees to fit inside their
            // parents, recursively.

            /// <summary>
            /// A container to fit nodes within. Assume nodes fill their
            /// container.
            /// </summary>
            public class Size
            {
                public Size(int w, int h) { Height = h; Width = w; }

                public int Width { get; private set; }
                public int Height { get; private set; }

                public override string ToString()
                {
                    return Width.ToString() + ", " + Height.ToString();
                }
            }

            public class Point
            {
                public Point(int x, int y) { X = x; Y = y; }

                public int X { get; private set; }
                public int Y { get; private set; }

                public override string ToString()
                {
                    return X.ToString() + ", " + Y.ToString();
                }
            }

            /// <summary>
            /// Given root a tree and a container size, find the rectangles for each
            /// node in the tree such that all nodes will fit in the container. Only leafs
            /// actually get sizes - branches represent depth only. It is assumed that leaves
            /// will fill all available space.
            /// </summary>
            public class ConstrainTree
            {
                public delegate Exercise1.SM<Size, Size> Updater();

                public static Exercise1.SM<Size, Size> RightUpdateState()
                {
                    return new Exercise1.SM<Size, Size>
                    {
                        s2scp = (size => new Exercise1.Scp<Size, Size>
                        {
                            label = new Size(size.Width * 2, size.Height * 2),
                            lcpContents = size
                        })
                    };
                }

                public static Exercise1.SM<Size, Size> LeftUpdateState()
                {
                    return new Exercise1.SM<Size, Size>
                    {
                        s2scp = (size => new Exercise1.Scp<Size, Size>
                        {
                            label = new Size(size.Width / 2, size.Height / 2),
                            lcpContents = new Size(size.Width / 2, size.Height / 2)
                        })
                    };
                }

                public static Exercise1.SM<Size, Program.Tr<Exercise1.Scp<Size, Size>>> Mk<A>(Program.Tr<A> tree, Updater update)
                {
                    if (tree is Program.Lf<A>)
                    {
                        var leaf = (Program.Lf<A>)tree;
                        return Exercise1.SM<Size, Size>.bind<Program.Tr<Exercise1.Scp<Size, Size>>>(update(),
                            (contents1 =>
                                Exercise1.SM<Size, Program.Tr<Exercise1.Scp<Size, Size>>>.@return(
                                new Program.Lf<Exercise1.Scp<Size, Size>>
                                {
                                    contents = new Exercise1.Scp<Size, Size>
                                    {
                                        label = contents1,
                                        lcpContents = contents1
                                    }
                                })));
                    }
                    else
                    {
                        var branch = (Program.Br<A>)tree;
                        var left = branch.left;
                        var right = branch.right;

                        return Exercise1.SM<Size, Program.Tr<Exercise1.Scp<Size, Size>>>.bind<Program.Tr<Exercise1.Scp<Size, Size>>>(
                            Mk<A>(left, new Updater(LeftUpdateState)),
                            (newLeft => Exercise1.SM<Size, Program.Tr<Exercise1.Scp<Size, Size>>>.bind<Program.Tr<Exercise1.Scp<Size, Size>>>(
                                Mk<A>(right, new Updater(RightUpdateState)),
                                (newRight => Exercise1.SM<Size, Program.Tr<Exercise1.Scp<Size, Size>>>.@return(
                                    new Program.Br<Exercise1.Scp<Size, Size>>
                                    {
                                        left = newLeft,
                                        right = newRight
                                    }
                                    ))
                                    )));
                    }
                }

                /// <summary>
                /// After traversal the width and height on each node will represent the maximum
                /// bounding box size necessary to cover all children of the node, assuming a binary
                /// tree laid out "down". A bounding box of 1 x 1 will over the node itself.
                /// </summary>
                /// <typeparam name="Contents1"></typeparam>
                /// <param name="tree"></param>
                /// <returns></returns>
                public static Program.Tr<Exercise1.Scp<Size, Size>> Bound<A>(Program.Tr<A> tree, Size containerSize)
                {
                    return Mk<A>(tree, new Updater(LeftUpdateState)).s2scp(containerSize).lcpContents;
                }
            }

        }
    }