{"id":355,"date":"2007-03-21T20:53:54","date_gmt":"2007-03-21T20:53:54","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/03\/21\/an-experiment-with-calculus-and-programming-language-design\/"},"modified":"2007-03-21T20:53:54","modified_gmt":"2007-03-21T20:53:54","slug":"an-experiment-with-calculus-and-programming-language-design","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2007\/03\/21\/an-experiment-with-calculus-and-programming-language-design\/","title":{"rendered":"An Experiment with &#960;-calculus and Programming Language Design"},"content":{"rendered":"<p> I feel like a bit of a change of pace, and trying a bit of an experiment.<\/p>\n<p> Re-reading Backus&#8217;s old FP work reminds me of what I was doing the last time I read it, which<br \/>\nwas back in grad school. At the time, I was working on some stuff involving parallel computation,<br \/>\nand I discovered Robin Milner&#8217;s &pi;-calculus as a tool for describing parallel computation. You<br \/>\ncan think of &pi;-calculus as being a sort of parallel (pun intended) for the &lambda;-calculus: in<br \/>\nsequential, single-threaded computation, &lambda;-calculus can be a great tool for describing what<br \/>\nthings mean. But &lambda;-calculus has absolutely no way of describing the concept of multiple<br \/>\nthings happening at once. &pi;-calculus is fundamentally built on the concept of multiple threads<br \/>\nwhich can only share information by passing messages.<\/p>\n<p> There&#8217;s a reason that reading Backus made me think of this, beyond just the temporal coincendence of the fact that I was working with &pi;-calculus the last time I read Backus&#8217;s<br \/>\nFP paper. One of the major points that Backus made was how poorly the vonNeumann model was<br \/>\nat describing many computations; that has become <em>far<\/em> more true in recent years. Even my laptop now has multiple processors; computation just isn&#8217;t single-threaded anymore. But most programming languages are basically deeply single-threaded. Even Haskell, for all of its functional<br \/>\npurity, isn&#8217;t particularly good at multi-threaded execution. But I&#8217;ve always thought it would be<br \/>\na great idea to build a language around &pi;-calculus the way that ML is built around &lambda;-calculus.<\/p>\n<p> So my experiment, such as it is, is to see if I can work through how to create an actual, useful, non-pathological programming language based on the &pi;-calculus; and not just do that,<br \/>\nbut do it publicly, here on the blog.<\/p>\n<p><!--more--><\/p>\n<p> The obvious starting place is with a quick overview of what &pi;-calculus is. Today, I&#8217;llgive you a quick overview of it; and then in another post, I&#8217;ll dive into a bit of semantics &#8211; but only as much semantics as is really relevant to building a language out of it.<\/p>\n<p> &pi;-calculus is really remarkably simple. The idea is that you describe a set of<br \/>\n<em>processes<\/em> using the syntax of the calculus, and then there are a set of reduction rules<br \/>\nthat describe how the computation actually executes. Syntactically, there are really only six<br \/>\nconstructs in &lambda;-calculus; and some of those can be viewed as &#8220;compounds&#8221;. The basic<br \/>\nconstructs are:<\/p>\n<ol>\n<li> Parallel composition: if P and Q are both processes then &#8220;<code>P|Q<\/code>&#8221; is a process which runs &#8220;P&#8221; and &#8220;Q&#8221; in parallel.<\/li>\n<li> Non-deterministic choice: if P and Q are both processes, then &#8220;P+Q&#8221; is a process<br \/>\nwhich will be non-deterministically transformed into either &#8220;P&#8221; or &#8220;Q&#8221;, but not both.<\/li>\n<li> Receive a message. If &#8220;P&#8221; is a process, and &#8220;c&#8221; is the name of a communication channel, and<br \/>\n&#8220;x&#8221; is an identifier, then then &#8220;<code>c(x).P<\/code>&#8221; waits for a message to be received<br \/>\non channel &#8220;c&#8221;, binds the contents of that message to the identifier  &#8220;x&#8221;,<br \/>\nand then becomes process P. This is very much like the &pi;-calculus version of<br \/>\na &lambda;abstraction.<\/li>\n<li> Send a message: If &#8220;P&#8221; is a process, &#8220;x&#8221; is a value, and &#8220;c&#8221; is the name of a communication channel, then &#8220;<code>*c(x).P<\/code>&#8221; sends the value &#8220;x&#8221; as a message on channel &#8220;c&#8221; and then proceeds with process &#8220;P&#8221;. (In standard &pi;-calculus syntax, this would be written a c with an overbar, but that&#8217;s very awkward in HTML, so I&#8217;m using &#8220;*&#8221; as an alternative.) <\/li>\n<li> Replication: If &#8220;P&#8221; is a process, then &#8220;<code>!P<\/code>&#8221; is a process consisting of an arbitrary<br \/>\nnumber of copies of P. &#8220;<code>!P<\/code>&#8221; is syntactically equivalent to &#8220;<code>P|!P<\/code>&#8220;. You can think of &#8220;!P&#8221; as being something like an HTTP service: no matter<br \/>\nhow many times you send an HTTP request to a server, there&#8217;s always an HTTP listener waiting<br \/>\nto receive your request.<\/li>\n<li> Channel creation: If &#8220;P&#8221; is the name of a process, and &#8220;x&#8221; is an unbound identifier,<br \/>\nthen &#8220;(&nu;x)P&#8221; creates a new communication channel bound to the identifier &#8220;x&#8221;, and<br \/>\nthen proceeds with process P (where the new identifier is in scope.)<\/li>\n<li> Process termination\/null process. &#8220;0&#8221; is an identifier for a null process. It&#8217;s generally<br \/>\nused to describe process termination: when a process P become 0, it halts.<\/li>\n<\/ol>\n<p> Computations in &pi;-calculus is described in terms of a fundamental reduction rule, &rarr;, which is read &#8220;reduces in one step&#8221; (think of &rarr; as the &pi;-calculus version of &beta;-reduction): &#8220;<code>*x(z).P | x(y).Q&rarr;P|(Q[z\/y])<\/code>&#8220;, where Q[z\/y] means &#8220;Q with z replacing y&#8221;. <\/p>\n<p> To clarify the what this means in the context of the other constructs, we also usually say the following, although strictly speaking, they should be derivable from the syntax and the basic reduction rule:<\/p>\n<ul>\n<li> If &#8220;P&rarr;Q&#8221; then &#8220;P|R&rarr;Q|R&#8221;<\/li>\n<li> If &#8220;P&rarr;Q&#8221; then &#8220;(&nu;x)P &rarr; (&nu;x)Q&#8221;.<\/li>\n<li> If &#8220;(P&rarr;Q)&#8221; then &#8220;(P+R)&rarr;Q&#8221;.<\/li>\n<\/ul>\n<p> Suppose that &#8220;out&#8221; is a communication channel that prints to a standard output. Then<br \/>\nthe following is a little &#8220;program&#8221; in &pi;-calculus which prints hello world.<\/p>\n<pre>\n(&nu;in)( (!(in(x).*out(x).0))\n| (*in(hello).*in(world).0) )\n<\/pre>\n<p> We can walk through a quick reduction of that:<\/p>\n<pre>\n(&nu;in)( (!(in(x).*out(x).0))\n| (*in(hello).*in(world).0) )\n= (&nu;in)( (in(x).*out(x).0|!(in(x).*out(x).0))\n| (*in(hello).*in(world).0) )    <em>[! expansion]<\/em>\n&rarr; (&nu;in)( *out(hello).0|!(in(x).*out(x).0))\n| (*in(world).0) )    <em>[one &rarr; step on channel \"in\"]<\/em>\n&rarr; (&nu;in)( !(in(x).*out(x).0))\n| (*in(world).0) )    <em>[one &rarr; step with the \"invisible\" standard out process step]<\/em>\n= (&nu;in)( (in(x).*out(x).0|!(in(x).*out(x).0))\n| (*in(world).0) )    <em>[Another \"!\" expansion]<\/em>\n&rarr; (&nu;in)( *out(world).0|!(in(x).*out(x).0))\n| (0) )    <em>[&rarr; on \"in\"]<\/em>\n&rarr; (&nu;in)(!(in(x).*out(x).0)) <em>[&rarr; with standard out process on \"out\".]<\/em>\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I feel like a bit of a change of pace, and trying a bit of an experiment. Re-reading Backus&#8217;s old FP work reminds me of what I was doing the last time I read it, which was back in grad school. At the time, I was working on some stuff involving parallel computation, and I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[33,54],"tags":[],"class_list":["post-355","post","type-post","status-publish","format-standard","hentry","category-logic","category-programming"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-5J","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=355"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/355\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=355"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}