tag:blogger.com,1999:blog-35868762.post2136855166291657122..comments2023-05-16T07:56:56.864-07:00Comments on Dead Programmer Society: Ruby Blocks, Closures, and ContinuationsRon Evanshttp://www.blogger.com/profile/06754261780924273552noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-35868762.post-76539317875988970702011-01-19T06:29:09.291-08:002011-01-19T06:29:09.291-08:00Good post, I hadn't realized that a block and ...Good post, I hadn't realized that a block and a closure had differences.larinhttps://www.blogger.com/profile/17393819019136224535noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-20524224829796053242010-11-26T15:17:31.054-08:002010-11-26T15:17:31.054-08:00[...] Some code examples were taken from Ruby Bloc...[...] Some code examples were taken from Ruby Blocks, Closures, and Continuations. [...]<br /><br />Thank you for your examples. They helped me creating a presentation about <a href="http://blog.code-cop.org/2010/10/functional-programming.html" rel="nofollow">Concepts of Functional Programming</a>, given at a local user group event.Peter Koflerhttps://www.blogger.com/profile/09597605582212160361noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-2527362466373775432007-03-13T22:15:00.000-07:002007-03-13T22:15:00.000-07:00One big problem of callc is that it often obscure...One big problem of callc is that it often obscures the control flow of a program. People never know what really happens when they see a bunch of callcc and call methods.<BR/><BR/>setjmp/jongjmp functions in C are similar to callcc and extensive use of those function makes the code less redable. However, in some limited circumstances such as error handling, they are very powerful tools to hack.서광열https://www.blogger.com/profile/07997415382509511910noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-67301230324608121832007-02-16T17:08:00.000-08:002007-02-16T17:08:00.000-08:00Good point about using continuations as a form of ...Good point about using continuations as a form of concurrency. As as example, here is what <B>looks like</B> the equivalent code using Ruby Threads:<BR/><BR/>def foo<BR/> puts "hello world foo"<BR/> Thread.pass<BR/> puts "goodbye world foo"<BR/> Thread.pass<BR/>end<BR/><BR/>def bar<BR/> puts "hello world bar"<BR/> Thread.pass<BR/> puts "goodbye world bar"<BR/> Thread.pass<BR/>end<BR/><BR/>f = Thread.new do<BR/> foo<BR/>end<BR/><BR/>b = Thread.new do<BR/> bar<BR/>end<BR/><BR/>f.join<BR/><BR/>Will output:<BR/>hello world foo<BR/>hello world bar<BR/>goodbye world foo<BR/>goodbye world bar<BR/><BR/>However, even though the output is the same, the path of execution followed is very different in these two bits of code.<BR/><BR/>The "Thread.pass" method does also preserve the state, just like the "callcc" method. Also, like "callcc", eventually control will return to the next line of code after the "Thread.pass". <BR/><BR/>However, "Thread.pass" just gives up control to the thread scheduler. You do not have control over which thread executes next without using some kind of thread synchronization object. If there was another thread waiting to be scheduled, it might execute first.<BR/><BR/>The "callcc" method, on the other hand, allows you to have control by passing in the specific block of code to be executed before returning to continue execution immediately after where the callcc method was invoked. Likewise, local state is preserved for objects in scope.<BR/><BR/>Interestingly, in to the Fowler/Weirich presentation, they say that the Ruby language implementation of Threads and Continuations uses a shared code base. In part, this may explain why the Ruby.NET project is not yet supporting either one.Ron Evanshttps://www.blogger.com/profile/06754261780924273552noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-54674464371020446352007-02-16T11:42:00.000-08:002007-02-16T11:42:00.000-08:00my coroutine example is somewhat flawed, in truth...my coroutine example is somewhat flawed, in truth, the bar is just getting invoked 5 times (rather than invoked once and continued four times). A better example is:<BR/><BR/>def foo(cont)<BR/> puts "hello world foo"<BR/> cont2 = callcc{|cc| cont.call cc}<BR/> puts "goodbye world foo"<BR/> callcc{|cc| cont2.call cc}<BR/>end<BR/><BR/>def bar(cont)<BR/> puts "hello world bar"<BR/> cont2 = callcc{|cc| cont.call cc}<BR/> puts "goodbye world bar"<BR/> callcc{|cc| cont2.call cc}<BR/>end<BR/><BR/>Calling this as<BR/>foo(lambda{|x| bar x})<BR/><BR/>will cause it to output<BR/>hello world foo<BR/>hello world bar<BR/>goodbye world foo<BR/>goodbye world bar<BR/>=> #<Continuation:0x4991c><BR/><BR/>(note that it returns a continuation, this is key for coroutines, each one returns a continuation for the other one to invoke some time later).jaaronhttps://www.blogger.com/profile/05911286705000249753noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-88010334102223335062007-02-16T11:27:00.000-08:002007-02-16T11:27:00.000-08:00I'm glad the issues with your closure example were...I'm glad the issues with your closure example were pointed out. This is very abstract stuff that is really hard to wrap one's head around (which has led to a lot of contradictory and just plain wrong descriptions of closures, and continuations, and so forth). <BR/><BR/>Your continuation example doesn't illustrate one of the most compelling uses of call/cc: coroutines. The idea is that you can achieve a form of cooperative concurrency in a single threaded environment using call/cc.<BR/><BR/>Example:<BR/><BR/>def foo(cont)<BR/> 5.times do<BR/> puts "hello world"<BR/> callcc{ |cc| cont.call(cc) }<BR/> end<BR/>end<BR/><BR/>def bar(cont)<BR/> 5.times do<BR/> puts "goodbye world"<BR/> callcc {|cc| cont.call(cc) }<BR/> end<BR/>end<BR/><BR/># invoke foo<BR/>foo(lambda{|x| bar x})<BR/><BR/># Note: foo is invoked with a block that calls bar as its argument.<BR/># this will cause the two routines to alternate execution.<BR/># the output will look like:<BR/>hello world<BR/>goodbye world<BR/>hello world<BR/>goodbye world<BR/>hello world<BR/>goodbye world<BR/>hello world<BR/>goodbye world<BR/>hello world<BR/>goodbye world<BR/>=> 5<BR/><BR/>Continuations also provide an alternate model of computation; instead of calling a subroutine, waiting for it to return, then using its result, the subroutine is called with the current continuation the callee does its thing then rather than returning executes the continuation it received.jaaronhttps://www.blogger.com/profile/05911286705000249753noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-91944735077994905792007-02-16T10:38:00.000-08:002007-02-16T10:38:00.000-08:00It is indeed true that an important distiction bet...It is indeed true that an important distiction between blocks and closures is that there is data available within the scope of the closure. Thank you for your very nice example, which does show that better than mine did.Ron Evanshttps://www.blogger.com/profile/06754261780924273552noreply@blogger.comtag:blogger.com,1999:blog-35868762.post-41676325725161623612007-02-16T08:30:00.000-08:002007-02-16T08:30:00.000-08:00It should be noted that you haven't actually given...It should be noted that you haven't actually given an example of <I>closures</I> in your post: your first example is merely the definition and usage of an anonymous first-class function (a block in ruby-lingo): <B>it doesn't use its definition context</B>, and therefore even though the function does technically close over its context, that fact is not demonstrated and completely useless.<BR/><BR/>Now if we want to demonstrate a closure we need a few things: a scope in which the block is created (a function will do), and data local to that scope. For example:<BR/><BR/>class GreetingGenerator<BR/> def initialize(greeting)<BR/> @block = lambda {|name| puts "#{greeting}, #{name}"}<BR/> end<BR/> def greet(name)<BR/> @block.call name<BR/> end<BR/>end<BR/><BR/>frenchGreeting = GreetingGenerator.new "Bonjour"<BR/>englishGreeting = GreetingGenerator.new "Hello"<BR/>southernGreeting = GreetingGenerator.new "Howdy"<BR/>japaneseGreeting = GreetingGenerator.new "Ohayou"<BR/>germanGreeting = GreetingGenerator.new "Guten Tag"<BR/><BR/>frenchGreeting.greet "John"<BR/>englishGreeting.greet "John"<BR/>southernGreeting.greet "John"<BR/>japaneseGreeting.greet "John"<BR/>germanGreeting.greet "John"<BR/><BR/>puts greeting # just checking that the `greeting` variable doesn't exist in the main scope<BR/><BR/>In this case, each block carries a little information with it ("greeting").<BR/><BR/>This information doesn't come from its application context (the `greet` method), nor does it come from the global scope (as we check in the script's last line): it comes from the <B>definition scope</B> of the block (the `initialize` method).<BR/><BR/>The blocks <I>close over</I> their definition context and carry that context with them long after the definition scope has been closed and their definition contexts (potentially) reclaimed.<BR/><BR/>We also check that the `closure` is block-specific by creating multiple blocks and calling them only after all of them have been created.Unknownhttps://www.blogger.com/profile/10261896483118253882noreply@blogger.com