Você está na página 1de 13

1

More NextBlog

CreateBlog SignIn

ALGORITHMICALLY
CHALLENGED
R A NDO M T H O U G H T S O F A N I T W O R K ER I N T H E S T O NE A G E O F CO MP U T ER S CI ENCE.

SATU RDAY , JU LY 4, 2009

ABOU T ME

DelimitedContinuationsExplained(inScala)
OneofthepromisedfeaturesforScala2.8isadelimited
continuationsplugin.AbriefdescriptionwasgivenintheScala
site,thoughtheexampleswere,inmyopinion,badlychosen.
D AN I E L SOBRAL

Thoughmyunderstandingisstillprettybare,Iintendtosheda
bitoflightonithere.Thisisnotdirectedattheexperts,the

Follow

845

peoplewhowillactuallyusethemregularly,butatthepeople

I'manITguy,agamer

whowillseethemoccasionallyandwillwonderwhattheheckis

andsomeonecurious

happening.Also,Imightquitepossiblyhavemademistakes

aboutmyprofession,

below.Ifyoudofindmistakes,pleasecorrectmeandIllfixthe

politics,philosophyand

post.

anythingthatengages
themind.Ilikemovies,

First,letsdiscusswhyScalaisadoptingcontinuations,and

booksandcats.I'ma

what,exactly,arethem.

formerFreeBSD
committer,anda

Continuationsare,inessence,aprogramflowcontrolstructure,

present(small)

likeconditionalstatements,loops,exceptions,etc.Asmentioned

contributortoScala.

inScalasContinuationsPaper,classical,orfull,continuations

V I EW MY CO MP LET E P R O FI LE

canbeseenasafunctionalversionofGOTOstatements.Ifyou
haventheardofGOTOstatementsbefore,itsenoughtoknow
theyareessentiallyextinctforgoodreason.

LI NK ED P R O FI LE

DanielSobral
Stillquoting,delimitedcontinuationsaremorelikeregular
functionsandlesslikeGOTOs,andthismakesthemmore
powerfulthantheclassicalones.So,basically,wearelooking

S T ACK O VER FLO W

intoawayofchangingtheexecutionflowofaprogram.
Usingcontinuationsisnoteasy.Infact,itsdifficultanderror
prone,accordingtosomereferences.Buttheycanbevery
efficientperformancewise,andtheyallowonetobuildlibraries
withsomeinterestingflowcontrolabstractionsbuiltontopof
continuations.
Forthesereasons,IdoubtmostScalauserswillhavemuch
contactwiththem,andIsuspecttheoneswhodowillregretthe
necessity.Thepeoplewritinglibrariesandframeworks,though,

ProgrammingScala
ScalaScalaScala
ResetCode

willlikelybegratefulthattheyareavailable.Inthatrespect,
continuationisnotunlikesomeotheradvancedScalafeatures.

ADDTH IS

Share |

Ok,soletsgettothem.InScala,continuationswillbeused
throughtwokeywords(orfunctions?),resetandshift.Like
catchandthrow,ashiftalwayshappeninsideareset,

S H A R ED O N LI NK ED I N

Share

thelaterbeingresponsiblefordelimitingthescopeofthe
continuation.Beforeweproceed,letsgivesomeusage
examples,whichIllthentrytoexplain.
Example1:

SUBSCRIBE TO

Posts
Comments

reset{
shift{k:(Int=>Int)=>
k(7)
}+1
}
//result:8

FO LLO W ER S

Jointhissite
withGoogleFriendConnect

Members(90) More
Example2:
deffoo()={
println("Oncehere.")
shift((k:Int=>Int)=>k(k(k(7))))
}
defbar()={
1+foo()
}
defbaz()={
reset(bar()*2)
}
baz()//result70

Example3:
reset{
println(1)
}
//prints:1

reset{
println(1)
shift{(cont:Unit=>Unit)=>}
println(2)
}
//prints:1

reset{
println(1)
shift{(cont:Unit=>Unit)=>
println(2)
}
println(3)
}
//prints:12

