Jump to page: 1 2
Thread overview
[dmd-concurrency] Pattern matching on message receives, error handling
Jan 13, 2010
Mark Kegel
Jan 13, 2010
Sean Kelly
Jan 13, 2010
Mark Kegel
Jan 13, 2010
Fawzi Mohamed
Jan 13, 2010
Sean Kelly
Jan 13, 2010
Sean Kelly
Jan 13, 2010
Kevin Bealer
Jan 13, 2010
Sean Kelly
Jan 14, 2010
Mark Kegel
January 12, 2010
I've been very interested in the actor style for the past year or so. Altogether, I'm very glad to see D at least giving this programming style a go.

After reading through the example posted in the latest fragment, I have to confess that I'm a little concerned. The actor style is something you really have to embrace from the groundup.

When I think of how message passing is accomplished in either Erlang or Scala (the two languages I've looked at with message passing / actors), one thing strikes as being absolutely essential to getting this paradigm right: pattern matching. As functional languages (arguably more so for Erlang than Scala, but whatever), this is something that just makes messages easy to handle.

I don't see pattern matching as a first class feature of D. And this makes me scared. I do not want to have to handle complex dispatch (or silly implicit message protocols, please) without the saving grace of builtin pattern matching.

Call me silly, but how many of you have seen 1000 line switch statements when polymorphism was called for? And what is polymorphism but dispatch / pattern matching over argument type?

So when I see code like this:

       auto msg = receiveOnly!(Tid, int)();
       if (!msg[0]) return;
       writeln("Secondary thread: ", msg[1]);
       msg[0].send(thisTid);

...where you the first thing you tell new readers about how to test what message you just got is with an 'if' statement, I get scared. Please give users a more elegant mechanism, and if you need ideas about what it could look like I would suggest that Scala offers a passable (and maybe even compatible) syntax.

Since I read through a draft, I would also like to suggest that another topic will need to be discussed in the final edition: error handling. Throwing exceptions on unexpected messages is nice, but what happens when spawned actors (actor != thread) have a bad day? What kinds of guarentees can the other actors in the system make about their state or does the entire theater burn down if someone is missing some hairspray?

But really, one of the major things erlang has going for it (and that scala completely missed) is that error handling is important! If you can tell when other actors die, recover knowing that nothing else is broken, then you can at least start to build resilient systems. Does D even give you this? For a systems programming language in which segfaults and memory corruption are possible, even expected, how does D meld this to the actor system in a way that keeps the system stable?


Mark Kegel
January 12, 2010
Mark Kegel wrote:
> Call me silly, but how many of you have seen 1000 line switch statements when polymorphism was called for? And what is polymorphism but dispatch / pattern matching over argument type?
> 
> So when I see code like this:
> 
>        auto msg = receiveOnly!(Tid, int)();
>        if (!msg[0]) return;
>        writeln("Secondary thread: ", msg[1]);
>        msg[0].send(thisTid);
> 
> ...where you the first thing you tell new readers about how to test what message you just got is with an 'if' statement, I get scared. Please give users a more elegant mechanism, and if you need ideas about what it could look like I would suggest that Scala offers a passable (and maybe even compatible) syntax.

You have a point, but quite a weak one. First, the example above is not illustrative on the pattern matching capabilities of D; we'll get to those with the full-fledged receive() function. For now I just considered fine to send a null Tid to signal loop termination. With receive() you can dispatch to various functions depending on the types of the arguments. With introspection it's very easy to dispatch messages to an object depending on their types.

I don't agree with the comparison with switch() vs. virtuals at all. The problem with switch() is that it breaks modularity by requiring all cases to be present in the same place. On the contrary, pattern matching _also_ requires all cases to be present together (which makes me believe it's much less powerful than it's cracked to be). So if I pattern match with

patternmatch (receive()) {
     case int x: ...
     case (string a, int b): ...
}

versus

receive(
     (int x) { ... },
     (string a, int b) { ... }
);

we're only talking about a difference in syntax, not power. So essentially you shouldn't be scared just because there's no built-in syntax for something that can be expressed within the existing language with the same power.

> Since I read through a draft, I would also like to suggest that another topic will need to be discussed in the final edition: error handling.

I'm trying to make time for that too.


Andrei
January 12, 2010
On Jan 12, 2010, at 7:54 PM, Andrei Alexandrescu wrote:

> Mark Kegel wrote:
>> Call me silly, but how many of you have seen 1000 line switch
>> statements when polymorphism was called for? And what is
>> polymorphism but dispatch / pattern matching over argument type?
>> So when I see code like this:
>>       auto msg = receiveOnly!(Tid, int)();
>>       if (!msg[0]) return;
>>       writeln("Secondary thread: ", msg[1]);
>>       msg[0].send(thisTid);
>> ...where you the first thing you tell new readers about how to
>> test what message you just got is with an 'if' statement, I get
>> scared. Please give users a more elegant mechanism, and if you
>> need ideas about what it could look like I would suggest that
>> Scala offers a passable (and maybe even compatible) syntax.
> 
> You have a point, but quite a weak one. First, the example above is not illustrative on the pattern matching capabilities of D; we'll get to those with the full-fledged receive() function. For now I just considered fine to send a null Tid to signal loop termination. With receive() you can dispatch to various functions depending on the types of the arguments. With introspection it's very easy to dispatch messages to an object depending on their types.
> 
> I don't agree with the comparison with switch() vs. virtuals at all. The problem with switch() is that it breaks modularity by requiring all cases to be present in the same place. On the contrary, pattern matching _also_ requires all cases to be present together (which makes me believe it's much less powerful than it's cracked to be). So if I pattern match with
> 
> patternmatch (receive()) {
>    case int x: ...
>    case (string a, int b): ...
> }
> 
> versus
> 
> receive(
>    (int x) { ... },
>    (string a, int b) { ... }
> );
> 
> we're only talking about a difference in syntax, not power. So essentially you shouldn't be scared just because there's no built-in syntax for something that can be expressed within the existing language with the same power.

It's worth adding that Erlang does provide pattern matching capabilities that have to be a run time in D, but I'm not sure this same capability is available in Scala, for example.  In our case, this will probably be handled manually by the user, with possibly some fancier way to do it later (metaprogramming can do quite a lot in D).  For example:

    receive(
        (int x) { ... },
        (string a, int b) { if (a == "blah") return false; ...; return true; }
    );

Here, the second function checks string a to see if it matches a pattern, and returns true/false to indicate that there was a match.

January 13, 2010
The reason I'm scared is fairly simple, I've seen this exact design (well maybe not exact, but the similarities are too great to ignore) used before. I worked for a couple of years on a large Telco system. Message handling was its bread and butter. We had lots of independent process with probably thousands of different messages flying through the system.

One thing that got beat into my head was that you really, really want to be able to easily trace the sequence of messages in a system. What pattern matching gives you is a more concise notation for telling outside users what is handled and what is not. That is the protocols that actors use to communicate are made explicit, not left implicit and thus recalcuated everytime you want to know how it works. In essence you can segment your code more cleanly along logical communication lines rather than function signature, since one function signature might be the entry point for fifteen different completely orthogonal messages.

It really all comes down to how easily the syntax of the language scales to large designs. Sure, taking anonymous functions and then testing for all messages that matching that signature is exactly as powerful as pattern matching, but I would contend that its a lot less readable.

So lets assume that you let receive() take some kind of pattern object, what might this look like? To write it out in the worst possible way, here's one idea:

receive( pattern("foo", _1) + (string a, string b) { ...},
            pattern("bar", _1) + (string a, string b) { ...},
            pattern(_1, _2) + (string a, string b) { ...},
            ...
          );

I've used _* placeholders similar to the way boost lambda does.

While I've intentionally made this as ugly (and self documenting) as possible, I really have no idea how'd you'd clean this syntax up, since I'm just not that familiar with D's introspection capabilites. Does any one more familiar with D have a better way to implement pattern matching?

Mark


On Tue, Jan 12, 2010 at 11:38 PM, Sean Kelly <sean at invisibleduck.org> wrote:
> On Jan 12, 2010, at 7:54 PM, Andrei Alexandrescu wrote:
>
>> Mark Kegel wrote:
>>> Call me silly, but how many of you have seen 1000 line switch
>>> statements when polymorphism was called for? And what is
>>> polymorphism but dispatch / pattern matching over argument type?
>>> So when I see code like this:
>>> ? ? ? auto msg = receiveOnly!(Tid, int)();
>>> ? ? ? if (!msg[0]) return;
>>> ? ? ? writeln("Secondary thread: ", msg[1]);
>>> ? ? ? msg[0].send(thisTid);
>>> ...where you the first thing you tell new readers about how to
>>> test what message you just got is with an 'if' statement, I get
>>> scared. Please give users a more elegant mechanism, and if you
>>> need ideas about what it could look like I would suggest that
>>> Scala offers a passable (and maybe even compatible) syntax.
>>
>> You have a point, but quite a weak one. First, the example above is not illustrative on the pattern matching capabilities of D; we'll get to those with the full-fledged receive() function. For now I just considered fine to send a null Tid to signal loop termination. With receive() you can dispatch to various functions depending on the types of the arguments. With introspection it's very easy to dispatch messages to an object depending on their types.
>>
>> I don't agree with the comparison with switch() vs. virtuals at all. The problem with switch() is that it breaks modularity by requiring all cases to be present in the same place. On the contrary, pattern matching _also_ requires all cases to be present together (which makes me believe it's much less powerful than it's cracked to be). So if I pattern match with
>>
>> patternmatch (receive()) {
>> ? ?case int x: ...
>> ? ?case (string a, int b): ...
>> }
>>
>> versus
>>
>> receive(
>> ? ?(int x) { ... },
>> ? ?(string a, int b) { ... }
>> );
>>
>> we're only talking about a difference in syntax, not power. So essentially you shouldn't be scared just because there's no built-in syntax for something that can be expressed within the existing language with the same power.
>
> It's worth adding that Erlang does provide pattern matching capabilities that have to be a run time in D, but I'm not sure this same capability is available in Scala, for example. ?In our case, this will probably be handled manually by the user, with possibly some fancier way to do it later (metaprogramming can do quite a lot in D). ?For example:
>
> ? ?receive(
> ? ? ? ?(int x) { ... },
> ? ? ? ?(string a, int b) { if (a == "blah") return false; ...; return true; }
> ? ?);
>
> Here, the second function checks string a to see if it matches a pattern, and returns true/false to indicate that there was a match.
>
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
>
January 13, 2010
On 13-gen-10, at 16:44, Mark Kegel wrote:

> The reason I'm scared is fairly simple, I've seen this exact design (well maybe not exact, but the similarities are too great to ignore) used before. I worked for a couple of years on a large Telco system. Message handling was its bread and butter. We had lots of independent process with probably thousands of different messages flying through the system.
>
> One thing that got beat into my head was that you really, really want to be able to easily trace the sequence of messages in a system. What pattern matching gives you is a more concise notation for telling outside users what is handled and what is not. That is the protocols that actors use to communicate are made explicit, not left implicit and thus recalcuated everytime you want to know how it works. In essence you can segment your code more cleanly along logical communication lines rather than function signature, since one function signature might be the entry point for fifteen different completely orthogonal messages.
>
> It really all comes down to how easily the syntax of the language scales to large designs. Sure, taking anonymous functions and then testing for all messages that matching that signature is exactly as powerful as pattern matching, but I would contend that its a lot less readable.
>
> So lets assume that you let receive() take some kind of pattern object, what might this look like? To write it out in the worst possible way, here's one idea:
>
> receive( pattern("foo", _1) + (string a, string b) { ...},
>            pattern("bar", _1) + (string a, string b) { ...},
>            pattern(_1, _2) + (string a, string b) { ...},
>            ...
>          );
>
> I've used _* placeholders similar to the way boost lambda does.
>
> While I've intentionally made this as ugly (and self documenting) as possible, I really have no idea how'd you'd clean this syntax up, since I'm just not that familiar with D's introspection capabilites. Does any one more familiar with D have a better way to implement pattern matching?

maybe the place where you want to go looks like exposing an object
that has several methods, and thus a fixed interface, and have
execution on that object be sequential by default on the server (what
I do in blip.parallel.rpc).
Also to send a message in general you use a proxy tat has exactly the
same exposed interface and does the remote call.

Fawzi
>
> Mark
>
>
> On Tue, Jan 12, 2010 at 11:38 PM, Sean Kelly <sean at invisibleduck.org> wrote:
>> On Jan 12, 2010, at 7:54 PM, Andrei Alexandrescu wrote:
>>
>>> Mark Kegel wrote:
>>>> Call me silly, but how many of you have seen 1000 line switch
>>>> statements when polymorphism was called for? And what is
>>>> polymorphism but dispatch / pattern matching over argument type?
>>>> So when I see code like this:
>>>>       auto msg = receiveOnly!(Tid, int)();
>>>>       if (!msg[0]) return;
>>>>       writeln("Secondary thread: ", msg[1]);
>>>>       msg[0].send(thisTid);
>>>> ...where you the first thing you tell new readers about how to
>>>> test what message you just got is with an 'if' statement, I get
>>>> scared. Please give users a more elegant mechanism, and if you
>>>> need ideas about what it could look like I would suggest that
>>>> Scala offers a passable (and maybe even compatible) syntax.
>>>
>>> You have a point, but quite a weak one. First, the example above is not illustrative on the pattern matching capabilities of D; we'll get to those with the full-fledged receive() function. For now I just considered fine to send a null Tid to signal loop termination. With receive() you can dispatch to various functions depending on the types of the arguments. With introspection it's very easy to dispatch messages to an object depending on their types.
>>>
>>> I don't agree with the comparison with switch() vs. virtuals at all. The problem with switch() is that it breaks modularity by requiring all cases to be present in the same place. On the contrary, pattern matching _also_ requires all cases to be present together (which makes me believe it's much less powerful than it's cracked to be). So if I pattern match with
>>>
>>> patternmatch (receive()) {
>>>    case int x: ...
>>>    case (string a, int b): ...
>>> }
>>>
>>> versus
>>>
>>> receive(
>>>    (int x) { ... },
>>>    (string a, int b) { ... }
>>> );
>>>
>>> we're only talking about a difference in syntax, not power. So essentially you shouldn't be scared just because there's no built- in syntax for something that can be expressed within the existing language with the same power.
>>
>> It's worth adding that Erlang does provide pattern matching capabilities that have to be a run time in D, but I'm not sure this same capability is available in Scala, for example.  In our case, this will probably be handled manually by the user, with possibly some fancier way to do it later (metaprogramming can do quite a lot in D).  For example:
>>
>>    receive(
>>        (int x) { ... },
>>        (string a, int b) { if (a == "blah") return false; ...;
>> return true; }
>>    );
>>
>> Here, the second function checks string a to see if it matches a pattern, and returns true/false to indicate that there was a match.
>>
>> _______________________________________________
>> dmd-concurrency mailing list
>> dmd-concurrency at puremagic.com
>> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
>>
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency

January 13, 2010
On Jan 13, 2010, at 7:44 AM, Mark Kegel wrote:

> The reason I'm scared is fairly simple, I've seen this exact design (well maybe not exact, but the similarities are too great to ignore) used before. I worked for a couple of years on a large Telco system. Message handling was its bread and butter. We had lots of independent process with probably thousands of different messages flying through the system.

I had my start in telco as well, which I suppose is why I'm so soft on Erlang (we used C++ top to bottom).

> One thing that got beat into my head was that you really, really want to be able to easily trace the sequence of messages in a system. What pattern matching gives you is a more concise notation for telling outside users what is handled and what is not. That is the protocols that actors use to communicate are made explicit, not left implicit and thus recalcuated everytime you want to know how it works. In essence you can segment your code more cleanly along logical communication lines rather than function signature, since one function signature might be the entry point for fifteen different completely orthogonal messages.

I've been trying to match the Erlang receive syntax as closely as possible.  You're right that what is still missing is a clearly readable run time pattern matching syntax. I hope that someone will create something with templates and such to take care of this.

> It really all comes down to how easily the syntax of the language scales to large designs. Sure, taking anonymous functions and then testing for all messages that matching that signature is exactly as powerful as pattern matching, but I would contend that its a lot less readable.
> 
> So lets assume that you let receive() take some kind of pattern object, what might this look like? To write it out in the worst possible way, here's one idea:
> 
> receive( pattern("foo", _1) + (string a, string b) { ...},
>            pattern("bar", _1) + (string a, string b) { ...},
>            pattern(_1, _2) + (string a, string b) { ...},
>            ...
>          );
> 
> I've used _* placeholders similar to the way boost lambda does.
> 
> While I've intentionally made this as ugly (and self documenting) as possible, I really have no idea how'd you'd clean this syntax up, since I'm just not that familiar with D's introspection capabilites. Does any one more familiar with D have a better way to implement pattern matching?

I'd imagine patterns typically use constants, so you could at least do something like this:

    receive( test("p1 == \"foo\" || p2 == 5)( (string a, int b) {...} ) );

though this could still use a lot of improvement.
January 13, 2010
Sean Kelly wrote:
>> While I've intentionally made this as ugly (and self documenting) as possible, I really have no idea how'd you'd clean this syntax up, since I'm just not that familiar with D's introspection capabilites. Does any one more familiar with D have a better way to implement pattern matching?
> 
> I'd imagine patterns typically use constants, so you could at least do something like this:
> 
>     receive( test("p1 == \"foo\" || p2 == 5)( (string a, int b) {...} ) );
> 
> though this could still use a lot of improvement.

Guess you meant to close the quote like this:

receive( test("p1 == \"foo\" || p2 == 5")( (string a, int b) {...} ) );

But then again, what are we gaining compared to:

receive(
     bool(string a, int b) { if (a != "foo" && b != 5) return false; ...} );

?

I agree that a nicer pattern matching would be nice to have, but that's about it - "nice to have". It's not enabling.

That being said, it's worth looking into better ways of expressing matches, be they via current language mechanisms or new.


Andrei

January 13, 2010
Sean Kelly wrote:
> It's worth adding that Erlang does provide pattern matching capabilities that have to be a run time in D, but I'm not sure this same capability is available in Scala, for example.  In our case, this will probably be handled manually by the user, with possibly some fancier way to do it later (metaprogramming can do quite a lot in D).  For example:
> 
>     receive(
>         (int x) { ... },
>         (string a, int b) { if (a == "blah") return false; ...; return true; }
>     );
> 
> Here, the second function checks string a to see if it matches a pattern, and returns true/false to indicate that there was a match.

For the OO crowd, I think this will be quite useful:

class Handler
{
     bool receive(int x) { ... }
     bool receive(int x, double y) { ... }
     bool receive(string s, int x) { ... }
     ...
}

auto h1 = new Handler;
auto h2 = new Handler;
receive(h1, h2); // dispatches automatically
                  // starting from h1

Sean, may I put this in confidence that you'll find the time to implement it? Walter, we may need some introspection chops for that too.


Andrei
January 13, 2010
On Jan 13, 2010, at 10:55 AM, Andrei Alexandrescu wrote:
> 
> For the OO crowd, I think this will be quite useful:
> 
> class Handler
> {
>    bool receive(int x) { ... }
>    bool receive(int x, double y) { ... }
>    bool receive(string s, int x) { ... }
>    ...
> }
> 
> auto h1 = new Handler;
> auto h2 = new Handler;
> receive(h1, h2); // dispatches automatically
>                 // starting from h1
> 
> Sean, may I put this in confidence that you'll find the time to implement it? Walter, we may need some introspection chops for that too.

Sure.  Hopefully tupleof and/or __traits will be enough, but I guess we'll see.
January 13, 2010
On Wed, Jan 13, 2010 at 1:55 PM, Andrei Alexandrescu <andrei at erdani.com>wrote:

> Sean Kelly wrote:
>
>> It's worth adding that Erlang does provide pattern matching capabilities that have to be a run time in D, but I'm not sure this same capability is available in Scala, for example.  In our case, this will probably be handled manually by the user, with possibly some fancier way to do it later (metaprogramming can do quite a lot in D).  For example:
>>
>>    receive(
>>        (int x) { ... },
>>        (string a, int b) { if (a == "blah") return false; ...; return
>> true; }
>>    );
>>
>> Here, the second function checks string a to see if it matches a pattern, and returns true/false to indicate that there was a match.
>>
>
> For the OO crowd, I think this will be quite useful:
>
> class Handler
> {
>    bool receive(int x) { ... }
>    bool receive(int x, double y) { ... }
>    bool receive(string s, int x) { ... }
>    ...
> }
>
> auto h1 = new Handler;
> auto h2 = new Handler;
> receive(h1, h2); // dispatches automatically
>                 // starting from h1
>
> Sean, may I put this in confidence that you'll find the time to implement it? Walter, we may need some introspection chops for that too.
>
>
> Andrei


Also, what does it mean?

If I do this syntax:

 auto dg1 = void delegate(A x) { ... };
auto dg2 = void delegate(B x) { ... };

receive(dg1, dg2);

Does this mean receive anything that matches dg1, and if nothing is found, receive anything that matches dg2?  Or does it mean to take the first message in order that matches either one?

Kevin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/dmd-concurrency/attachments/20100113/62db27d2/attachment-0001.htm>
« First   ‹ Prev
1 2