reset{
println(1)
shift{(cont:Unit=>Unit)=>

S EAR CH T H I S B LO G

Search

BLOG ARCHIVE

2014(2)
2013(2)
2012(7)
2011(14)
2010(5)
2009(34)
October(2)
September(5)
August(1)
July(5)

shift{(cont:Unit=>Unit)=>
cont()
println(2)
}
println(3)

}
//prints:132

reset{
println(1)
shift{(cont:Unit=>Unit)=>
cont()
cont()
println(2)
}
println(3)
}
//prints:1332

Software
Engineering
AnIdeaWhose
TimeHasGon...
GettingAround
TypeErasure
withManifests
Delimited
Continuations
Explained(in
Scala)
Matrices6
Matrices5b

Sowehavetwoexamplesofcontinuationsasexpressions,and

June(18)

oneaspurelyflowcontrol.Thefirstexampleseemspretty

May(1)

simple:thewholeshiftisreplacedwith7,and,therefore,
resetreturns7+1.Thethirdexampleshowsthat,infact,the
codebeforeshiftisexecutedonce,thecodeaftershiftasmany

April(1)
March(1)

timesasthefunctioninsideshiftisrepeated,andthecode
insidetheshiftitselfisexecutedonce,goingbackandforthto
thecodeafterit.
Sowelookatthesecondexample,andseethatthecode
beforeshiftseemstogetexecutedmanytimes!Actually,the

LAB ELS

agile(2)
beginner(15)

methodreceivingtheresultofshiftisbeingexecutedmany

continuations(2)

times,onceforeachtimeshiftreturn,butthestatementright

equality(3)

beforeshiftonlygetsexecutedonce.

fibonacci(2)
language(14)

Theseexampleswereprobablyenoughtomakethingsmore

manifest(1)

confusing,right?Soletsunderstandwhatishappening.Scalas

matrix(7)

continuationsarebasedonaselectiveContinuationPassing
Style(CPS)transformation.TheWikipediapageonCPSis
actuallyprettygood,so,please,takeamomenttoperuseit,
andunderstandtheexamplesgivenatthebeginning,andthen
comebackhere.

operators(1)
programming(20)
puzzle(2)
regex(3)
scala(49)

Ready?Well,onethingthatimmediatelycametomindisthat
thislookslikeprogrammingbackwards!Theycallitinsideout
inthatpage,whichismoreappropriate,Isuppose,butonehas
topayattentiontofirstimpressions.:)
Whatishappeningthereisthattheinnermostfunctionsof
normalprogrammingstyleareoutmost,andviceversa.Youll
seethatthisiswhatishappeningwithshiftandreset.
Illnowexplainabitofwhatishappeninginacontinuation.
Notice,first,thatthecodeassociatedwiththeshiftsare

softwareengineering
(1)

functions:theyareallintheform{k=>}.So,thefirst
thingtokeepinmindisthatshiftsarefunctionstakinga
parameter.
Now,payattention,again,totheveryfirstexample.Itdefines
thetypeofkasbeingInt=>Int.Inthefirstexample,then,
shiftisafunctionwhichtakesasparameteranotherfunction.
Thisisactuallytrueofallshifts,evenifthefunctionbeing
passedtoshifttakesnoparametersandreturnsUnit.
Soashiftisafunctionwhichreceivesanotherfunctionas
parameter.Asthebodyoftheshiftgetsexecuted,thatfunction
parametermaygetcalledornot.Fromthesecondexample,itis
prettyobviousthatthecodeaftertheshiftonlygetsexecutedif
thatfunctionparameteriscalled.
Bynowyouprobablyhaveanuneasyintuitionthatthat
parameteris,infact,thecodeaftertheshift.Thatsessentially
right,butletsfurtherintothis.
Theresultofresetistheresultofthecodeinsideshift.So,in
thefirstexample,8,theresultofreset,isalsotheresultof
theshiftcode,k:(Int=>Int)=>k(7).Itshouldbeobvious,
then,thatthevaluepassedtokisx:Int=>x+1,whichis
afunctionofInt=>Int,asrequired.Now,takealookatthe
twolinesbelow:
shift{k:(Int=>Int)=>k(7)}+1
x+1

So,thefunctionbeingpassedasparameterisformedbytaking
thewholecodefromtheshifton,andreplacingshiftwithx.In
effect,whenk(7)isexecuted,thisislikereplacingthewhole
shiftwith7,andthencontinuinguntiltheendofreset.Atthat
point,though,theexecutionresumesinsidetheshift.Inthis
examplethereisnothingmoreinthere,sotheresultis8.
Whatisactuallyhappeningisthis:
((k:Int=>Int)=>k(7))((x:Int)=>x+1)

Assaidbefore,wearepassingafunctiontoanother.Youcan
pastethatcodeinsideREPLanditwillworkasexpected.You
mayalsotrythiscodehere:
((k:Int=>Int)=>k(k(k(7))))((x:Int)=>(1+x)*2)

Areyougettingafeelingnowforit?So,wheneveryousee
reset/shift,firstthinkofthewholeresetexpression,replacing

theshiftinsideitwithanunknownvariablewellkeepcallingit
xfornow:
Reset{beforeshift{k=>...k(something)...}after}
Reset{beforexafter}

NowreplaceResetwithafunctiondeclarationofoneparameter:
Reset{beforexafter}
x=>{beforexafter}

Whattypeisxsupposedtobe?Youcanfinditinsidetheshift,
bylookingintowhatisbeingpassedtokthesomethingin
theexample.Wecannowrewritethewholeresetstatement,
placingfirsttheshiftcode,thentheresetcodeasparameter,
likewedidbefore:
(k=>...k(something)...){x=>{beforexafter}}

Andthisisprettymuchwhathappenswhenyouuse
continuations.
Asforthesecondexample,thoughthe1+lookstobetothe
leftofshift,itactuallygetsexecutedaftershiftreturns,soit
ispartofthefunctionbeingpassedtoshift.Youfindthesame
situationwhenmakingtailrecursivefunctions.Ifyoutried1+
recurse(),itwouldnotbetailrecursive,because+would
onlybeexecutedafterthecalltorecursereturned.
Prettyeasy,right?Letstrysomethingwithtwoshifts:
typeMonadic[+U,C[_]]={
defflatMap[V](f:U=>C[V]):C[V]
}

classReflective[+A,C[_]](xs:Monadic[A,C]){
defreflect[B]():A@cps[C[B],C[B]]={
shift{k:(A=>C[B])=>
xs.flatMap(k)
}
}
}

implicitdefreflective[A](xs:Iterable[A])=
newReflective[A,Iterable](xs)

reset{
valleft=List("x","y","z")
valright=List(4,5,6)

List((left.reflect[Any],right.reflect[Any]))
}
//result:List[(String,Int)]=List((x,4),(x,5),(x,6),
(y,4),(y,5),(y,6),(z,4),(z,5),(z,6)

Donthateme,itisingoodcause.:)Ifyoucanunderstand
thiswell,youcanunderstandthis.ButImsureyoullstartto
reallygetagripinit.
First,letsdiscardtheirrelevantpartsofthecodeabove.
Everythingbutthereflectdefinitionandtheresetcodeis
irrelevant.Theyaretherejusttomakereflectavailabletoa
List,itsplainoldEnrichMyLibrarypattern,thoughtheuseofa
structuraltypeiscool.:)
Now,letsreplaceleft.reflectandright.reflectwiththeactual
codegettingexecutedIllchangekintok1andk2soasnotto
confuseboth,soweareleftonlywiththeresetcode:
reset{
valleft=List("x","y","z")
valright=List(4,5,6)

List((shift{k1:(String=>List[Any])=>left.flatMap(k1)},
shift{k2:(Int=>List[Any])=>right.flatMap(k2)}))
}

Sincetherearetwoshifts,shouldwehavetwounknownsbeing
passedtoshift?Ifyoulookatthetypesignaturesfork1andk2,
theanswerisno.Eachshiftgetsasingleparameter.
YoumaythinkthattheAnytypemeansonelistisformedbya
singleelement,andtheotherbyatuple,butthatsnottrue.You
canchangebothtypestoList[(String,Int)],anditwouldstill
work.No,thetypesignaturesareright,thetrickisinhowthey
arecomposedtogether.
Ifyouthinkitthroughsimplyintermsofflowcontrol,thefirst
shiftwillreturnavalue(x),andthenthesecondshiftwill
returnavalue(4),producingalist.Theflowthenreturnstothe
lastshift,whichwillreturnanothervalue(5),andthenanother
still(6).Atthispoint,theflowreturnsonceagaintothefirst
shift,whichwillproduceitsnextvalue(y).Sothetrickisin
howwecomposethefunctions.
Letssee,first,bothshifts:
((k1:String=>List[(String,Int)])=>left.flatMap(k1))
((k2:Int=>List[(String,Int)])=>right.flatMap(k2))

Thesecantandwontbechanged.Thetrickishowwecompose
them:
((k1:String=>List[(String,Int)])=>left.flatMap(k1))
((x:String)=>((k2:Int=>List[(String,Int)])=>
right.flatMap(k2))
((y:Int)=>List((x,y)))
)

SowhathappensisthatthefunctionwhichtakesaStringand
returnsaListof(String,Int)thatgetspassedtothefirstshiftis
composedofboththeresetfunctionandthesecondshift
function.
Thefunctionpassedtothesecondshiftdoesnotneedan
additionalStringparameterbecausethestringisalreadybound.
Nowyouhaveseenboththecontrolflowofreset/shift,andhow
totranslatethemintocommonfunctionstounderstandwhat
valuestheywillproduce.Ihopeitwillbeofhelpwhenthetime
comesinwhichyouneedtounderstandthesestructures.
Note:manythingshavechangedsinceIwrotethisblog.
Continuationshavegonethroughminorchangesinthe
annotations,andcollectionshavegonethroughbigchanges.I
haven'tcheckedoutallexamplesyet,butIknowtheonewith
thetwolistsdoesn'tworkanymore.Thefollowingcodewillwork
forthatexample,thoughIwishIcouldmakeitmoregeneral
thanitpresentlyis:

typeMonadic[+U,C[_]]={
defflatMap[V,That](f:U=>C[V])(implicitcbf:
collection.generic.CanBuildFrom[C[U],V,That]):That
}

classReflective[+A](xs:Monadic[A,Traversable]){
defreflect[B]():A@cps[Traversable[B]]={
shift{k:(A=>Traversable[B])=>
xs.flatMap(k)(collection.breakOut):Traversable[B]
}
}
}

implicitdefreflective[A](xs:Traversable[A])=new
Reflective[A](xs)

reset{
valleft=List("x","y","z")
valright=List(4,5,6)

List((left.reflect[Any],right.reflect[Any]))
}
SH ARE |
P O S T ED B Y DA NI EL S O B R A L A T 2: 04 A M
LA B ELS : CO NT I NU A T I O NS , S CA LA

24COMMENTS:
Andrei July4,2009at5:49AM

Thewikilinkisbroken.
Reply

eivindw July4,2009at6:18AM
Thanksforagreatarticle.Theconceptofcontinuationsis
finallybeginningtomakesenseforme.
For what areas/problems do you see yourself using
continuations?
Reply

RicardoLima July4,2009at9:33AM
Well, from What I understood, shift and reset will be
implemented as a library + compiler plugin. I'm still
strugglingwiththe"ListMonadascontinuation"example
Reply

Daniel July4,2009at10:47AM
Allmylinksgotbroken.Blogger'sdoing,notmine.
Reply

Anonymous April23,2010at1:25PM
Except for the "different" new syntax, how is this any
betterfromdefiningfunctionsthattakecallbackfunctions
asaparameter?
Reply

Daniel April23,2010at4:03PM
Itisreallynotanydifferentfromthat,inthesamesense
thatJavaisnotreallyanydifferentthanmachinecode.
Passingcallbacks:
((k1:String=>List[(String,Int)])=>left.flatMap(k1))
((x: String) => ((k2: Int => List[(String,Int)]) =>
right.flatMap(k2))
((y:Int)=>List((x,y)))
)
Usingdelimitedcontinations:
List((left.reflect[Any],right.reflect[Any]))
Reply

EricKolotyluk June14,2010at5:18PM
This is by far the best explanation of continuations in
ScalaIhaveseenyet.Imostlyunderstandwhatisgoing
onnow,butcontinuationsaspartofanexpressionisstill
a little mind warping, and seems to have no utility I can

thinkof.
Itwouldbeinterestingtoseeifcontinuationscouldmake
UserInterfaceimplementationalittleeasierbyreplacing
SwingUtilities.invokeLater() and/or SwingWorker with
somethingmorelightweightormorereadable.
Reply

Anonymous September12,2010at5:30PM
Wait.
That'sWRONG.
A"Continuation",asIunderstandit,workslikethis:
reset{
valy=shift{(k:Int=>Unit)
valx=2+3
println("Insidecall/cc")
k(x)
println("Thisnevergetsprinted.")
}
println("2+3="+y)
}
Shouldoutput
Insidecall/cc
2+3=5
Not
Insidecall/cc
2+3=5
Thisnevergetsprinted.
Reply

Daniel September12,2010at6:09PM
Well, it outputs what you think it shouldn't. The question
is, why do you think it is wrong? Perhaps you read all
about general continuations and not enough of delimited
continuations? Or was it something in my text that lead
youtoexpectotherwise?
Reply

Anonymous September16,2010at8:29PM
Oh.Nevermind.
How did I do so much research on continuations, call/cc,
and continuation passing style in order to wrap my head
aroundit,andmissthis?
Sorry.
Reply

Reply

PaoloG.Giarrusso September17,2010at11:10PM
"ProgrammingLanguages:ApplicationandInterpretation"
has some beautiful examples on general continuations,
using them for a microwebframework. For that
purpose, continuations are already used in practice in
various cases. A success story about this was written by
PaulGraham:
http://www.paulgraham.com/avg.html
http://lib.store.yahoo.net/lib/paulgraham/bbnexcerpts.txt
Iwouldreallylike,however,tounderstandhowtodothe
same with delimited continuations. It seems to me (with
my current limited understanding) that one needs to use
reifytosavethecontinuation,whichlooksevenscarier!
Reply

Anonymous September22,2010at7:15AM
Is it possible to explain simply what is the motivationon
thisbraintwistingnonsense?
Reply

Anonymous September22,2010at7:25AM
Please could you explain what is the motivation of
continuations?
Reply

EricKolotyluk September22,2010at3:59PM
I also agree that it is quite mind twisting, and I have
been unable to get any of the examples to work in
EclipsewiththelatestScalaplugin.
From what I understand, continuations are another form
of control like if, while, try, etc. The motivation is to
make it easier to express some forms of control in
distributed or parallel applications. Unfortunately I don't
understandwellenoughtogiveaclearexample.
Reply

Martin September25,2010at4:17PM
An interesting thing you could do with continuations is:
stop a process send it over the the network to an other
computer and then continue at the point where you left
of. There is a Framework that implements this behavior
ontopofscalausingcontinuations.
http://code.google.com/p/swarmdpl/
Reply

Reply

Anonymous October25,2010at9:59AM
theexample:
reset{
println(1)
shift{(cont:Unit=>Unit)=>
cont()
cont()
println(2)
}
println(3)
}
would not work as expected using your transformation
"rule":
Reset{beforeshift{k=>...k(something)...}after}
(k=>...k(something)...){x=>{beforexafter}}
ithinktheexampleshouldbe"translated"to:
println(1)((c:Unit=>Unit)=>{c()c()println(2)})
((x:Unit)=>println(3))
?
Reply

Daniel October25,2010at12:23PM
You are correct. "Before" should be moved outside the
function call to be strictly correct. I'll consider how to
rewritethatexplanation.
Reply

monisa March25,2011at9:50AM
I am planning to use continuation support in the scala
language through shift and reset to track the users who
are logged in(We are trying to use this instead of using
sessionorcookietotracktheusersloggedin).Soplease
provide us any sample programe to implement the
continuation support through shift and reset to track the
loggedusersinscala.
Reply

SethTisue June3,2011at11:27PM
"Deprecating the Observer Pattern (Maier, Rompf, and
Oderksy,
http://lamp.epfl.ch/~imaier/pub/DeprecatingObserversTR
2010.pdf) is a good article for seeing how continuations
canbemorethanjustawaytotieyourbraininknots.It

shows how you can use continuations to write event


handling code without having to break it up into a bunch
ofseparateeventhandlers.
Reply

EricKolotyluk June6,2011at11:34AM
Iagreethat"DeprecatingtheObserverPattern"isagood
article,butthatarticlealsotiesmybraininknots.
I would really like to see a non trivial example of how
continuationscanworkintheUIthatIcanfollow.
Reply

Anonymous July20,2011at9:25PM
@Eric Kolotyluk: Continuations weren't necessarily
intendedforuseattheUIlayer..
Reply

HendyIrawan January16,2012at6:32AM
Greatexplanationoncontinuations.
Thanks!
@Seth:Thanks...thatpaperreallyhelps!
Reply

Anonymous January21,2013at10:13PM
Greatarticle!Ididitalmostinstantly
defa(x:Int)=shift{k:(Int=>Int)=>k(x)}
deffib(n:Int):Int@cps[Int]=nmatch{
casexif(x>2)=>fib(x2)+fib(x1)
casex=>a(x)
}
reset{
fib(5)
}
Now,continuereading...^)
Reply
Replies
Anonymous January21,2013at10:37PM
A previous post is not quite correct, but trying
this:
deffib(n:Int):Int@cps[Int]={

def fib0(n: Int, sub1: Int, sub2: Int): Int


@cps[Int]=nmatch{
case x if (math.abs(x) < 2) => shift { k:
(Int=>Int)=>k(x)}
casex=>fib0(xsub1,sub1,sub2)+fib0(x
sub2,sub1,sub2)
}
vals=math.signum(n)
fib0(ns,s,s*2)
}
throwsjava.lang.StackOverflowError:
reset{fib(15)}
Isitpossibletohandlethis,usingcontinuations?
Reply

Enteryourcomment...

Commentas:

Publish

GoogleAccount

Preview

LINKSTOTHISP OST
CreateaLink
NewerPost

Home

Subscribeto:PostComments(Atom)

OlderPost