123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807 |
- This is scheme48.info, produced by makeinfo version 6.7 from
- scheme48.texi.
- This manual is for Scheme48 version 1.3.
- Copyright (C) 2004, 2005, 2006 Taylor Campbell. All rights reserved.
- This manual includes material derived from works bearing the
- following notice:
- Copyright (C) 1993-2005 Richard Kelsey, Jonathan Rees, and Mike
- Sperber. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
- * The name of the authors may not be used to endorse or promote
- products derived from this software without specific prior
- written permission.
- THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- INFO-DIR-SECTION The Algorithmic Language Scheme
- START-INFO-DIR-ENTRY
- * Scheme48: (scheme48). Nearly complete reference manual for version 1.3
- END-INFO-DIR-ENTRY
- File: scheme48.info, Node: Top, Next: Introduction & acknowledgements, Up: (dir)
- The Nearly Complete Scheme48 Reference Manual
- *********************************************
- This manual is for Scheme48 version 1.3.
- Copyright (C) 2004, 2005, 2006 Taylor Campbell. All rights reserved.
- This manual includes material derived from works bearing the
- following notice:
- Copyright (C) 1993-2005 Richard Kelsey, Jonathan Rees, and Mike
- Sperber. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
- * The name of the authors may not be used to endorse or promote
- products derived from this software without specific prior
- written permission.
- THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- * Menu:
- * Introduction & acknowledgements::
- * User environment::
- * Module system::
- * System facilities::
- * Multithreading::
- * Libraries::
- * C interface::
- * POSIX interface::
- * Pre-Scheme:: A low-level dialect of Scheme
- * References::
- * Concept index::
- * Binding index::
- * Structure index::
- -- The Detailed Node Listing --
- * Introduction & acknowledgements::
- User environment
- * Running Scheme48::
- * Emacs integration::
- * Using the module system::
- * Command processor::
- Module system
- * Module system architecture::
- * Module configuration language:: Language of the module system
- * Macros in concert with modules::
- * Static type system::
- System facilities
- * System features::
- * Condition system::
- * Bitwise manipulation::
- * Generic dispatch system::
- * I/O system::
- * Reader & writer::
- * Records::
- * Suspending and resuming heap images::
- Multithreading
- * Basic thread operations::
- * Optimistic concurrency::
- * Higher-level synchronization::
- * Concurrent ML:: High-level event synchronization
- * Pessimistic concurrency:: Mutual exclusion/locking
- * Custom thread synchronization::
- Libraries
- * Boxed bitwise-integer masks::
- * Enumerated/finite types and sets::
- * Macros for writing loops::
- * Library data structures::
- * I/O extensions::
- * TCP & UDP sockets::
- * Common-Lisp-style formatting::
- * Library utilities::
- C interface
- * Shared bindings between Scheme and C::
- * Calling C functions from Scheme::
- * Dynamic loading of C modules::
- * Accessing Scheme data from C::
- * Calling Scheme procedures from C::
- * Interacting with the Scheme heap in C::
- * Using Scheme records in C::
- * Raising exceptions from C::
- * Unsafe C macros::
- POSIX interface
- * POSIX processes::
- * POSIX signals::
- * POSIX process environment::
- * POSIX users and groups::
- * POSIX host OS and machine identification::
- * POSIX file system access::
- * POSIX time::
- * POSIX I/O utilities::
- * POSIX regular expressions::
- * POSIX C to Scheme correspondence::
- Pre-Scheme
- * Differences between Pre-Scheme & Scheme::
- * Pre-Scheme type specifiers::
- * Standard Pre-Scheme environment::
- * More Pre-Scheme packages::
- * Invoking the Pre-Scheme compiler::
- * Example Pre-Scheme compiler usage::
- * Running Pre-Scheme as Scheme::
- File: scheme48.info, Node: Introduction & acknowledgements, Next: User environment, Prev: Top, Up: Top
- 1 Introduction
- **************
- Scheme48 is an implementation of Scheme based on a byte-code virtual
- machine with design goals of simplicity and cleanliness. To briefly
- enumerate some interesting aspects of it, Scheme48 features:
- * an advanced module system based on Jonathan Rees's W7 security
- kernel with well-integrated interaction between macros and modules;
- * a virtual machine written in a dialect of Scheme itself,
- Pre-Scheme, for which a compiler is written with Scheme48;
- * a sophisticated, user-level, preëmptive multithreading system with
- numerous high-level concurrency abstractions;
- * a composable, lock-free shared-memory thread synchronization
- mechanism known as "optimistic concurrency"; and
- * an advanced user environment that is well-integrated with the
- module and thread systems to facilitate very rapid development of
- software systems scaling from small to large and single-threaded to
- multi-threaded.
- It was originally written by Jonathan Rees and Richard Kelsey in 1986
- in response to the fact that so many Lisp implementations had started
- out simple and grown to be complex monsters of projects. It has been
- used in a number of research areas, including:
- * mobile robots at Cornell [Donald 92];
- * a multi-user collaboration system, sometimes known as a 'MUD'
- ('multi-user dungeon') or 'MUSE' ('multi-user simulation
- environment'), as well as general research in capability-based
- security [Museme; Rees 96]; and
- * advanced distributed computing with higher-order mobile agents at
- NEC's Princeton research lab [Cejtin et al. 95].
- The system is tied together in a modular fashion by a configuration
- language that permits quite easy mixing and matching of components, so
- much so that Scheme48 can be used essentially as its own OS, as it was
- in Cornell's mobile robots program, or just as easily within another, as
- the standard distribution is. The standard distribution is quite
- portable and needs only a 32-bit byte-addressed POSIX system.
- The name 'Scheme48' commemorates the time it took Jonathan Rees and
- Richard Kelsey to originally write Scheme48 on August 6th & 7th, 1986:
- forty-eight hours. (It has been joked that the system has expanded to
- such a size now that it requires forty-eight hours to _read_ the
- source.)
- 1.1 This manual
- ===============
- This manual begins in the form of an introduction to the usage of
- Scheme48, suitable for those new to the system, after which it is
- primarily a reference material, organized by subject. Included in the
- manual is also a complete reference manual for Pre-Scheme, a low-level
- dialect of Scheme for systems programming and in which the Scheme48
- virtual machine is written; *note Pre-Scheme::.
- This manual is, except for some sections pilfered and noted as such
- from the official but incomplete Scheme48 reference manual, solely the
- work of Taylor Campbell, on whom all responsibility for the content of
- the manual lies. The authors of Scheme48 do not endorse this manual.
- 1.2 Acknowledgements
- ====================
- Thanks to Jonathan Rees and Richard Kelsey for having decided so many
- years ago to make a simple Scheme implementation with a clean design in
- the first place, and for having worked on it so hard for so many years
- (almost twenty!); to Martin Gasbichler and Mike Sperber, for having
- picked up Scheme48 in the past couple years when Richard and Jonathan
- were unable to work actively on it; to Jeremy Fincher for having asked
- numerous questions about Scheme48 as he gathered knowledge from which he
- intended to build an implementation of his own Lisp dialect, thereby
- inducing me to decide to write the manual in the first place; to Jorgen
- Schäfer, for having also asked so many questions, proofread various
- drafts, and made innumerable suggestions to the manual.
- File: scheme48.info, Node: User environment, Next: Module system, Prev: Introduction & acknowledgements, Up: Top
- 2 User environment
- ******************
- * Menu:
- * Running Scheme48::
- * Emacs integration::
- * Using the module system::
- * Command processor::
- File: scheme48.info, Node: Running Scheme48, Next: Emacs integration, Up: User environment
- 2.1 Running Scheme48
- ====================
- Scheme48 is run by invoking its virtual machine on a dumped heap image
- to resume a saved system state. The common case of invoking the default
- image, 'scheme48.image', which contains the usual command processor,
- run-time system, &c., is what the 'scheme48' script that is installed
- does. The actual virtual machine executable itself, 'scheme48vm', is
- typically not installed into an executable directory such as
- '/usr/local/bin/' on Unix, but in the Scheme48 library directory, which
- is, by default on Unix installations of Scheme48, '/usr/local/lib/'.
- However, both 'scheme48' and 'scheme48vm' share the following
- command-line options; the only difference is that 'scheme48' has a
- default '-i' argument.
- '-h HEAP-SIZE'
- The size of Scheme48's heap, in cells. By default, the heap size
- is 3 megacells, or 12 megabytes, permitting 6 megabytes per
- semispace -- Scheme48 uses a simple stop & copy garbage
- collector.(1) Since the current garbage collector cannot resize
- the heap dynamically if it becomes consistently too full, users on
- machines with much RAM may be more comfortable with liberally
- increasing this option.
- '-s STACK-SIZE'
- The stack size, in cells. The default stack size is 10000 bytes,
- or 2500 cells. Note that this is only the size of the stack cache
- segment of memory for fast stack frame storage. When this
- overflows, there is no error; instead, Scheme48 simply copies the
- contents of the stack cache into the heap, until the frames it
- copied into the heap are needed later, at which point they are
- copied back into the stack cache. The '-s' option therefore
- affects only performance, not the probability of fatal stack
- overflow errors.
- '-i IMAGE-FILENAME'
- The filename of the suspended heap image to resume. When running
- the 'scheme48' executable, the default is the regular Scheme48
- image; when running the virtual machine directly, this option must
- be passed explicitly. For information on creating custom heap
- images, *note Image-building commands::, and also *note Suspending
- and resuming heap images::.
- '-a ARGUMENT ...'
- Command-line arguments to pass to the heap image's resumer, rather
- than being parsed by the virtual machine. In the usual Scheme48
- command processor image, these arguments are put in a list of
- strings that will be the initial focus value (*note Focus value::).
- '-u'
- Muffles warnings on startup about undefined imported foreign
- bindings.
- The usual Scheme48 image may accept an argument of 'batch', using the
- '-a' switch to the virtual machine. This enters Scheme48 in batch mode,
- which displays no welcoming banner, prints no prompt for inputs, and
- exits when an EOF is read. This may be used to run scripts from the
- command-line, often in the exec language (*note Command programs::), by
- sending text to Scheme48 through Unix pipes or shell heredocs. For
- example, this Unix shell command will load the command program in the
- file 'foo.scm' into the exec language environment and exit Scheme48 when
- the program returns:
- echo ,exec ,load foo.scm | scheme48 -a batch
- This Unix shell command will load 'packages.scm' into the module
- language environment, open the 'tests' structure into the user
- environment, and call the procedure 'run-tests' with zero arguments:
- scheme48 -a batch <<END
- ,config ,load packages.scm
- ,open tests
- (run-tests)
- END
- Scheme48 also supports [SRFI 22] and [SRFI 7] by providing R5RS and
- [SRFI 7] script interpreters in the location where Scheme48 binaries are
- kept as 'scheme-r5rs' and 'scheme-srfi-7'. See the [SRFI 22] and [SRFI
- 7] documents for more details. Scheme48's command processor also has
- commands for loading [SRFI 7] programs, with or without a [SRFI 22]
- script header; *note SRFI 7::.
- 2.1.1 Command processor introduction
- ------------------------------------
- The Scheme48 command processor is started up on resumption of the usual
- Scheme48 image. This is by default what the 'scheme48' script installed
- by Scheme48 does. It will first print out a banner that contains some
- general information about the system, which will typically look
- something like this:
- Welcome to Scheme 48 1.3 (made by root on Sun Jul 10 10:57:03 EDT 2005)
- Copyright (c) 1993-2005 by Richard Kelsey and Jonathan Rees.
- Please report bugs to scheme-48-bugs@s48.org.
- Get more information at http://www.s48.org/.
- Type ,? (comma question-mark) for help.
- After the banner, it will initiate a REPL (read-eval-print loop). At
- first, there should be a simple '>' prompt. The command processor
- interprets Scheme code as well as "commands". Commands operate the
- system at a level above or outside Scheme. They begin with a comma, and
- they continue until the end of the line, unless they expect a Scheme
- expression argument, which may continue as many lines as desired. Here
- is an example of a command invocation:
- > ,set load-noisily on
- This will set the 'load-noisily' switch (*note Command processor
- switches::) on.
- *Note:* If a command accepts a Scheme expression argument that is
- followed by more arguments, all of the arguments after the Scheme
- expression must be put on the same line as the last line of the Scheme
- expression.
- Certain operations, such as breakpoints and errors, result in a
- recursive command processor to be invoked. This is known as "pushing a
- command level". *Note Command levels::. Also, the command processor
- supports an "object inspector", an interactive program for inspecting
- the components of objects, including continuation or stack frame
- objects; the debugger is little more than the inspector, working on
- continuations. *Note Inspector::.
- Evaluation of code takes place in the "interaction environment".
- (This is what R5RS's 'interaction-environment' returns.) Initially,
- this is the "user environment", which by default is a normal R5RS Scheme
- environment. There are commands that set the interaction environment
- and evaluate code in other environments, too; *note Module commands::.
- The command processor's prompt has a variety of forms. As above, it
- starts out with as a simple '>'. Several factors can affect the prompt.
- The complete form of the prompt is as follows:
- * It begins with an optional command level (*note Command levels::)
- number: at the top level, there is no command level number; as
- command levels are pushed, the number is incremented, starting at
- 1.
- * Optionally, the name of the interaction environment follows the
- command level number: if the interaction environment is the user
- environment, there is no name printed here; named environments are
- printed with their names; unnamed environments (usually created
- using the ',new-package' command; *note Module commands::) are
- printed with their numeric identifiers. If a command level number
- preceded an environment name, a space is printed between them.
- * If the command processor is in the regular REPL mode, it ends with
- a '>' and a space before the user input area; if it is in inspector
- mode (*note Inspector::), it ends with a ':' and a space before the
- user input area.
- For example, this prompt denotes that the user is in inspector mode
- at command level 3 and that the interaction environment is an
- environment named 'frobozz':
- 3 frobozz:
- This prompt shows that the user is in the regular REPL mode at the
- top level, but in the environment for module descriptions (*note Module
- commands::):
- config>
- For a complete listing of all the commands in the command processor,
- *note Command processor::.
- ---------- Footnotes ----------
- (1) The Scheme48 team is also working on a new, generational garbage
- collector, but it is not in the standard distribution of Scheme48 yet.
- File: scheme48.info, Node: Emacs integration, Next: Using the module system, Prev: Running Scheme48, Up: User environment
- 2.2 Emacs integration
- =====================
- Emacs is the canonical development environment for Scheme48. The
- 'scheme.el' and 'cmuscheme.el' packages provide support for editing
- Scheme code and running inferior Scheme processes, respectively. Also,
- the 'scheme48.el' package provides more support for integrating directly
- with Scheme48.(1) 'scheme.el' and 'cmuscheme.el' come with GNU Emacs;
- 'scheme48.el' is available separately from
- <http://www.emacswiki.org/cgi-bin/wiki/download/scheme48.el>.
- To load 'scheme48.el' if it is in the directory EMACS-DIR, add these
- lines to your '.emacs':
- (add-to-list 'load-path "EMACS-DIR/")
- (autoload 'scheme48-mode "scheme48"
- "Major mode for improved Scheme48 integration."
- t)
- (add-hook 'hack-local-variables-hook
- (lambda ()
- (if (and (boundp 'scheme48-package)
- scheme48-package)
- (progn (scheme48-mode)
- (hack-local-variables-prop-line)))))
- The 'add-hook' call sets Emacs up so that any file with a
- 'scheme48-package' local variable specified in the file's '-*-' line or
- 'Local Variables' section will be entered in Scheme48 mode. Files
- should use the 'scheme48-package' variable to enable Scheme48 mode; they
- should not specify Scheme48 mode explicitly, since this would fail in
- Emacs instances without 'scheme48.el'. That is, put this at the tops of
- files:
- ;;; -*- Mode: Scheme; scheme48-package: ... -*-
- Avoid this at the tops of files:
- ;;; -*- Mode: Scheme48 -*-
- There is also SLIME48, the Superior Lisp Interaction Mode for Emacs
- with Scheme48. It provides a considerably higher level of integration
- the other Emacs packages do, although it is less mature. It is at
- <http://mumble.net/~campbell/scheme/slime48.tar.gz>;
- there is also a Darcs repository(2) at
- <http://mumble.net/~campbell/darcs/slime48/>.
- Finally, 'paredit.el' implements pseudo-structural editing facilities
- for S-expressions: it automatically balances parentheses and provides a
- number of high-level operations on S-expressions. 'Paredit.el' is
- available on the web at
- <http://mumble.net/~campbell/emacs/paredit.el>.
- 'cmuscheme.el' defines these:
- -- Emacs command: run-scheme [scheme-prog]
- Starts an inferior Scheme process or switches to a running one.
- With no argument, this uses the value of 'scheme-program-name' to
- run the inferior Scheme system; with a prefix argument SCHEME-PROG,
- this invokes SCHEME-PROG.
- -- Emacs variable: scheme-program-name
- The Scheme program to invoke for inferior Scheme processes.
- Under 'scheme48-mode' with 'scheme.el', 'cmuscheme.el', and
- 'scheme48.el', these keys are defined:
- 'C-M-f' -- 'forward-sexp'
- 'C-M-b' -- 'backward-sexp'
- 'C-M-k' -- 'kill-sexp'
- '<ESC> C-<DEL>' (_not_ 'C-M-<DEL>') -- 'backward-kill-sexp'
- 'C-M-q' -- 'indent-sexp'
- 'C-M-@' -- 'mark-sexp'
- 'C-M-<SPC>' -- 'mark-sexp'
- S-expression manipulation commands. 'C-M-f' moves forward by one
- S-expression; 'C-M-b' moves backward by one. 'C-M-k' kills the
- S-expression following the point; '<ESC> C-<DEL>' kills the
- S-expression preceding the point. 'C-M-q' indents the S-expression
- following the point. 'C-M-@' & 'C-M-<SPC>', equivalent to one
- another, mark the S-expression following the point.
- 'C-c z' -- 'switch-to-scheme'
- Switches to the inferior Scheme process buffer.
- 'C-c C-l' -- 'scheme48-load-file'
- Loads the file corresponding with the current buffer into Scheme48.
- If that file was not previously loaded into Scheme48 with 'C-c
- C-l', Scheme48 records the current interaction environment in place
- as it loads the file; if the file was previously recorded, it is
- loaded into the recorded environment. *Note Emacs integration
- commands::.
- 'C-c C-r' -- 'scheme48-send-region'
- 'C-c M-r' -- 'scheme48-send-region-and-go'
- 'C-c C-r' sends the currently selected region to the current
- inferior Scheme process. The file of the current buffer is
- recorded as in the 'C-c C-l' command, and code is evaluated in the
- recorded package. 'C-c M-r' does similarly, but subsequently also
- switches to the inferior Scheme process buffer.
- 'C-M-x' -- 'scheme48-send-definition'
- 'C-c C-e' -- 'scheme48-send-definition'
- 'C-c M-e' -- 'scheme48-send-definition-and-go'
- 'C-M-x' (GNU convention) and 'C-c C-e' send the top-level
- definition that the current point is within to the current inferior
- Scheme process. 'C-c M-e' does similarly, but subsequently also
- switches to the inferior Scheme process buffer. 'C-c C-e' and 'C-c
- M-e' also respect Scheme48's file/environment mapping.
- 'C-x C-e' -- 'scheme48-send-last-sexp'
- Sends the S-expression preceding the point to the inferior Scheme
- process. This also respects Scheme48's file/environment mapping.
- ---------- Footnotes ----------
- (1) 'scheme48.el' is based on the older 'cmuscheme48.el', which is
- bundled with Scheme48 in the 'emacs/' directory. Since 'cmuscheme48.el'
- is older and less developed, it is not documented here.
- (2) Darcs is a revision control system; see
- <http://www.darcs.net/>
- for more details.
- File: scheme48.info, Node: Using the module system, Next: Command processor, Prev: Emacs integration, Up: User environment
- 2.3 Using the module system
- ===========================
- Scheme48 is deeply integrated with an advanced module system. For
- complete detail of its module system, *note Module system::. Briefly,
- however:
- * "Packages" are top-level environments suitable for evaluating
- expressions and definitions, either interactively, from files
- loaded on-the-fly, or as the bodies of modules. They can also
- access bindings exported by structures by "opening" the structures.
- * "Structures" are libraries, or implementations of interfaces,
- exporting sets of bindings that packages can access. Underlying
- structures are usually packages, in which the user can, in some
- cases, interactively evaluate code during development.
- Scheme48's usual development system, the command processor, provides
- a number of commands for working with the module system. For complete
- details, *note Module commands::. Chief among these commands are
- ',open' and ',in'. ',open STRUCT ...' makes all of the bindings from
- each of STRUCT ... available in the interaction environment. Many of
- the sections in this manual describe one or more structures with the
- name they are given. For example, in order to use, or open, the
- multi-dimensional array library in the current interaction environment,
- one would enter
- ,open arrays
- to the command processor. ',in STRUCT' sets the interaction environment
- to be the package underlying STRUCT. For instance, if, during
- development, the user decides that the package of the existing structure
- 'foo' should open the structure 'bar', he might type
- ,in foo
- ,open bar
- The initial interaction environment is known as the "user package";
- the interaction environment may be reverted to the user package with the
- ',user' command.
- Module descriptions, or code in the module configuration language
- (*note Module configuration language::) should be loaded into the
- special environment for that language with the ',config' command (*note
- Module commands::). E.g., if 'packages.scm' contains a set of module
- descriptions that the user wishes to load, among which is the definition
- of a structure 'frobozz' which he wishes to open, he will typically send
- the following to the command processor prompt:
- ,config ,load packages.scm
- ,open frobozz
- *Note:* These are commands for the interactive command processor,
- _not_ special directives to store in files to work with the module
- system. The module language is disjoint from Scheme; for complete
- detail on it, *note Module system::.
- 2.3.1 Configuration mutation
- ----------------------------
- (This section was derived from work copyrighted (C) 1993-2005 by Richard
- Kelsey, Jonathan Rees, and Mike Sperber.)
- During program development, it is often desirable to make changes to
- packages and interfaces. In static languages, it is usually necessary
- to re-compile and re-link a program in order for such changes to be
- reflected in a running system. Even in interactive Common Lisp systems,
- a change to a package's exports often requires reloading clients that
- have already mentioned names whose bindings change. In those systems,
- once 'read' resolves a use of a name to a symbol, that resolution is
- fixed, so a change in the way that a name resolves to a symbol can be
- reflected only by re-'read'ing all such references.
- The Scheme48 development environment supports rapid turnaround in
- modular program development by allowing mutations to a program's
- configuration and giving a clear semantics to such mutation. The rule
- is that variable bindings in a running program are always resolved
- according to the current structure and interface bindings, even when
- these bindings change as a result of edits to the configuration. For
- example, consider the following:
- (define-interface foo-interface (export a c))
- (define-structure foo foo-interface
- (open scheme)
- (begin (define a 1)
- (define (b x) (+ a x))
- (define (c y) (* (b a) y))))
- (define-structure bar (export d)
- (open scheme foo)
- (begin (define (d w) (+ (b w) a))))
- This program has a bug. The variable named 'b', which is free in the
- definition of 'd', has no binding in 'bar''s package. Suppose that 'b'
- was intended to be exported by 'foo', but was mistakenly omitted. It is
- not necessary to re-process 'bar' or any of 'foo''s other clients at
- this point. One need only change 'foo-interface' and inform the
- development system of that change (using, say, an appropriate Emacs
- command), and 'foo''s binding of 'b' will be found when the procedure
- 'd' is called and its reference to 'b' actually evaluated.
- Similarly, it is possible to replace a structure; clients of the old
- structure will be modified so that they see bindings from the new one.
- Shadowing is also supported in the same way. Suppose that a client
- package C opens a structure 'mumble' that exports a name 'x', and
- 'mumble''s implementation obtains the binding of 'x' from some other
- structure 'frotz'. C will see the binding from 'frotz'. If one then
- alters 'mumble' so that it shadows 'bar''s binding of 'x' with a
- definition of its own, procedures in C that refer to 'x' will
- subsequently automatically see 'mumble''s definition instead of the one
- from 'frotz' that they saw earlier.
- This semantics might appear to require a large amount of computation
- on every variable reference: the specified behaviour appears to require
- scanning the package's list of opened structures and examining their
- interfaces -- on every variable reference evaluated, not just at
- compile-time. However, the development environment uses caching with
- cache invalidation to make variable references fast, and most of the
- code is invoked only when the virtual machine traps due to a reference
- to an undefined variable.
- 2.3.2 Listing interfaces
- ------------------------
- The 'list-interfaces' structure provides a utility for examining
- interfaces. It is usually opened into the config package with ',config
- ,open list-interfaces' in order to have access to the structures &
- interfaces easily.
- -- procedure: list-interface struct-or-interface --> unspecified
- Lists all of the bindings exported by STRUCT-OR-INTERFACE along
- with their static types (*note Static type system::). For example,
- > ,config ,open list-interfaces
- > ,config (list-interface condvars)
- condvar-has-value? (proc (:condvar) :value)
- condvar-value (proc (:condvar) :value)
- condvar? (proc (:value) :boolean)
- make-condvar (proc (&rest :value) :condvar)
- maybe-commit-and-set-condvar! (proc (:condvar :value) :boolean)
- maybe-commit-and-wait-for-condvar (proc (:condvar) :boolean)
- set-condvar-has-value?! (proc (:condvar :value) :unspecific)
- set-condvar-value! (proc (:condvar :value) :unspecific)
- File: scheme48.info, Node: Command processor, Prev: Using the module system, Up: User environment
- 2.4 Command processor
- =====================
- The Scheme48 command processor is the main development environment. It
- incorporates a read-eval-print loop as well as an interactive inspector
- and debugger. It is well-integrated with the module system for rapid
- dynamic development, which is made even more convenient with the Emacs
- interface, 'scheme48.el'; *note Emacs integration::.
- * Menu:
- * Basic commands::
- * Command processor switches::
- * Emacs integration commands::
- * Focus value::
- * Command levels::
- * Module commands::
- * SRFI 7::
- * Debugging commands::
- * Inspector::
- * Command programs::
- * Image-building commands::
- * Resource statistics and control::
- File: scheme48.info, Node: Basic commands, Next: Command processor switches, Up: Command processor
- 2.4.1 Basic commands
- --------------------
- There are several generally useful commands built-in, along with many
- others described in subsequent sections:
- -- command: ,help
- -- command: ,help command
- -- command: ,?
- -- command: ,? command
- Requests help on commands. ',?' is an alias for ',help'. Plain
- ',help' lists a synopsis of all commands available, as well as all
- switches (*note Command processor switches::). ',help COMMAND'
- requests help on the particular command COMMAND.
- -- command: ,exit
- -- command: ,exit status
- -- command: ,exit-when-done
- -- command: ,exit-when-done status
- Exits the command processor. ',exit' immediately exits with an
- exit status of 0. ',exit STATUS' exits with the status that
- evaluating the expression STATUS in the interaction environment
- produces. ',exit-when-done' is like ',exit', but it waits until
- all threads complete before exiting.
- -- command: ,go expression
- ',go' is like ',exit', except that it requires an argument, and it
- evaluates EXPRESSION in the interaction environment in a _tail
- context_ with respect to the command processor. This means that
- the command processor may no longer be reachable by the garbage
- collector, and may be collected as garbage during the evaluation of
- EXPRESSION. For example, the full Scheme48 command processor is
- bootstrapped from a minimal one that supports the ',go' command.
- The full command processor is initiated in an argument to the
- command, but the minimal one is no longer reachable, so it may be
- collected as garbage, leaving only the full one.
- -- command: ,run expression
- Evaluates EXPRESSION in the interaction environment. Alone, this
- command is not very useful, but it is required in situations such
- as the inspector (*note Inspector::) and command programs (*note
- Command programs::).
- -- command: ,undefine name
- Removes the binding for NAME in the interaction environment.
- -- command: ,load filename ...
- Loads the contents each FILENAME as Scheme source code into the
- interaction environment. Each FILENAME is translated first (*note
- Filenames::). The given filenames may be surrounded or not by
- double-quotes; however, if a filename contains spaces, it must be
- surrounded by double-quotes. The differences between the ',load'
- command and Scheme's 'load' procedure are that ',load' does not
- require its arguments to be quoted, allows arbitrarily many
- arguments while the 'load' procedure accepts only one filename (and
- an optional environment), and works even in environments in which
- 'load' is not bound.
- -- command: ,translate from to
- A convenience for registering a filename translation without
- needing to open the 'filenames' structure. For more details on
- filename translations, *note Filenames::; this command corresponds
- with the 'filename' structure's 'set-translation!' procedure. As
- with ',load', each of the filenames FROM and TO may be surrounded
- or not by double-quotes, unless there is a space in the filenames,
- in which case it must be surrounded by double-quotes.
- Note that in the exec language (*note Command programs::),
- 'translate' is the same as the 'filenames' structure's
- 'set-translation!' procedure, _not_ the procedure named 'translate'
- from the 'filenames' structure.
- File: scheme48.info, Node: Command processor switches, Next: Emacs integration commands, Prev: Basic commands, Up: Command processor
- 2.4.2 Switches
- --------------
- The Scheme48 command processor keeps track of a set of "switches",
- user-settable configurations.
- -- command: ,set switch
- -- command: ,set switch {on|off|?}
- -- command: ,unset switch
- -- command: ,set ?
- ',set SWITCH' & ',set SWITCH on' set the switch SWITCH on. ',unset
- SWITCH' & ',set SWITCH off' turn SWITCH off. ',set SWITCH ?' gives
- a brief description of SWITCH's current status. ',set ?' gives
- information about all the available switches and their current
- state.
- The following switches are defined. Each switch is listed with its
- name and its default status.
- 'ask-before-loading' _(off)_
- If this is on, Scheme48 will prompt the user before loading
- modules' code. If it is off, it will quietly just load it.
- 'batch' _(off)_
- Batch mode is intended for automated uses of the command processor.
- With batch mode on, errors cause the command processor to exit, and
- the prompt is not printed.
- 'break-on-warnings' _(off)_
- If the 'break-on-warnings' switch is on, warnings (*note Condition
- system::) signalled that reach the command processor's handler will
- cause a command level (*note Command levels::) to be pushed,
- similarly to breakpoints and errors.
- 'inline-values' _(off)_
- 'Inline-values' tells whether or not certain procedures may be
- integrated in-line.
- 'levels' _(on)_
- Errors will push a new command level (*note Command levels::) if
- this switch is on, or they will just reset back to the top level if
- 'levels' is off.
- 'load-noisily' _(off)_
- Loading source files will cause messages to be printed if
- 'load-noisily' is on; otherwise they will be suppressed.
- File: scheme48.info, Node: Emacs integration commands, Next: Focus value, Prev: Command processor switches, Up: Command processor
- 2.4.3 Emacs integration commands
- --------------------------------
- There are several commands that exist mostly for Emacs integration
- (*note Emacs integration::); although they may be used elsewhere, they
- are not very useful or convenient without 'scheme48.el'.
- -- command: ,from-file filename
- -- command: ,end
- ',from-file FILENAME' proclaims that the code following the
- command, until an ',end' command, comes from FILENAME -- for
- example, this may be due to an appropriate Emacs command, such as
- 'C-c C-l' in 'scheme48.el' --; if this is the first time the
- command processor has seen code from FILENAME, it is registered to
- correspond with the interaction environment wherein the
- ',from-file' command was used. If it is not the first time, the
- code is evaluated within the package that was registered for
- FILENAME.
- -- command: ,forget filename
- Clears the command processor's memory of the package to which
- FILENAME corresponds.
- File: scheme48.info, Node: Focus value, Next: Command levels, Prev: Emacs integration commands, Up: Command processor
- 2.4.4 Focus value
- -----------------
- The Scheme48 command processor maintains a current "focus value". This
- is typically the value that the last expression evaluated to, or a list
- of values if it returned multiple values. If it evaluated to either
- zero values or Scheme48's 'unspecific' token (*note System features::),
- the focus value is unchanged. At the initial startup of Scheme48, the
- focus value is set to the arguments passed to Scheme48's virtual machine
- after the '-a' argument on the command-line (*note Running Scheme48::).
- The focus value is accessed through the '##' syntax; the reader
- substitutes a special quotation (special so that the compiler will not
- generate warnings about a regular 'quote' expression containing a weird
- value) for occurrences of '##'. Several commands, such as ',inspect'
- and ',dis', either accept an argument or use the current focus value.
- Also, in the inspector (*note Inspector::), the focus object is the
- object that is currently being inspected.
- > (cons 1 2)
- '(1 . 2)
- > ##
- '(1 . 2)
- > (begin (display "Hello, world!") (newline))
- Hello, world!
- > ##
- '(1 . 2)
- > (cdr ##)
- 2
- > (define x 5)
- ; no values returned
- > (+ ## x)
- 7
- > (values 1 2 3)
- ; 3 values returned
- 1
- 2
- 3
- > ##
- '(1 2 3)
- File: scheme48.info, Node: Command levels, Next: Module commands, Prev: Focus value, Up: Command processor
- 2.4.5 Command levels
- --------------------
- The Scheme48 command processor maintains a stack of "command levels", or
- recursive invocations of the command processor. Each command level
- retains information about the point from the previous command level at
- which it was pushed: the threads that were running -- which the command
- processor suspends --, including the thread of that command level
- itself; the continuation of what pushed the level; and, if applicable,
- the condition (*note Condition system::) that caused the command level
- to be pushed. Each command level has its own thread scheduler, which
- controls all threads running at that level, including those threads'
- children.
- Some beginning users may find command levels confusing, particularly
- those who are new to Scheme or who are familiar with the more simplistic
- interaction methods of other Scheme systems. These users may disable
- the command level system with the 'levels' switch (*note Command
- processor switches::) by writing the command ',set levels off'.
- -- command: ,push
- -- command: ,pop
- -- command: ,resume
- -- command: ,resume level
- -- command: ,reset
- -- command: ,reset level
- ',push' pushes a new command level. ',pop' pops the current
- command level. 'C-d'/'^D', or EOF, has the same effect as the
- ',pop' command. Popping the top command level inquires the user
- whether to exit or to return to the top level. ',resume LEVEL'
- pops all command levels down to LEVEL and resumes all threads that
- were running at LEVEL when it was suspended to push another command
- level. ',reset LEVEL' resets the command processor to LEVEL,
- terminating all threads at that level but the command reader
- thread. ',resume' & ',reset' with no argument use the top command
- level.
- -- command: ,condition
- -- command: ,threads
- ',condition' sets the focus value to the condition that caused the
- command level to be pushed, or prints 'no condition' if there was
- no relevant condition. ',threads' invokes the inspector on the
- list of threads of the previous command level, or on nothing if the
- current command level is the top one.
- > ,push
- 1> ,push
- 2> ,pop
- 1> ,reset
- Top level
- > ,open threads formats
- > ,push
- 1> ,push
- 2> (spawn (lambda ()
- (let loop ()
- (sleep 10000) ; Sleep for ten seconds.
- (format #t "~&foo~%")
- (loop)))
- 'my-thread)
- 2>
- foo
- ,push
- 3> ,threads
- ; 2 values returned
- [0] '#{Thread 4 my-thread}
- [1] '#{Thread 3 command-loop}
- 3: q
- '(#{Thread 4 my-thread} #{Thread 3 command-loop})
- 3> ,resume 1
- foo
- 2>
- foo
- ,push
- 3> ,reset 1
- Back to 1> ,pop
- >
- File: scheme48.info, Node: Module commands, Next: SRFI 7, Prev: Command levels, Up: Command processor
- 2.4.6 Module commands
- ---------------------
- Scheme48's command processor is well-integrated with its module system
- (*note Module system::). It has several dedicated environments,
- including the user package and the config package, and can be used to
- evaluate code within most packages in the Scheme48 image during program
- development. The config package includes bindings for Scheme48's
- configuration language; structure & interface definitions may be
- evaluated in it. The command processor also has provisions to support
- rapid development and module reloading by automatically updating
- references to redefined variables in compiled code without having to
- reload all of that code.
- -- command: ,open struct ...
- Opens each STRUCT into the interaction environment, making all of
- its exported bindings available. This may have the consequence of
- loading code to implement those bindings. If there was code
- evaluated in the interaction environment that referred to a
- previously undefined variable for whose name a binding was exported
- by one of these structures, a message is printed to the effect that
- that binding is now available, and the code that referred to that
- undefined variable will be modified to subsequently refer to the
- newly available binding.
- -- command: ,load-package struct
- -- command: ,reload-package struct
- ',load-package' and ',reload-package' both load the code associated
- with the package underlying STRUCT, after ensuring that all of the
- other structures opened by that package are loaded as well.
- ',load-package' loads the code only if has not already been loaded;
- ',reload-package' unconditionally loads it.
- -- command: ,user
- -- command: ,user command-or-exp
- -- command: ,config
- -- command: ,config command-or-exp
- -- command: ,for-syntax
- -- command: ,for-syntax command-or-exp
- -- command: ,new-package
- -- command: ,in structure
- -- command: ,in structure command-or-exp
- These all operate on the interaction environment. ',user' sets it
- to the user package, which is the default at initial startup.
- ',user COMMAND-OR-EXP' temporarily sets the interaction environment
- to the user package, processes COMMAND-OR-EXP, and reverts the
- interaction environment to what it was before ',user' was invoked.
- The ',config' & ',for-syntax' commands are similar, except that
- they operate on the config package and the package used for the
- user package's macros (*note Macros in concert with modules::).
- ',new-package' creates a temporary, unnamed package with a vanilla
- R5RS environment and sets the interaction environment to it. That
- new package is not accessible in any way except to the user of the
- command processor, and it is destroyed after the user switches to
- another environment (unless the user uses the ',structure' command;
- see below). ',in STRUCTURE' sets the interaction environment to be
- STRUCTURE's package; STRUCTURE is a name whose value is extracted
- from the config package. ',in STRUCTURE COMMAND-OR-EXP' sets the
- interaction environment to STRUCTURE temporarily to process
- COMMAND-OR-EXP and then reverts it to what it was before the use of
- ',in'. Note that, within a structure, the bindings available are
- exactly those bindings that would be available within the
- structure's static code, i.e. code in the structure's 'begin'
- package clauses or code in files referred to by 'files' package
- clauses.
- -- command: ,user-package-is struct
- -- command: ,config-package-is struct
- ',user-package-is' & ',config-package-is' set the user & config
- packages, respectively, to be STRUCT's package. STRUCT is a name
- whose value is accessed from the current config package.
- -- command: ,structure name interface
- This defines a structure named NAME in the config package that is a
- view of INTERFACE on the current interaction environment.
- File: scheme48.info, Node: SRFI 7, Next: Debugging commands, Prev: Module commands, Up: Command processor
- 2.4.7 SRFI 7
- ------------
- Scheme48 supports [SRFI 7] after loading the 'srfi-7' structure by
- providing two commands for loading [SRFI 7] programs:
- -- command: ,load-srfi-7-program name filename
- -- command: ,load-srfi-7-script name filename
- These load [SRFI 7] a program into a newly constructed structure,
- named NAME, which opens whatever other structures are needed by
- features specified in the program. ',load-srfi-7-program' loads a
- simple [SRFI 7] program; ',load-srfi-7-script' skips the first
- line, intended for [SRFI 22] Unix scripts.
- File: scheme48.info, Node: Debugging commands, Next: Inspector, Prev: SRFI 7, Up: Command processor
- 2.4.8 Debugging commands
- ------------------------
- There are a number of commands useful for debugging, along with a
- continuation inspector, all of which composes a convenient debugger.
- -- command: ,bound? name
- -- command: ,where
- -- command: ,where procedure
- ',bound?' prints out binding information about NAME, if it is bound
- in the interaction environment, or 'Not bound' if NAME is unbound.
- ',where' prints out information about what file and package its
- procedure argument was created in. If PROCEDURE is not passed,
- ',where' uses the focus value. If ',where''s argument is not a
- procedure, it informs the user of this fact. If ',where' cannot
- find the location of its argument's creation, it prints 'Source
- file not recorded.'
- -- command: ,expand
- -- command: ,expand exp
- -- command: ,dis
- -- command: ,dis proc
- ',expand' prints out a macro-expansion of EXP, or the focus value
- if EXP is not provided. The expression to be expanded should be an
- ordinary S-expression. The expansion may contain 'generated names'
- and 'qualified names.' These merely contain lexical context
- information that allow one to differentiate between identifiers
- with the same name. Generated names look like '#{Generated NAME
- UNIQUE-NUMERIC-ID}'. Qualified names appear to be vectors; they
- look like '#(>> INTRODUCER-MACRO NAME UNIQUE-NUMERIC-ID)', where
- INTRODUCER-MACRO is the macro that introduced the name.
- ',dis' prints out a disassembly of its procedure, continuation, or
- template argument. If PROC is passed, it is evaluated in the
- interaction environment; if not, ',dis' disassembles the focus
- value. The disassembly is of Scheme48's virtual machine's byte
- code.(1)
- -- command: ,condition
- -- command: ,threads
- For the descriptions of these commands, *note Command levels::.
- These are mentioned here because they are relevant in the context
- of debugging.
- -- command: ,trace
- -- command: ,trace name ...
- -- command: ,untrace
- -- command: ,untrace name ...
- Traced procedures will print out information about when they are
- entered and when they exit. ',trace' lists all of the traced
- procedures' bindings. ',trace NAME ...' sets each NAME in the
- interaction environment, which should be bound to a procedure, to
- be a traced procedure over the original procedure. ',untrace'
- resets all traced procedures to their original, untraced
- procedures. ',untrace NAME ...' untraces each individual traced
- procedure of NAME ... in the interaction environment.
- -- command: ,preview
- Prints a trace of the previous command level's suspended
- continuation. This is analogous with stack traces in many
- debuggers.
- -- command: ,debug
- Invokes the debugger: runs the inspector on the previous command
- level's saved continuation. For more details, *note Inspector::.
- -- command: ,proceed
- -- command: ,proceed exp
- Returns to the continuation of the condition signalling of the
- previous command level. Only certain kinds of conditions will push
- a new command level, however -- breakpoints, errors, and
- interrupts, and, if the 'break-on-warnings' switch is on, warnings
- --; also, certain kinds of errors that do push new command levels
- do not permit being proceeded from. In particular, only with a few
- VM primitives may the ',proceed' command be used. If EXP is
- passed, it is evaluated in the interaction environment to produce
- the values to return; if it is not passed, zero values are
- returned.
- ---------- Footnotes ----------
- (1) A description of the byte code is forthcoming, although it does
- not have much priority to this manual's author. For now, users can read
- the rudimentary descriptions of the Scheme48 virtual machine's byte code
- instruction set in 'vm/interp/arch.scm' of Scheme48's Scheme source.
- File: scheme48.info, Node: Inspector, Next: Command programs, Prev: Debugging commands, Up: Command processor
- 2.4.9 Inspector
- ---------------
- Scheme48 provides a simple interactive object inspector. The command
- processor's prompt's end changes from '>' to ':' when in inspection
- mode. The inspector is the basis of the debugger, which is, for the
- most part, merely an inspector of continuations. In the debugger, the
- prompt is 'debug:'. In the inspector, objects are printed followed by
- menus of their components. Entries in the menu are printed with the
- index, which optionally includes a symbolic name, and the value of the
- component. For example, a pair whose car is the symbol 'a' and whose
- cdr is the symbol 'b' would be printed by the inspector like this:
- '(a . b)
- [0: car] 'a
- [1: cdr] 'b
- The inspector maintains a stack of the focus objects it previously
- inspected. Selecting a new focus object pushes the current one onto the
- stack; the 'u' command pops the stack.
- -- command: ,inspect
- -- command: ,inspect exp
- Invokes the inspector. If EXP is present, it is evaluated in the
- user package and its result is inspected (or a list of results, if
- it returned multiple values, is inspected). If EXP is absent, the
- current focus value is inspected.
- The inspector operates with its own set of commands, separate from
- the regular interaction commands, although regular commands may be
- invoked from the inspector as normal. Inspector commands are entered
- with or without a preceding comma at the inspector prompt. Multiple
- inspector commands may be entered on one line; an input may also consist
- of an expression to be evaluated. If an expression is evaluated, its
- value is selected as the focus object. Note, however, that, since
- inspector commands are symbols, variables cannot be evaluated just by
- entering their names; one must use either the ',run' command or wrap the
- variables in a 'begin'.
- These inspector commands are defined:
- -- inspector command: menu
- -- inspector command: m
- 'Menu' prints a menu for the focus object. 'M' moves forward in
- the current menu if there are more than sixteen items to be
- displayed.
- -- inspector command: u
- Pops the stack of focus objects, discarding the current one and
- setting the focus object to the current top of the stack.
- -- inspector command: q
- Quits the inspector, going back into the read-eval-print loop.
- -- inspector command: template
- Attempts to coerce the focus object into a template. If
- successful, this selects it as the new focus object; if not, this
- prints an error to that effect. Templates are the static
- components of closures and continuations: they contain the code for
- the procedure, the top-level references made by the procedure,
- literal constants used in the code, and any inferior templates of
- closures that may be constructed by the code.
- -- inspector command: d
- Goes down to the parent of the continuation being inspected. This
- command is valid only in the debugger mode, i.e. when the focus
- object is a continuation.
- File: scheme48.info, Node: Command programs, Next: Image-building commands, Prev: Inspector, Up: Command processor
- 2.4.10 Command programs
- -----------------------
- The Scheme48 command processor can be controlled programmatically by
- "command programs", programs written in the "exec language". This
- language is essentially a mirror of the commands but in a syntax using
- S-expressions. The language also includes all of Scheme. The exec
- language is defined as part of the "exec package".
- -- command: ,exec
- -- command: ,exec command
- Sets the interaction environment to be the exec package. If an
- argument is passed, it is set temporarily, only to run the given
- command.
- Commands in the exec language are invoked as procedures in Scheme.
- Arguments should be passed as follows:
- * Identifiers, such as those of structure names in the config
- package, should be passed as literal symbols. For instance, the
- command ',in frobbotz' would become in the exec language '(in
- 'frobbotz)'.
- * Filenames should be passed as strings; e.g., ',dump frob.image'
- becomes '(dump "frob.image")'.
- * Commands should be represented in list values with the car being
- the command name and the cdr being the arguments. Note that when
- applying a command an argument that is a command invocation is
- often quoted to produce a list, but the list should not include any
- quotation; for instance, ',in mumble ,undefine frobnicate' would
- become '(in 'mumble '(undefine frobnicate))', even though simply
- ',undefine frobnicate' would become '(undefine 'frobnicate)'.
- The reason for this is that the command invocation in the exec
- language is different from a list that represents a command
- invocation passed as an argument to another command; since commands
- in the exec language are ordinary procedures, the arguments must be
- quoted, but the quoted arguments are not themselves evaluated: they
- are applied as commands.
- An argument to a command that expects a command invocation can also
- be a procedure, which would simply be called with zero arguments.
- For instance, '(config (lambda () (display
- (interaction-environment)) (newline)))' will call the given
- procedure with the interaction environment set to the config
- package.
- * Expressions must be passed using the 'run' command. For example,
- the equivalent of ',user (+ 1 2)' in the exec language would be
- '(user '(run (+ 1 2)))'.
- Command programs can be loaded by running the ',load' command in the
- exec package. Scripts to load application bundles are usually written
- in the exec language and loaded into the exec package. For example,
- this command program, when loaded into the exec package, will load
- 'foo.scm' into the config package, ensure that the package 'frobbotzim'
- is loaded, and open the 'quuxim' structure in the user package:
- (config '(load "foo.scm"))
- (load-package 'frobbotzim)
- (user '(open quuxim))
- File: scheme48.info, Node: Image-building commands, Next: Resource statistics and control, Prev: Command programs, Up: Command processor
- 2.4.11 Image-building commands
- ------------------------------
- Since Scheme48's operation revolves about an image-based model, these
- commands provide a way to save heap images on the file system, which may
- be resumed by invoking the Scheme48 virtual machine on them as in *note
- Running Scheme48::.
- -- command: ,build resumer filename
- -- command: ,dump filename
- -- command: ,dump filename message
- ',build' evaluates RESUMER, whose value should be a unary
- procedure, and builds a heap image in FILENAME that, when resumed
- by the virtual machine, will pass the resumer all of the
- command-line arguments after the '-a' argument to the virtual
- machine. The run-time system will have been initialized as with
- usual resumers (*note Suspending and resuming heap images::), and a
- basic condition handler will have been installed by the time that
- the resumer is called. On Unix, RESUMER must return an integer
- exit status for the process. ',dump' dumps the Scheme48 command
- processor, including all of the current settings, to FILENAME. If
- MESSAGE is passed, it should be a string delimited by
- double-quotes, and it will be printed as part of the welcome banner
- on startup; its default value, if it is not present, is
- '"(suspended image)"'.
- File: scheme48.info, Node: Resource statistics and control, Prev: Image-building commands, Up: Command processor
- 2.4.12 Resource statistics and control
- --------------------------------------
- Scheme48 provides several devices for querying statistics about various
- resources and controlling resources, both in the command processor and
- programmatically.
- -- command: ,collect
- Forces a garbage collection and prints the amount of space in the
- heap before and after the collection.
- -- command: ,time expression
- Evaluates EXPRESSION and prints how long it took. Three numbers
- are printed: run time, GC time, and real time. The run time is the
- amount of time in Scheme code; the GC time is the amount of time
- spent in the garbage collector; and the real time is the actual
- amount of time that passed during the expression's evaluation.
- -- command: ,keep
- -- command: ,keep kind ...
- -- command: ,flush
- -- command: ,flush kind ...
- Scheme48 maintains several different kinds of information used for
- debugging information. ',keep' with no arguments shows what kinds
- of debugging data are preserved and what kinds are not. ',keep
- KIND ...' requests that the debugging data of the given kinds
- should be kept; the ',flush' command requests the opposite.
- ',flush' with no arguments flushes location names and resets the
- debug data table. The following are the kinds of debugging data:
- 'names'
- procedure names
- 'maps'
- environment maps used by the debugger to show local variable
- names
- 'files'
- filenames where procedures were defined
- 'source'
- source code surrounding continuations, printed by the debugger
- 'tabulate'
- if true, will store debug data records in a global table that
- can be easily flushed; if false, will store directly in
- compiled code
- ',flush' can also accept 'location-names', which will flush the
- table of top-level variables' names (printed, for example, by the
- ',bound?' command); 'file-packages', which will flush the table
- that maps filenames to packages in which code from those files
- should be evaluated; or 'table', in which case the table of debug
- data is flushed.
- Removing much debug data can significantly reduce the size of
- Scheme48 heap images, but it can also make error messages and
- debugging much more difficult. Usually, all debug data is
- retained; only for images that must be small and that do not need
- to be debuggable should the debugging data flags be turned off.
- The 'spatial' structure exports these utilities for displaying
- various statistics about the heap:
- -- procedure: space --> unspecified
- -- procedure: vector-space [predicate] --> unspecified
- -- procedure: record-space [predicate] --> unspecified
- 'Space' prints out a list of the numbers of all objects and the
- number of bytes allocated for those objects on the heap,
- partitioned by the objects' primitive types and whether or not they
- are immutable (pure) or mutable (impure). 'Vector-space' prints
- the number of vectors and the number of bytes used to store those
- vectors of several different varieties, based on certain heuristics
- about their form. If the predicate argument is passed, it gathers
- only vectors that satisfy that predicate. 'Record-space' prints
- out, for each record type in the heap, both the number of all
- instances of that record type and the number of bytes used to store
- all of those instances. Like 'vector-space', if the predicate
- argument is passed, 'record-space' will consider only those records
- that satisfy the predicate.
- All of these three procedures first invoke the garbage collector
- before gathering statistics.
- The 'traverse' structure provides a simple utility for finding paths
- by which objects refer to one another.
- -- procedure: traverse-breadth-first object --> unspecified
- -- procedure: traverse-depth-first object --> unspecified
- These traverse the heap, starting at OBJECT, recording all objects
- transitively referred to. 'Traverse-breadth-first' uses a
- FIFO-queue-directed breadth-first graph traversal, while
- 'traverse-depth-first' uses a LIFO-stack-directed depth-first graph
- traversal. The traversal halts at any leaves in the graph, which
- are distinguished by an internal "leaf predicate" in the module.
- See below on 'set-leaf-predicate!' on how to customize this and
- what the default is.
- The traversal information is recorded in a global resource; it is
- not thread-safe, and intended only for interactive usage. The
- record can be reset by passing some simple object with no
- references to either 'traverse-breadth-first' or
- 'traverse-depth-first'; e.g., '(traverse-depth-first #f)'.
- -- procedure: trail object --> unspecified
- After traversing the heap from an initial object, '(trail OBJECT)'
- prints the path of references and intermediate objects by which the
- initial object holds a transitive reference to OBJECT.
- -- procedure: set-leaf-predicate! predicate --> unspecified
- -- procedure: usual-leaf-predicate object --> boolean
- 'Set-leaf-predicate!' sets the current leaf predicate to be
- PREDICATE. 'Usual-leaf-predicate' is the default leaf predicate;
- it considers simple numbers (integers and flonums), strings, byte
- vectors, characters, and immediate objects (true, false, nil, and
- the unspecific object) to be leaves, and everything else to be
- branches.
- File: scheme48.info, Node: Module system, Next: System facilities, Prev: User environment, Up: Top
- 3 Module system
- ***************
- Scheme48 has an advanced module system that is designed to interact well
- with macros, incremental compilation, and the interactive development
- environment's (*note User environment::) code reloading facilities for
- rapid program development. For details on the integration of the module
- system and the user environment for rapid code reloading, *note Using
- the module system::.
- * Menu:
- * Module system architecture::
- * Module configuration language:: Language of the module system
- * Macros in concert with modules::
- * Static type system::
- File: scheme48.info, Node: Module system architecture, Next: Module configuration language, Up: Module system
- 3.1 Module system architecture
- ==============================
- The fundamental mechanism by which Scheme code is evaluated is the
- lexical environment. Scheme48's module system revolves around this
- fundamental concept. Its purpose is to control the denotation of names
- in code(1) in a structured, modular manner. The module system is
- manipulated by a static "configuration language", described in the next
- section; this section describes the concepts in the architecture of the
- module system.
- The "package" is the entity internal to the module system that maps a
- set of names to denotations. For example, the package that represents
- the Scheme language maps 'lambda' to a descriptor for the special form
- that the compiler interprets to construct a procedure, 'car' to the
- procedure that accesses the car of a pair, &c. Packages are not
- explicitly manipulated by the configuration language, but they lie
- underneath structures, which are described below. A package also
- contains the code of a module and controls the visibility of names
- within that code. It also includes some further information, such as
- optimizer switches. A "structure" is a view on a package; that is, it
- contains a package and an "interface" that lists all of the names it
- exports to the outside. Multiple structures may be constructed atop a
- single package; this mechanism is often used to offer multiple
- abstraction levels to the outside. A "module" is an abstract entity: it
- consists of some code, the namespace visible to the code, and the set of
- abstractions or views upon that code.
- A package contains a list of the structures whose bindings should be
- available in the code of that package. If a structure is referred to in
- a such a list of a package, the package is said to "open" that
- structure. It is illegal for a package to open two structures whose
- interfaces contain the same name.(2) Packages may also modify the names
- of the bindings that they import. They may import only selected
- bindings, exclude certain bindings from structures, rename imported
- bindings, create alias bindings, and add prefixes to names.
- Most packages will open the standard 'scheme' structure, although it
- is not implicitly opened, and the module system allows not opening
- 'scheme'. It may seem to be not very useful to not open it, but this is
- necessary if some bindings from it are intended to be shadowed by
- another structure, and it allows for entirely different languages from
- Scheme to be used in a package's code. For example, Scheme48's byte
- code interpreter virtual machine is implemented in a subset of Scheme
- called Pre-Scheme, which is described in a later chapter in this manual.
- The modules that compose the VM all open not the 'scheme' structure but
- the _'prescheme'_ structure. The configuration language itself is
- controlled by the module system, too. In another example, from Scsh,
- the Scheme shell, there is a structure 'scsh' that contains all of the
- Unix shell programming facilities. However, the 'scsh' structure
- necessarily modifies some of the bindings related to I/O that the
- 'scheme' structure exports. Modules could not open both 'scheme' and
- 'scsh', because they both provide several bindings with the same names,
- so Scsh defines a more convenient 'scheme-with-scsh' structure that
- opens both 'scheme', but with all of the shadowed bindings excluded, and
- 'scsh'; modules that use Scsh would open neither 'scsh' nor 'scheme':
- they instead open just 'scheme-with-scsh'.
- Interfaces are separated from structures in order that they may be
- reüsed and combined. For example, several different modules may
- implement the same abstractions differently. The structures that they
- include would, in such cases, reüse the same interfaces. Also, it is
- sometimes desirable to combine several interfaces into a "compound
- interface"; see the 'compound-interface' form in the next section.
- Furthermore, during interactive development, interface definitions may
- be reloaded, and the structures that use them will automatically begin
- using the new interfaces; *note Using the module system::.
- Scheme48's module system also supports "parameterized modules".
- Parameterized modules, sometimes known as "generic modules",
- "higher-order modules" or "functors", are essentially functions at the
- module system level that map structures to structures. They may be
- instantiated or applied arbitrarily many times, and they may accept and
- return arbitrarily many structures. Parameterized modules may also
- accept and return other parameterized modules.
- ---------- Footnotes ----------
- (1) This is in contrast to, for example, Common Lisp's package
- system, which controls the mapping from strings to names.
- (2) The current implementation, however, does not detect this.
- Instead it uses the left-most structure in the list of a package's
- 'open' clause; see the next section for details on this.
- File: scheme48.info, Node: Module configuration language, Next: Macros in concert with modules, Prev: Module system architecture, Up: Module system
- 3.2 Module configuration language
- =================================
- Scheme48's module system is used through a "module configuration
- language". _The configuration language is entirely separate from
- Scheme._ Typically, in one configuration, or set of components that
- compose a program, there is an 'interfaces.scm' file that defines all of
- the interfaces used by the configuration, and there is also a
- 'packages.scm' file that defines all of the packages & structures that
- compose it. Note that modules are not necessarily divided into files or
- restricted to one file: modules may include arbitrarily many files, and
- modules' code may also be written in-line to structure expressions (see
- the 'begin' package clause below), although that is usually only for
- expository purposes and trivial modules.
- Structures are always created with corresponding "package clauses".
- Each clause specifies an attribute of the package that underlies the
- structure or structures created using the clauses. There are several
- different types of clauses:
- -- package clause: open structure ...
- -- package clause: access structure ...
- 'Open' specifies that the package should open each of the listed
- structures, whose packages will be loaded if necessary. 'Access'
- specifies that each listed structure should be accessible using the
- '(structure-ref STRUCTURE IDENTIFIER)' special form, which
- evaluates to the value of IDENTIFIER exported by the accessed
- structure STRUCTURE. 'Structure-ref' is available from the
- 'structure-refs' structure. Each STRUCTURE passed to 'access' is
- not opened, however; the bindings exported thereby are available
- only using 'structure-ref'. While the qualified 'structure-ref'
- mechanism is no longer useful in the presence of modified
- structures (see below on 'modify', 'subset', & 'with-prefix'), some
- old code still uses it, and 'access' is also useful to force that
- the listed structures' packages be loaded without cluttering the
- namespace of the package whose clauses the 'access' clause is
- among.
- -- package clause: for-syntax package-clause ...
- Specifies a set of package clauses for the next floor of the
- reflective tower; *note Macros in concert with modules::.
- -- package clause: files file-specifier ...
- -- package clause: begin code ...
- 'Files' and 'begin' specify the package's code. 'Files' takes a
- sequence of namelists for the filenames of files that contain code;
- *note Filenames::. 'Begin' accepts in-line program code.
- -- package clause: optimize optimizer-specifier ...
- -- package clause: integrate [on?]
- 'Optimize' clauses request that specified compiler optimizers be
- applied to the code. (Actually, 'optimizer' is a misnomer. The
- 'optimize' clause may specify arbitrary passes that the compiler
- can be extended with.) 'Integrate' clauses specify whether or not
- integrable procedures from other modules, most notably Scheme
- primitives such as 'car' or 'vector-ref', should actually be
- integrated in this package. This is by default on. Most modules
- should leave it on for any reasonable performance; only a select
- few, into which code is intended to be dynamically loaded
- frequently and in which redefinition of imported procedures is
- common, need turn this off. The value of the argument to
- 'integrate' clauses should be a literal boolean, i.e. '#t' or '#f';
- if no argument is supplied, integration is enabled by default.
- Currently, the only optimizer built-in to Scheme48 is the automatic
- procedure integrator, or 'auto-integrate', which attempts stronger
- type reconstruction than is attempted with most code (*note Static
- type system::) and selects procedures below a certain size to be
- made integrable (so that the body will be compiled in-line in all
- known call sites). Older versions of Scheme48 also provided
- another optimizer, 'flat-environments', which would flatten certain
- lexical closure environments, rather than using a nested
- environment structure. Now, however, Scheme48's byte code compiler
- always flattens environments; specifying 'flat-environments' in an
- 'optimize' clause does nothing.
- A configuration is a sequence of definitions. There are definition
- forms for only structures and interfaces.
- -- configuration form: define-structure name interface package-clause
- ...
- -- configuration form: define-structures ((name interface) ...)
- package-clause ...
- 'Define-structure' creates a package with the given package clauses
- and defines NAME to be the single view atop it, with the interface
- INTERFACE. 'Define-structures' also creates a package with the
- given package clauses; upon that package, it defines each NAME to
- be a view on it with the corresponding interface.
- -- configuration form: define-module (name parameter ...) definition
- ... result
- -- configuration form: def name ... (parameterized-module argument ...)
- 'Define-module' defines NAME to be a parameterized module that
- accepts the given parameters.
- -- configuration form: define-interface name interface
- Defines NAME to be the interface that INTERFACE evaluates to.
- INTERFACE may either be an interface constructor application or
- simply a name defined to be an interface by some prior
- 'define-interface' form.
- -- interface constructor: export export-specifier ...
- 'Export' constructs a simple interface with the given export
- specifiers. The export specifiers specify names to export and
- their corresponding static types. Each EXPORT-SPECIFIER should
- have one of the following forms:
- 'SYMBOL'
- in which case SYMBOL is exported with the most general value
- type;
- '(SYMBOL TYPE)'
- in which case SYMBOL is exported with the given type; or
- '((SYMBOL ...) TYPE)'
- in which case each SYMBOL is exported with the same given type
- For details on the valid forms of TYPE, *note Static type system::.
- *Note:* All macros listed in interfaces _must_ be explicitly
- annotated with the type ':syntax'; otherwise they would be exported
- with a Scheme value type, which would confuse the compiler, because
- it would not realize that they are macros: it would instead treat
- them as ordinary variables that have regular run-time values.
- -- interface constructor: compound-interface interface ...
- This constructs an interface that contains all of the export
- specifiers from each INTERFACE.
- Structures may also be constructed anonymously; this is typically
- most useful in passing them to or returning them from parameterized
- modules.
- -- structure constructor: structure interface package-clauses
- -- structure constructor: structures (interface ...) package-clauses
- 'Structure' creates a package with the given clauses and evaluates
- to a structure over it with the given interface. 'Structures' does
- similarly, but it evaluates to a number of structures, each with
- the corresponding INTERFACE.
- -- structure constructor: subset structure (name ...)
- -- structure constructor: with-prefix structure name
- -- structure constructor: modify structure modifier ...
- These modify the interface of STRUCTURE. 'Subset' evaluates to a
- structure that exports only NAME ..., excluding any other names
- that STRUCTURE exported. 'With-prefix' adds a prefix NAME to every
- name listed in STRUCTURE's interface. Both 'subset' and
- 'with-prefix' are syntactic sugar for the more general 'modify',
- which applies the modifier commands in a strictly right-to-left or
- last-to-first order. *Note:* These all _denote new structures with
- new interfaces_; they do not destructively modify existing
- structures' interfaces.
- -- modifier command: prefix name
- -- modifier command: expose name ...
- -- modifier command: hide name ...
- -- modifier command: alias (from to) ...
- -- modifier command: rename (from to) ...
- 'Prefix' adds the prefix NAME to every exported name in the
- structure's interface. 'Expose' exposes only NAME ...; any other
- names are hidden. 'Hide' hides NAME .... 'Alias' exports each TO
- as though it were the corresponding FROM, as well as each FROM.
- 'Rename' exports each TO as if it were the corresponding FROM, but
- it also hides the corresponding FROM.
- Examples:
- (modify STRUCTURE
- (prefix foo:)
- (expose bar baz quux))
- makes only 'foo:bar', 'foo:baz', and 'foo:quux', available.
- (modify STRUCTURE
- (hide baz:quux)
- (prefix baz:)
- (rename (foo bar)
- (mumble frotz))
- (alias (gargle mumph)))
- exports 'baz:gargle' as what was originally 'mumble', 'baz:mumph'
- as an alias for what was originally 'gargle', 'baz:frotz' as what
- was originally 'mumble', 'baz:bar' as what was originally 'foo',
- _not_ 'baz:quux' -- what was originally simply 'quux' --, and
- everything else that STRUCTURE exported, but with a prefix of
- 'baz:'.
- There are several simple utilities for binding variables to
- structures locally and returning multiple structures not necessarily
- over the same package (i.e. not with 'structures'). These are all valid
- in the bodies of 'define-module' and 'def' forms, and in the arguments
- to parameterized modules and 'open' package clauses.
- -- syntax: begin body
- -- syntax: let ((name value) ...) body
- -- syntax: receive (name ...) producer body
- -- syntax: values value ...
- These are all as in ordinary Scheme. Note, however, that there is
- no reasonable way by which to use 'values' except to call it, so it
- is considered a syntax; also note that 'receive' may not receive a
- variable number of values -- i.e. there are no 'rest lists' --,
- because list values in the configuration language are nonsensical.
- Finally, the configuration language also supports syntactic
- extensions, or macros, as in Scheme.
- -- configuration form: define-syntax name transformer-specifier
- Defines the syntax transformer NAME to be the transformer specified
- by TRANSFORMER-SPECIFIER. TRANSFORMER-SPECIFIER is exactly the
- same as in Scheme code; it is evaluated as ordinary Scheme.
- File: scheme48.info, Node: Macros in concert with modules, Next: Static type system, Prev: Module configuration language, Up: Module system
- 3.3 Macros in concert with modules
- ==================================
- One reason that the standard Scheme language does not support a module
- system yet is the issue of macros and modularity. There are several
- issues to deal with:
- * that compilation of code that uses macros requires presence of
- those macros' definitions, which prevents true separate
- compilation, because those macros may be from other modules;
- * that a macro's expansion must preserve referential transparency and
- hygiene, for example in cases where it refers to names from within
- the module in which it was defined, even if those names weren't
- exported; and
- * that a macro's code may be arbitrary Scheme code, which in turn can
- use other modules, so one module's compile-time, when macros are
- expanded, is another's run-time, when the code used in macros is
- executed by the expander: this makes a tower of phases of code
- evaluation over which some coherent control must be provided.
- Scheme48's module system tries to address all of these issues coherently
- and comprehensively. Although it cannot offer _total_ separate
- compilation, it can offer incremental compilation, and compiled modules
- can be dumped to the file system & restored in the process of
- incremental compilation.(1)
- Scheme48's module system is also very careful to preserve non-local
- module references from a macro's expansion. Macros in Scheme48 are
- required to perform hygienic renaming in order for this preservation,
- however; *note Explicit renaming macros::. For a brief example,
- consider the 'delay' syntax for lazy evaluation. It expands to a simple
- procedure call:
- (delay EXPRESSION)
- ==> (make-promise (lambda () EXPRESSION))
- However, 'make-promise' is not exported from the 'scheme' structure.
- The expansion works correctly due to the hygienic renaming performed by
- the 'delay' macro transformer: when it hygienically renames
- 'make-promise', the output contains not the symbol but a special token
- that refers exactly to the binding of 'make-promise' from the
- environment in which the 'delay' macro transformer was defined. Special
- care is taken to preserve this information. Had 'delay' expanded to a
- simple S-expression with simple symbols, it would have generated a free
- reference to 'make-promise', which would cause run-time undefined
- variable errors, or, if the module in which 'delay' was used had its
- _own_ binding of or imported a binding of the name 'make-promise',
- 'delay''s expansion would refer to the wrong binding, and there could
- potentially be drastic and entirely unintended impact upon its
- semantics.
- Finally, Scheme48's module system has a special design for the tower
- of phases, called a "reflective tower".(2) Every storey represents the
- environment available at successive macro levels. That is, when the
- right-hand side of a macro definition or binding is evaluated in an
- environment, the next storey in that environment's reflective tower is
- used to evaluate that macro binding. For example, in this code, there
- are two storeys used in the tower:
- (define (foo ...bar...)
- (let-syntax ((baz ...quux...))
- ...zot...))
- In order to evaluate code in one storey of the reflective tower, it is
- necessary to expand all macros first. Most of the code in this example
- will eventually be evaluated in the first storey of the reflective tower
- (assuming it is an ordinary top-level definition), but, in order to
- expand macros in that code, the 'let-syntax' must be expanded. This
- causes '...quux...' to be evaluated in the _second_ storey of the tower,
- after which macro expansion can proceed, and long after which the
- enclosing program can be evaluated.
- The module system provides a simple way to manipulate the reflective
- tower. There is a package clause, 'for-syntax', that simply contains
- package clauses for the next storey in the tower. For example, a
- package with the following clauses:
- (open scheme foo bar)
- (for-syntax (open scheme baz quux))
- has all the bindings of 'scheme', 'foo', & 'bar', at the ground storey;
- and the environment in which macros' definitions are evaluated provides
- everything from 'scheme', 'baz', & 'quux'.
- With no 'for-syntax' clauses, the 'scheme' structure is implicitly
- opened; however, if there are 'for-syntax' clauses, 'scheme' must be
- explicitly opened.(3) Also, 'for-syntax' clauses may be arbitrarily
- nested: reflective towers are theoretically infinite in height. (They
- are internally implemented lazily, so they grow exactly as high as they
- need to be.)
- Here is a simple, though contrived, example of using 'for-syntax'.
- The 'while-loops' structure exports 'while', a macro similar to C's
- 'while' loop. 'While''s transformer unhygienically binds the name
- 'exit' to a procedure that exits from the loop. It necessarily,
- therefore, uses explicit renaming macros (*note Explicit renaming
- macros::) in order to break hygiene; it also, in the macro transformer,
- uses the 'destructure' macro to destructure the input form (*note
- Library utilities::, in particular, the structure 'destructuring' for
- destructuring S-expressions).
- (define-structure while-loops (export while)
- (open scheme)
- (for-syntax (open scheme destructuring))
- (begin
- (define-syntax while
- (lambda (form r compare)
- (destructure (((WHILE test . body) form))
- `(,(r 'CALL-WITH-CURRENT-CONTINUATION)
- (,(r 'LAMBDA) (EXIT)
- (,(r 'LET) (r 'LOOP) ()
- (,(r 'IF) ,test
- (,(r 'BEGIN)
- ,@body
- (,(r 'LOOP)))))))))
- (CALL-WITH-CURRENT-CONTINUATION LAMBDA LET IF BEGIN))))
- This next 'while-example' structure defines an example procedure
- 'foo' that uses 'while'. Since 'while-example' has no macro
- definitions, there is no need for any 'for-syntax' clauses; it imports
- 'while' from the 'while-loops' structure only at the ground storey,
- because it has no macro bindings to evaluate the transformer expressions
- of:
- (define-structure while-example (export foo)
- (open scheme while-loops)
- (begin
- (define (foo x)
- (while (> x 9)
- (if (integer? (sqrt x))
- (exit (expt x 2))
- (set! x (- x 1)))))))
- ---------- Footnotes ----------
- (1) While such facilities are not built-in to Scheme48, there is a
- package to do this, which will probably be integrated at some point soon
- into Scheme48.
- (2) This would be more accurately named 'syntactic tower,' as it has
- nothing to do with reflection.
- (3) This is actually only in the default config package of the
- default development environment. The full mechanism is very general.
- File: scheme48.info, Node: Static type system, Prev: Macros in concert with modules, Up: Module system
- 3.4 Static type system
- ======================
- Scheme48 supports a rudimentary static type system. It is intended
- mainly to catch some classes of type and arity mismatch errors early, at
- compile-time. By default, there is only _extremely_ basic analysis,
- which is typically only good enough to catch arity errors and the really
- egregious type errors. The full reconstructor, which is still not very
- sophisticated, is enabled by specifying an optimizer pass that invokes
- the code usage analyzer. The only optimizer pass built-in to Scheme48,
- the automatic procedure integrator, named 'auto-integrate', does so.
- The type reconstructor attempts to assign the most specific type it
- can to program terms, signalling warnings for terms that are certain to
- be invalid by Scheme's dynamic semantics. Since the reconstructor is
- not very sophisticated, it frequently gives up and assigns very general
- types to many terms. Note, however, that it is very lenient in that it
- only assigns more general types: it will _never_ signal a warning
- because it could not reconstruct a very specific type. For example, the
- following program will produce no warnings:
- (define (foo x y) (if x (+ y 1) (car y)))
- Calls to 'foo' that are clearly invalid, such as '(foo #t 'a)', could
- cause the type analyzer to signal warnings, but it is not sophisticated
- enough to determine that 'foo''s second argument must be either a number
- or a pair; it simply assigns a general value type (see below).
- There are some tricky cases that depend on the order by which
- arguments are evaluated in a combination, because that order is not
- specified in Scheme. In these cases, the relevant types are narrowed to
- the most specific ones that could not possibly cause errors at run-time
- for any order. For example,
- (lambda (x) (+ (begin (set! x '(3)) 5) (car x)))
- will be assigned the type '(proc (:pair) :number)', because, if the
- arguments are evaluated right-to-left, and 'x' is not a pair, there will
- be a run-time type error.
- The type reconstructor presumes that all code is potentially
- reachable, so it may signal warnings for code that the most trivial
- control flow analyzer could decide unreachable. For example, it would
- signal a warning for '(if #t 3 (car 7))'. Furthermore, it does not
- account for continuation throws; for example, though it is a perfectly
- valid Scheme program, the type analyzer might signal a warning for this
- code:
- (call-with-current-continuation
- (lambda (k) (0 (k))))
- The type system is based on a type lattice. There are several
- maximum or 'top' elements, such as ':values', ':syntax', and
- ':structure'; and one minimum or 'bottom' element, ':error'. This
- description of the type system makes use of the following notations: 'E
- : T' means that the term E has the type, or some compatible subtype of,
- T; and 'T--_{A} <= T--_{B}' means that T-_{A} is a compatible subtype of
- T-_{B} -- that is, any term whose static type is T-_{A} is valid in any
- context that expects the type T-_{B} --.
- Note that the previous text has used the word 'term,' not
- 'expression,' because static types are assigned to not only Scheme
- expressions. For example, 'cond' macro has the type ':syntax'.
- Structures in the configuration language also have static types: their
- interfaces. (Actually, they really have the type ':structure', but this
- is a deficiency in the current implementation's design.) Types, in
- fact, have their own type: ':type'. Here are some examples of values,
- first-class or otherwise, and their types:
- cond : :syntax
- (values 1 'foo '(x . y))
- : (some-values :exact-integer :symbol :pair)
- :syntax : :type
- 3 : :exact-integer
- (define-structure foo (export a b) ...)
- foo : (export a b)
- One notable deficiency of the type system is the absence of any sort
- of parametric polymorphism.
- -- type constructor: join type ...
- -- type constructor: meet type ...
- 'Join' and 'meet' construct the supremum and infimum elements in
- the type lattice of the given types. That is, for any two disjoint
- types T-_{A} and T-_{B}, let T-_{J} be '(join T--_{A} T--_{B})' and
- T-_{M} be '(meet T--_{A} T--_{B})':
- * T-_{J} <= T-_{A} and T-_{J} <= T-_{B}
- * T-_{A} <= T-_{M} and T-_{B} <= T-_{M}
- For example, '(join :pair :null)' allows either pairs or nil, i.e.
- lists, and '(meet :integer :exact)' accepts only integers that are
- also exact.
- (More complete definitions of supremum, infimum, and other elements
- of lattice theory, may be found elsewhere.)
- -- type: :error
- This is the minimal, or 'bottom,' element in the type lattice. It
- is the type of, for example, calls to 'error'.
- -- type: :values
- -- type: :arguments
- All Scheme _expressions_ have the type ':values'. They may have
- more specific types as well, but all expressions' types are
- compatible subtypes of ':values'. ':Values' is a maximal element
- of the type lattice. ':Arguments' is synonymous with ':values'.
- -- type: :value
- Scheme expressions that have a single result have the type
- ':value', or some compatible subtype thereof; it is itself a
- compatible subtype of ':values'.
- -- type constructor: some-values type ...
- 'Some-values' is used to denote the types of expressions that have
- multiple results: if 'E_{1} ... E_{N}' have the types 'T_{1} ...
- T_{N}', then the Scheme expression '(values E_{1} ... E_{N})' has
- the type '(some-values T_{1} ... T_{N})'.
- 'Some-values'-constructed types are compatible subtypes of
- ':values'.
- 'Some-values' also accepts 'optional' and 'rest' types, similarly
- to Common Lisp's 'optional' and 'rest' formal parameters. The
- sequence of types may contain a '&opt' token, followed by which is
- any number of further types, which are considered to be optional.
- For example, 'make-vector''s domain is '(some-values :exact-integer
- &opt :value)'. There may also be a '&rest' token, which must
- follow the '&opt' token if there is one. Following the '&rest'
- token is one more type, which the rest of the sequents in a
- sequence after the required or optional sequents must satisfy. For
- example, 'map''s domain is '(some-values :procedure (join :pair
- :null) &rest (join :pair :null))': it accepts one procedure and at
- least one list (pair or null) argument.
- -- type constructor: procedure domain codomain
- -- type constructor: proc (arg-type ...) result-type
- Procedure type constructors. Procedure types are always compatible
- subtypes of ':value'. 'Procedure' is a simple constructor from a
- specific domain and codomain; DOMAIN and CODOMAIN must be
- compatible subtypes of ':values'. 'Proc' is a more convenient
- constructor. It is equivalent to '(procedure (some-values ARG-TYPE
- ...) RESULT-TYPE)'.
- -- type: :boolean
- -- type: :char
- -- type: :null
- -- type: :unspecific
- -- type: :pair
- -- type: :string
- -- type: :symbol
- -- type: :vector
- -- type: :procedure
- -- type: :input-port
- -- type: :output-port
- Types that represent standard Scheme data. These are all
- compatible subtypes of ':value'. ':Procedure' is the general type
- for all procedures; see 'proc' and 'procedure' for procedure types
- with specific domains and codomains.
- -- type: :number
- -- type: :complex
- -- type: :real
- -- type: :rational
- -- type: :integer
- Types of the Scheme numeric tower. ':integer <= :rational <= :real
- <= :complex <= :number'
- -- type: :exact
- -- type: :inexact
- -- type: :exact-integer
- -- type: :inexact-real
- ':Exact' and ':inexact' are the types of exact and inexact numbers,
- respectively. They are typically met with one of the types in the
- numeric tower above; ':exact-integer' and ':inexact-real' are two
- conveniences for the most common meets.
- -- type: :other
- ':Other' is for types that do not fall into any of the previous
- value categories. (':other <= :value') All new types introduced,
- for example by 'loophole' (*note Type annotations::), are
- compatible subtypes of ':other'.
- -- type constructor: variable type
- This is the type of all assignable variables, where 'TYPE <=
- :value'. Assignment to variables whose types are value types, not
- assignable variable types, is invalid.
- -- type: :syntax
- -- type: :structure
- ':Syntax' and ':structure' are two other maximal elements of the
- type lattice, along with ':values'. ':Syntax' is the type of
- macros or syntax transformers. ':Structure' is the general type of
- all structures.
- 3.4.1 Types in the configuration language
- -----------------------------------------
- Scheme48's configuration language has several places in which to write
- types. However, due to the definitions of certain elements of the
- configuration language, notably the 'export' syntax, the allowable type
- syntax is far more limited than the above. Only the following are
- provided:
- -- type: :values
- -- type: :value
- -- type: :arguments
- -- type: :syntax
- -- type: :structure
- All of the built-in maximal elements of the type lattice are
- provided, as well as the simple compatible subtype ':values',
- ':value'.
- -- type: :boolean
- -- type: :char
- -- type: :null
- -- type: :unspecific
- -- type: :pair
- -- type: :string
- -- type: :symbol
- -- type: :vector
- -- type: :procedure
- -- type: :input-port
- -- type: :output-port
- -- type: :number
- -- type: :complex
- -- type: :real
- -- type: :rational
- -- type: :integer
- -- type: :exact-integer
- These are the only value types provided in the configuration
- language. Note the conspicuous absence of ':exact', ':inexact',
- and ':inexact-real'.
- -- type constructor: procedure domain codomain
- -- type constructor: proc (arg-type ...) result-type
- These two are the only type constructors available. Note here the
- conspicuous absence of 'some-values', so procedure types that are
- constructed by 'procedure' can accept only one argument (or use the
- overly general ':values' type) & return only one result (or, again,
- use ':values' for the codomain), and procedure types that are
- constructed by 'proc' are similar in the result type.
- File: scheme48.info, Node: System facilities, Next: Multithreading, Prev: Module system, Up: Top
- 4 System facilities
- *******************
- This chapter details many facilities that the Scheme48 run-time system
- provides.
- * Menu:
- * System features::
- * Condition system::
- * Bitwise manipulation::
- * Generic dispatch system::
- * I/O system::
- * Reader & writer::
- * Records::
- * Suspending and resuming heap images::
- File: scheme48.info, Node: System features, Next: Condition system, Up: System facilities
- 4.1 System features
- ===================
- Scheme48 provides a variety of miscellaneous features built-in to the
- system.
- * Menu:
- * Miscellaneous features::
- * Various utilities::
- * Filenames::
- * Fluid/dynamic bindings::
- * ASCII character encoding::
- * Integer enumerations::
- * Cells::
- * Queues::
- * Hash tables::
- * Weak references::
- * Type annotations::
- * Explicit renaming macros::
- File: scheme48.info, Node: Miscellaneous features, Next: Various utilities, Up: System features
- 4.1.1 Miscellaneous features
- ----------------------------
- The structure 'features' provides some very miscellaneous features in
- Scheme48.
- -- procedure: immutable? object --> boolean
- -- procedure: make-immutable! object --> object
- All Scheme objects in Scheme48 have a flag determining whether or
- not they may be mutated. All immediate Scheme objects ('()', '#f',
- &c.) are immutable; all fixnums (small integers) are immutable; and
- all stored objects -- vectors, pairs, &c. -- may be mutable.
- 'Immutable?' returns '#t' if OBJECT may not be mutated, and
- 'make-immutable!', a bit ironically, modifies OBJECT so that it may
- not be mutated, if it was not already immutable, and returns it.
- (immutable? #t) => #t
- (define p (cons 1 2))
- (immutable? p) => #f
- (car p) => 1
- (set-car! p 5)
- (car p) => 5
- (define q (make-immutable! p))
- (eq? p q) => #t
- (car p) => 5
- (immutable? q) => #t
- (set-car! p 6) error-> immutable pair
- -- procedure: string-hash string --> integer-hash-code
- Computes a basic but fast hash of STRING.
- (string-hash "Hello, world!") => 1161
- -- procedure: force-output port --> unspecified
- Forces all buffered output to be sent out of PORT.
- This is identical to the binding of the same name exported by the
- 'i/o' structure (*note Ports::).
- -- procedure: current-noise-port --> output-port
- The current noise port is a port for sending noise messages that
- are inessential to the operation of a program.
- The 'silly' structure exports a single procedure, implemented as a VM
- primitive for the silly reason of efficiency, hence the name of the
- structure.(1) It is used in an inner loop of the reader.
- -- procedure: reverse-list->string char-list count --> string
- Returns a string of the first COUNT characters in CHAR-LIST, in
- reverse. It is a serious error if CHAR-LIST is not a list whose
- length is at least COUNT; the error is not detected by the VM, so
- bogus pointers may be involved as a result. Use this routine with
- care in inner loops.
- The 'debug-messages' structure exports a procedure for emitting very
- basic debugging messages for low-level problems.
- -- procedure: debug-message item ... --> unspecified
- Prints ITEM ... directly to an error port,(2) eliding buffering and
- thread synchronization on the Scheme side. Objects are printed as
- follows:
- * Fixnums (small integers) are written in decimal.
- * Characters are written literally with a '#\' prefix. No
- naming translation is performed, so the space and newline
- characters are written literally, not as '#\space' or
- '#\newline'.
- * Records are written as #{TYPE-NAME}, where TYPE-NAME is the
- name of the record's type.
- * Strings and symbols are written literally.
- * Booleans and the empty list are written normally, i.e. as
- '#t', '#f', or '()'.
- * Pairs are written as '(...)'.
- * Vectors are written as '#(...)'.
- * Objects of certain primitive types are written as #{TYPE}:
- procedures, templates, locations, code (byte) vectors, and
- continuations.(3)
- * Everything else is printed as #{???}.
- The 'code-quote' structure exports a variant of 'quote' that is
- useful in some sophisticated macros.
- -- special form: code-quote object --> object
- Evaluates to the literal value of OBJECT. This is semantically
- identical to 'quote', but OBJECT may be anything, and the compiler
- will not signal any warnings regarding its value, while such
- warnings would be signalled for 'quote' expressions that do not
- wrap readable S-expressions: arbitrary, compound, unreadable data
- may be stored in 'code-quote'. Values computed at compile-time may
- thus be transmitted to run-time code. However, care should be
- taken in doing this.
- ---------- Footnotes ----------
- (1) The author of this manual is not at fault for this nomenclature.
- (2) On Unix, this is 'stderr', the standard I/O error output file.
- (3) Continuations here are in the sense of VM stack frames, not
- escape procedures as obtained using 'call-with-current-continuation'.
- File: scheme48.info, Node: Various utilities, Next: Filenames, Prev: Miscellaneous features, Up: System features
- 4.1.2 Various utilities
- -----------------------
- The 'util' structure contains some miscellaneous utility routines
- extensively used internally in the run-time system. While they are not
- meant to compose a comprehensive library (such as, for example, [SRFI
- 1]), they were found useful in building the run-time system without
- introducing massive libraries into the core of the system.
- -- procedure: unspecific --> unspecific
- Returns Scheme48's "unspecific" token, which is used wherever R5RS
- uses the term 'unspecific' or 'unspecified.' In this manual, the
- term 'unspecified' is used to mean that the values returned by a
- particular procedure are not specified and may be anything,
- including a varying number of values, whereas 'unspecific' refers
- to Scheme48's specific 'unspecific' value that the 'unspecific'
- procedure returns.
- -- procedure: reduce kons knil list --> final-knil
- Reduces LIST by repeatedly applying KONS to elements of LIST and
- the current KNIL value. This is the fundamental list recursion
- operator.
- (reduce KONS KNIL
- (cons ELT_{1}
- (cons ELT_{2}
- (...(cons ELT_{N} '())...))))
- ==
- (KONS ELT_{1}
- (KONS ELT_{2}
- (...(KONS ELT_{N} KNIL)...)))
- Example:
- (reduce append '() '((1 2 3) (4 5 6) (7 8 9)))
- => (1 2 3 4 5 6 7 8 9)
- (append '(1 2 3)
- (append '(4 5 6)
- (append '(7 8 9) '())))
- => (1 2 3 4 5 6 7 8 9)
- -- procedure: fold combiner list accumulator --> final-accumulator
- Folds LIST into an accumulator by repeatedly combining each element
- into an accumulator with COMBINER. This is the fundamental list
- iteration operator.
- (fold COMBINER
- (list ELT_{1} ELT_{2} ... ELT_{N})
- ACCUMULATOR)
- ==
- (let* ((accum_{1} (COMBINER ELT_{1} ACCUMULATOR))
- (accum_{2} (COMBINER ELT_{2} accum_{1}))
- ...
- (accum_{N} (COMBINER ELT_{N} accum_{N-1})))
- accum_{N})
- Example:
- (fold cons '() '(a b c d))
- => (d c b a)
- (cons 'd (cons 'c (cons 'b (cons 'a '()))))
- => (d c b a)
- -- procedure: fold->2 combiner list accumulator_{1} accumulator_{2} -->
- [final-accumulator_{1} final-accumulator_{2}]
- -- procedure: fold->3 combiner list accumulator_{1} accumulator_{2}
- accumulator_{3} --> [final-accumulator_{1}
- final-accumulator_{2} final-accumulator_{3}]
- Variants of 'fold' for two and three accumulators, respectively.
- ;;; Partition LIST by elements that satisfy PRED? and those
- ;;; that do not.
- (fold->2 (lambda (elt satisfied unsatisfied)
- (if (PRED? elt)
- (values (cons elt satisfied) unsatisfied)
- (values satisfied (cons elt unsatisfied))))
- LIST
- '() '())
- -- procedure: filter predicate list --> filtered-list
- Returns a list of all elements in LIST that satisfy PREDICATE.
- (filter odd? '(3 1 4 1 5 9 2 6 5 3 5))
- => (3 1 1 5 9 5 3 5)
- -- procedure: posq object list --> integer or '#f'
- -- procedure: posv object list --> integer or '#f'
- -- procedure: position object list --> integer or '#f'
- These find the position of the first element equal to OBJECT in
- LIST. 'Posq' compares elements by 'eq?'; 'posv' compares by
- 'eqv?'; 'position' compares by 'equal?'.
- (posq 'c '(a b c d e f))
- => 2
- (posv 1/2 '(1 1/2 2 3/2))
- => 1
- (position '(d . e) '((a . b) (b . c) (c . d) (d . e) (e . f)))
- => 3
- -- procedure: any predicate list --> value or '#f'
- -- procedure: every predicate list --> boolean
- 'Any' returns the value that PREDICATE returns for the first
- element in LIST for which PREDICATE returns a true value; if no
- element of LIST satisfied PREDICATE, 'any' returns '#f'. 'Every'
- returns '#t' if every element of LIST satisfies PREDICATE, or '#f'
- if there exist any that do not.
- (any (lambda (x) (and (even? x) (sqrt x)))
- '(0 1 4 9 16))
- => 2
- (every odd? '(1 3 5 7 9))
- => #t
- -- procedure: sublist list start end --> list
- Returns a list of the elements in LIST including & after that at
- the index START and before the index END.
- (sublist '(a b c d e f g h i) 3 6) => (d e f)
- -- procedure: last list --> value
- Returns the last element in LIST. 'Last''s effect is undefined if
- LIST is empty.
- (last '(a b c)) => c
- -- procedure: insert object list elt< --> list
- Inserts OBJECT into the sorted list LIST, comparing the order of
- OBJECT and each element by ELT<.
- (insert 3 '(0 1 2 4 5) <) => (0 1 2 3 4 5)
- File: scheme48.info, Node: Filenames, Next: Fluid/dynamic bindings, Prev: Various utilities, Up: System features
- 4.1.3 Filenames
- ---------------
- There are some basic filename manipulation facilities exported by the
- 'filenames' structure.(1)
- -- constant: *scheme-file-type* --> symbol
- -- constant: *load-file-type* --> symbol
- '*Scheme-file-type*' is a symbol denoting the file extension that
- Scheme48 assumes for Scheme source files; any other extension, for
- instance in the filename list of a structure definition, must be
- written explicitly. '*Load-file-type*' is a symbol denoting the
- preferable file extension to load files from. ('*Load-file-type*'
- was used mostly in bootstrapping Scheme48 from Pseudoscheme or T
- long ago and is no longer very useful.)
- -- procedure: file-name-directory filename --> string
- -- procedure: file-name-nondirectory filename --> string
- 'File-name-directory' returns the directory component of the
- filename denoted by the string FILENAME, including a trailing
- separator (on Unix, '/'). 'File-name-nondirectory' returns
- everything but the directory component of the filename denoted by
- the string FILENAME, including the extension.
- (file-name-directory "/usr/local/lib/scheme48/scheme48.image")
- => "/usr/local/lib/scheme48/"
- (file-name-nondirectory "/usr/local/lib/scheme48/scheme48.image")
- => "scheme48.image"
- (file-name-directory "scheme48.image")
- => ""
- (file-name-nondirectory "scheme48.image")
- => "scheme48.image"
- "Namelists" are platform-independent means by which to name files.
- They are represented as readable S-expressions of any of the following
- forms:
- 'BASENAME'
- represents a filename with only a basename and no directory or file
- type/extension;
- '(DIRECTORY BASENAME [TYPE])'
- represents a filename with a single preceding directory component
- and an optional file type/extension; and
- '((DIRECTORY ...) BASENAME [TYPE])'
- represents a filename with a sequence of directory components, a
- basename, and an optional file type/extension.
- Each atomic component -- that is, the basename, the type/extension,
- and each individual directory component -- may be either a string or a
- symbol. Symbols are converted to the canonical case of the host
- operating system by 'namestring' (on Unix, lowercase); the case of
- string components is not touched.
- -- procedure: namestring namelist directory default-type --> string
- Converts NAMELIST to a string in the format required by the host
- operating system.(2) If NAMELIST did not have a directory
- component, DIRECTORY, a string in the underlying operating system's
- format for directory prefixes, is added to the resulting
- namestring; and, if NAMELIST did not have a type/extension,
- DEFAULT-TYPE, which may be a string or a symbol and which should
- _not_ already contain the host operating system's delimiter
- (usually a dot), is appended to the resulting namestring.
- DIRECTORY or DEFAULT-TYPE may be '#f', in which case they are not
- prefixed or appended to the resulting filename.
- (namestring 'foo #f #f) => "foo"
- (namestring 'foo "bar" 'baz) => "bar/foo.baz"
- (namestring '(rts defenum) "scheme" 'scm)
- => "scheme/rts/defenum.scm"
- (namestring '((foo bar) baz quux) "zot" #f)
- => "zot/foo/bar/baz.quux"
- (namestring "zot/foo/bar/baz.quux" #f "mumble")
- => "zot/foo/bar/baz.quux.mumble"
- 4.1.3.1 Filename translations
- .............................
- Scheme48 keeps a registry of "filename translations", translations from
- filename prefixes to the real prefixes. This allows abstraction of
- actual directory prefixes without necessitating running Scheme code to
- construct directory pathnames (for example, in configuration files).
- Interactively, in the usual command processor, users can set filename
- translations with the ',translate'; *note Basic commands::.
- -- procedure: translations --> string/string-alist
- Returns the alist of filename translations.
- -- procedure: set-translation! from to --> unspecified
- Adds a filename prefix translation, overwriting an existing one if
- one already existed.
- -- procedure: translate filename --> translated-filename
- Translates the first prefix of FILENAME found in the registry of
- translations and returns the translated filename.
- (set-translation! "s48" "/home/me/scheme/scheme48/scheme")
- (translate (namestring '(bcomp frame) "s48" 'scm))
- => "/home/me/scheme/scheme48/scheme/bcomp/frame.scm"
- (translate (namestring "comp-packages" "s48" 'scm))
- => "/home/me/scheme/scheme48/scheme/comp-packages.scm"
- (translate "s48/frobozz")
- => "/home/me/scheme/scheme48/scheme/frobozz"
- (set-translation! "scheme48" "s48")
- (translate (namestring '((scheme48 big) filename) #f 'scm))
- => scheme48/big/filename.scm
- (translate (translate (namestring '((scheme48 big) filename) #f 'scm)))
- => "/home/me/scheme/scheme48/scheme/big/filename.scm"
- One filename translation is built-in, mapping '=scheme48/' to the
- directory of system files in a Scheme48 installation, which on Unix is
- typically a directory in '/usr/local/lib'.
- (translate "=scheme48/scheme48.image")
- => /usr/local/scheme48/scheme48.image
- ---------- Footnotes ----------
- (1) The facilities Scheme48 provides are very rudimentary, and they
- are not intended to act as a coherent and comprehensive pathname or
- logical name facility such as that of Common Lisp. However, they served
- the basic needs of Scheme48's build process when they were originally
- created.
- (2) However, the current standard distribution of Scheme48 is
- specific to Unix: the current code implements only Unix filename
- facilities.
- File: scheme48.info, Node: Fluid/dynamic bindings, Next: ASCII character encoding, Prev: Filenames, Up: System features
- 4.1.4 Fluid/dynamic bindings
- ----------------------------
- The 'fluids' structure provides a facility for dynamically bound
- resources, like special variables in Common Lisp, but with first-class,
- unforgeable objects.
- Every thread (*note Multithreading::) in Scheme48 maintains a "fluid
- or dynamic environment". It maps "fluid descriptors" to their values,
- much like a lexical environment maps names to their values. The dynamic
- environment is implemented by deep binding and dynamically scoped.
- Fluid variables are represented as first-class objects for which there
- is a top-level value and possibly a binding in the current dynamic
- environment. Escape procedures, as created with Scheme's
- 'call-with-current-continuation', also store & preserve the dynamic
- environment at the time of their continuation's capture and restore it
- when invoked.
- The convention for naming variables that are bound to fluid objects
- is to add a prefix of '$' (dollar sign); e.g., '$foo'.
- -- procedure: make-fluid top-level-value --> fluid
- Fluid constructor.
- -- procedure: fluid fl --> value
- -- procedure: set-fluid! fl value --> unspecified
- -- procedure: fluid-cell-ref fluid-cell --> value
- -- procedure: fluid-cell-set! fluid-cell value --> unspecified
- 'Fluid' returns the value that the current dynamic environment
- associates with FL, if it has an association; if not, it returns
- FL's top-level value, as passed to 'make-fluid' to create FL.
- 'Set-fluid!' assigns the value of the association in the current
- dynamic environment for FL to VALUE, or, if there is no such
- association, it assigns the top-level value of FL to VALUE. Direct
- assignment of fluids is deprecated, however, and may be removed in
- a later release; instead, programmers should use fluids that are
- bound to mutable cells (*note Cells::). 'Fluid-cell-ref' and
- 'fluid-cell-set!' are conveniences for this; they simply call the
- corresponding cell operations after fetching the cell that the
- fluid refers to by using 'fluid'.
- -- procedure: let-fluid fluid value thunk --> values
- -- procedure: let-fluids fluid_{0} value_{0} fluid_{1} value_{1} ...
- thunk --> values
- These dynamically bind their fluid arguments to the corresponding
- value arguments and apply THUNK with the new dynamic environment,
- restoring the old one after THUNK returns and returning the value
- it returns.
- (define $mumble (make-fluid 0))
- (let ((a (fluid $mumble))
- (b (let-fluid $mumble 1
- (lambda () (fluid $mumble))))
- (c (fluid $mumble))
- (d (let-fluid $mumble 2
- (lambda ()
- (let-fluid $mumble 3
- (lambda () (fluid $mumble)))))))
- (list a b c d))
- => (0 1 0 3)
- (let ((note (lambda (when)
- (display when)
- (display ": ")
- (write (fluid $mumble))
- (newline))))
- (note 'initial)
- (let-fluid $mumble 1 (lambda () (note 'let-fluid)))
- (note 'after-let-fluid)
- (let-fluid $mumble 1
- (lambda ()
- (note 'outer-let-fluid)
- (let-fluid $mumble 2 (lambda () (note 'inner-let-fluid)))))
- (note 'after-inner-let-fluid)
- ((call-with-current-continuation
- (lambda (k)
- (lambda ()
- (let-fluid $mumble 1
- (lambda ()
- (note 'let-fluid-within-cont)
- (let-fluid $mumble 2
- (lambda () (note 'inner-let-fluid-within-cont)))
- (k (lambda () (note 'let-fluid-thrown)))))))))
- (note 'after-throw))
- -| initial: 0
- -| let-fluid: 1
- -| after-let-fluid: 0
- -| outer-let-fluid: 1
- -| inner-let-fluid: 2
- -| let-fluid-within-cont: 1
- -| inner-let-fluid-within-cont: 2
- -| let-fluid-thrown: 0
- -| after-throw: 0
- File: scheme48.info, Node: ASCII character encoding, Next: Integer enumerations, Prev: Fluid/dynamic bindings, Up: System features
- 4.1.5 ASCII character encoding
- ------------------------------
- These names are exported by the 'ascii' structure.
- -- procedure: char->ascii char --> ascii-integer
- -- procedure: ascii->char ascii-integer --> character
- These convert characters to and from their integer ASCII encodings.
- 'Char->ascii' and 'ascii->char' are similar to R5RS's
- 'char->integer' and 'integer->char', but they are guaranteed to use
- the ASCII encoding. Scheme48's 'integer->char' and 'char->integer'
- deliberately do not use the ASCII encoding to encourage programmers
- to make use of only what R5RS guarantees.
- (char->ascii #\a) => 97
- (ascii->char 97) => #\a
- -- constant: ascii-limit --> integer
- -- constant: ascii-whitespaces --> ascii-integer-list
- 'Ascii-limit' is an integer that is one greater than the highest
- number that 'char->ascii' may return or 'ascii->char' will accept.
- 'Ascii-whitespaces' is a list of the integer encodings of all
- characters that are considered whitespace: space (32), horizontal
- tab (9), line-feed/newline (10), vertical tab (11), form-feed/page
- (12), and carriage return (13).
- File: scheme48.info, Node: Integer enumerations, Next: Cells, Prev: ASCII character encoding, Up: System features
- 4.1.6 Integer enumerations
- --------------------------
- Scheme48 provides a facility for "integer enumerations", somewhat akin
- to C enums. The names described in this section are exported by the
- 'enumerated' structure.
- *Note:* These enumerations are _not_ compatible with the
- enumerated/finite type facility (*note Enumerated/finite types and
- sets::).
- -- syntax: define-enumeration enumeration-name (enumerand-name ...)
- Defines ENUMERATION-NAME to be a static enumeration. (Note that it
- is _not_ a regular variable. It is actually a macro, though its
- exact syntax is not exposed; it must be exported with the ':syntax'
- type (*note Static type system::).) ENUMERATION-NAME thereafter
- may be used with the enumeration operators described below.
- -- syntax: enum enumeration-name enumerand-name --> enumerand-integer
- -- syntax: components enumeration-name --> component-vector
- 'Enum' expands to the integer value represented symbolically by
- ENUMERAND-NAME in the enumeration ENUMERATION-NAME as defined by
- 'define-enumeration'. 'Components' expands to a literal vector of
- the components in ENUMERATION-NAME as defined by
- 'define-enumeration'. In both cases, ENUMERAND-NAME must be
- written literally as the name of the enumerand; see
- 'name->enumerand' for extracting an enumerand's integer given a
- run-time symbol naming an enumerand.
- -- syntax: enumerand->name enumerand-integer enumeration-name -->
- symbol
- -- syntax: name->enumerand enumerand-name enumeration-name -->
- integer-enumerand
- 'Enumerand->name' expands to a form that evaluates to the symbolic
- name that the integer value of the expression ENUMERAND-INTEGER is
- mapped to by ENUMERATION-NAME as defined by 'define-enumeration'.
- 'Name->enumerand' expands to a form that evaluates to the integer
- value of the enumerand in ENUMERATION-NAME that is represented
- symbolically by the value of the expression ENUMERAND-NAME.
- The 'enum-case' structure provides a handy utility of the same name
- for dispatching on enumerands.
- -- syntax: enum-case
- (enum-case ENUMERATION-NAME KEY
- ((ENUMERAND-NAME ...) BODY)
- ...
- [(else ELSE-BODY)])
- Matches KEY with the clause one of whose names maps in
- ENUMERATION-NAME to the integer value of KEY. KEY must be an
- exact, non-negative integer. If no matching clause is found, and
- ELSE-BODY is present, 'enum-case' will evaluate ELSE-BODY; if
- ELSE-BODY is not present, 'enum-case' will return an unspecific
- value.
- Examples:
- (define-enumeration foo
- (bar
- baz))
- (enum foo bar) => 0
- (enum foo baz) => 1
- (enum-case foo (enum foo bar)
- ((baz) 'x)
- (else 'y))
- => y
- (enum-case foo (enum foo baz)
- ((bar) 'a)
- ((baz) 'b))
- => b
- (enumerand->name 1 foo) => baz
- (name->enumerand 'bar foo) => 0
- (components foo) => #(bar baz)
- File: scheme48.info, Node: Cells, Next: Queues, Prev: Integer enumerations, Up: System features
- 4.1.7 Cells
- -----------
- Scheme48 also provides a simple mutable cell data type from the 'cells'
- structure. It uses them internally for local, lexical variables that
- are assigned, but cells are available still to the rest of the system
- for general use.
- -- procedure: make-cell contents --> cell
- -- procedure: cell? object --> boolean
- -- procedure: cell-ref cell --> value
- -- procedure: cell-set! cell value --> unspecified
- 'Make-cell' creates a new cell with the given contents. 'Cell?' is
- the disjoint type predicate for cells. 'Cell-ref' returns the
- current contents of CELL. 'Cell-set!' assigns the contents of CELL
- to VALUE.
- Examples:
- (define cell (make-cell 42))
- (cell-ref cell) => 42
- (cell? cell) => #t
- (cell-set! cell 'frobozz)
- (cell-ref cell) => frobozz
- File: scheme48.info, Node: Queues, Next: Hash tables, Prev: Cells, Up: System features
- 4.1.8 Queues
- ------------
- The 'queues' structure exports names for procedures that operate on
- simple first-in, first-out queues.
- -- procedure: make-queue --> queue
- -- procedure: queue? object --> boolean
- 'Make-queue' constructs an empty queue. 'Queue?' is the disjoint
- type predicate for queues.
- -- procedure: queue-empty? queue --> boolean
- -- procedure: empty-queue! queue --> unspecified
- 'Queue-empty?' returns '#t' if QUEUE contains zero elements or '#f'
- if it contains some. 'Empty-queue!' removes all elements from
- QUEUE.
- -- procedure: enqueue! queue object --> unspecified
- -- procedure: dequeue! queue --> value
- -- procedure: maybe-dequeue! queue --> value or '#f'
- -- procedure: queue-head queue --> value
- 'Enqueue!' adds OBJECT to QUEUE. 'Dequeue!' removes & returns the
- next object available from QUEUE; if QUEUE is empty, 'dequeue!'
- signals an error. 'Maybe-dequeue!' is like 'dequeue!', but it
- returns '#f' in the case of an absence of any element, rather than
- signalling an error. 'Queue-head' returns the next element
- available from QUEUE without removing it, or it signals an error if
- QUEUE is empty.
- -- procedure: queue-length queue --> integer
- Returns the number of objects in QUEUE.
- -- procedure: on-queue? queue object --> boolean
- -- procedure: delete-from-queue! queue object --> unspecified
- 'On-queue?' returns true if QUEUE contains OBJECT or '#f' if not.
- 'Delete-from-queue!' removes the first occurrence of OBJECT from
- QUEUE that would be dequeued.
- -- procedure: queue->list queue --> list
- -- procedure: list->queue list --> queue
- These convert queues to and from lists of their elements.
- 'Queue->list' returns a list in the order in which its elements
- were added to the queue. 'List->queue' returns a queue that will
- produce elements starting at the head of the list.
- Examples:
- (define q (make-queue))
- (enqueue! q 'foo)
- (enqueue! q 'bar)
- (queue->list q) => (foo bar)
- (on-queue? q 'bar) => #t
- (dequeue! q) => 'foo
- (queue-empty? q) => #f
- (delete-from-queue! queue 'bar)
- (queue-empty? q) => #t
- (enqueue! q 'frobozz)
- (empty-queue! q)
- (queue-empty? q) => #t
- (dequeue! q) error-> empty queue
- Queues are integrated with Scheme48's optimistic concurrency (*note
- Optimistic concurrency::) facilities, in that every procedure exported
- except for 'queue->list' ensures fusible atomicity in operation -- that
- is, every operation except for 'queue->list' ensures that the
- transaction it performs is atomic, and that it may be fused within
- larger atomic transactions, as transactions wrapped within
- 'call-ensuring-atomicity' &c. may be.
- File: scheme48.info, Node: Hash tables, Next: Weak references, Prev: Queues, Up: System features
- 4.1.9 Hash tables
- -----------------
- Scheme48 provides a simple hash table facility in the structure
- 'tables'.
- -- procedure: make-table [hasher] --> table
- -- procedure: make-string-table --> string-table
- -- procedure: make-symbol-table --> symbol-table
- -- procedure: make-integer-table --> integer-table
- Hash table constructors. 'Make-table' creates a table that hashes
- keys either with HASHER, if it is passed to 'make-table', or
- 'default-hash-function', and it compares keys for equality with
- 'eq?', unless they are numbers, in which case it compares with
- 'eqv?'. 'Make-string-table' makes a table whose hash function is
- 'string-hash' and that compares the equality of keys with
- 'string=?'. 'Make-symbol-table' constructs a table that hashes
- symbol keys by converting them to strings and hashing them with
- 'string-hash'; it compares keys' equality by 'eq?'. Tables made by
- 'make-integer-table' hash keys by taking their absolute value, and
- test for key equality with the '=' procedure.
- -- procedure: make-table-maker comparator hasher --> table-maker
- Customized table constructor constructor: this returns a nullary
- procedure that creates a new table that uses COMPARATOR to compare
- keys for equality and HASHER to hash keys.
- -- procedure: table? object --> boolean
- Hash table disjoint type predicate.
- -- procedure: table-ref table key --> value or '#f'
- -- procedure: table-set! table key value --> unspecified
- 'Table-ref' returns the value associated with KEY in TABLE, or '#f'
- if there is no such association. If VALUE is '#f', 'table-set!'
- ensures that there is no longer an association with KEY in TABLE;
- if VALUE is any other value, 'table-set!' creates a new association
- or assigns an existing one in TABLE whose key is KEY and whose
- associated value is VALUE.
- -- procedure: table-walk proc table --> unspecified
- 'Table-walk' applies PROC to the key & value, in that order of
- arguments, of every association in TABLE.
- -- procedure: make-table-immutable! table --> table
- This makes the structure of TABLE immutable, though not its
- contents. 'Table-set!' may not be used with tables that have been
- made immutable.
- -- procedure: default-hash-function value --> integer-hash-code
- -- procedure: string-hash string --> integer-hash-code
- Two built-in hashing functions. 'Default-hash-function' can hash
- any Scheme value that could usefully be used in a 'case' clause.
- 'String-hash' is likely to be fast, as it is implemented as a VM
- primitive. 'String-hash' is the same as what the 'features'
- structure exports under the same name.
- File: scheme48.info, Node: Weak references, Next: Type annotations, Prev: Hash tables, Up: System features
- 4.1.10 Weak references
- ----------------------
- Scheme48 provides an interface to weakly held references in basic weak
- pointers and "populations", or sets whose elements are weakly held. The
- facility is in the structure 'weak'.
- 4.1.10.1 Weak pointers
- ......................
- -- procedure: make-weak-pointer contents --> weak-pointer
- -- procedure: weak-pointer? object --> boolean
- -- procedure: weak-pointer-ref weak-pointer --> value or '#f'
- 'Make-weak-pointer' creates a weak pointer that points to CONTENTS.
- 'Weak-pointer?' is the weak pointer disjoint type predicate.
- 'Weak-pointer-ref' accesses the value contained within
- 'weak-pointer', or returns '#f' if there were no strong references
- to the contents and a garbage collection occurred. Weak pointers
- resemble cells (*note Cells::), except that they are immutable and
- hold their contents weakly, not strongly.
- 4.1.10.2 Populations (weak sets)
- ................................
- -- procedure: make-population --> population
- -- procedure: add-to-population! object population --> unspecified
- -- procedure: population->list population --> list
- -- procedure: walk-population proc population --> unspecified
- 'Make-population' constructs an empty population.
- 'Add-to-population!' adds OBJECT to the population POPULATION.
- 'Population->list' returns a list of the elements of POPULATION.
- Note, though, that this can be dangerous in that it can create
- strong references to the population's contents and potentially leak
- space because of this. 'Walk-population' applies PROC to every
- element in POPULATION.
- File: scheme48.info, Node: Type annotations, Next: Explicit renaming macros, Prev: Weak references, Up: System features
- 4.1.11 Type annotations
- -----------------------
- Scheme48 allows optional type annotations with the 'loophole' special
- form from the 'loopholes' structure.
- -- syntax: loophole type expression --> values
- This is exactly equivalent in semantics to EXPRESSION, except the
- static type analyzer is informed that the whole expression has the
- type TYPE. For details on the form of TYPE, *note Static type
- system::.
- Type annotations can be used for several different purposes:
- * simply to give more information to the static type analyzer;
- * to work as a simple abstract data type facility: passing a type
- name that does not already exist creates a new disjoint value type;
- and
- * to prevent the type system from generating warnings in the rare
- cases where it would do so incorrectly, such as in the
- 'primitive-cwcc', 'primitive-catch', and 'with-continuation'
- devices (to be documented in a later edition of this manual).
- To see an example of the second use, see 'rts/jar-defrecord.scm' in
- Scheme48's source tree.
- *Note:* Type annotations do _not_ damage the safety of Scheme's type
- system. They affect only the static type analyzer, which does not
- change run-time object representations; it only checks type soundness of
- code and generates warnings for programs that would cause run-time type
- errors.
- File: scheme48.info, Node: Explicit renaming macros, Prev: Type annotations, Up: System features
- 4.1.12 Explicit renaming macros
- -------------------------------
- Scheme48 supports a simple low-level macro system based on explicitly
- renaming identifiers to preserve hygiene. The macro system is
- well-integrated with the module system; *note Macros in concert with
- modules::.
- "Explicit renaming" macro transformers operate on simple
- S-expressions extended with "identifiers", which are like symbols but
- contain more information about lexical context. In order to preserve
- that lexical context, transformers must explicitly call a "renamer"
- procedure to produce an identifier with the proper scope. To test
- whether identifiers have the same denotation, transformers are also
- given an identifier comparator.
- The facility provided by Scheme48 is almost identical to the explicit
- renaming macro facility described in [Clinger 91].(1) It differs only
- by the 'transformer' keyword, which is described in the paper but not
- used by Scheme48, and in the annotation of auxiliary names.
- -- syntax: define-syntax name transformer [aux-names]
- Introduces a derived syntax NAME with the given transformer, which
- may be an explicit renaming transformer procedure, a pair whose car
- is such a procedure and whose cdr is a list of auxiliary
- identifiers, or the value of a 'syntax-rules' expression. In the
- first case, the added operand AUX-NAMES may, and usually should
- except in the case of local (non-exported) syntactic bindings, be a
- list of all of the auxiliary top-level identifiers used by the
- macro.
- Explicit renaming transformer procedures are procedures of three
- arguments: an input form, an identifier renamer procedure, and an
- identifier comparator procedure. The input form is the whole form of
- the macro's invocation (including, at the car, the identifier whose
- denotation was the syntactic binding). The identifier renamer accepts
- an identifier as an argument and returns an identifier that is
- hygienically renamed to refer absolutely to the identifier's denotation
- in the environment of the macro's definition, not in the environment of
- the macro's usage. In order to preserve hygiene of syntactic
- transformations, macro transformers must call this renamer procedure for
- any literal identifiers in the output. The renamer procedure is
- referentially transparent; that is, two invocations of it with the same
- arguments in terms of 'eq?' will produce the same results in the sense
- of 'eq?'.
- For example, this simple transformer for a 'swap!' macro is
- incorrect:
- (define-syntax swap!
- (lambda (form rename compare)
- (let ((a (cadr form))
- (b (caddr form)))
- `(LET ((TEMP ,a))
- (SET! ,a ,b)
- (SET! ,b TEMP)))))
- The introduction of the literal identifier 'temp' into the output may
- conflict with one of the input variables if it were to also be named
- 'temp': '(swap! temp foo)' or '(swap! bar temp)' would produce the wrong
- result. Also, the macro would fail in another very strange way if the
- user were to have a local variable named 'let' or 'set!', or it would
- simply produce invalid output if there were no binding of 'let' or
- 'set!' in the environment in which the macro was used. These are basic
- problems of abstraction: the user of the macro should not need to know
- how the macro is internally implemented, notably with a 'temp' variable
- and using the 'let' and 'set!' special forms.
- Instead, the macro must hygienically rename these identifiers using
- the renamer procedure it is given, and it should list the top-level
- identifiers it renames (which cannot otherwise be extracted
- automatically from the macro's definition):
- (define-syntax swap!
- (lambda (form rename compare)
- (let ((a (cadr form))
- (b (caddr form)))
- `(,(rename 'LET) ((,(rename 'TEMP) ,a))
- (,(rename 'SET!) ,a ,b)
- (,(rename 'SET!) ,b ,(rename 'TEMP)))))
- (LET SET!))
- However, some macros are unhygienic by design, i.e. they insert
- identifiers into the output intended to be used in the environment of
- the macro's usage. For example, consider a 'loop' macro that loops
- endlessly, but binds a variable named 'exit' to an escape procedure to
- the continuation of the 'loop' expression, with which the user of the
- macro can escape the loop:
- (define-syntax loop
- (lambda (form rename compare)
- (let ((body (cdr form)))
- `(,(rename 'CALL-WITH-CURRENT-CONTINUATION)
- (,(rename 'LAMBDA) (EXIT) ; Literal, unrenamed EXIT.
- (,(rename 'LET) ,(rename 'LOOP) ()
- ,@body
- (,(rename 'LOOP)))))))
- (CALL-WITH-CURRENT-CONTINUATION LAMBDA LET))
- Note that macros that expand to 'loop' must also be unhygienic; for
- instance, this naïve definition of a 'loop-while' macro is incorrect,
- because it hygienically renames 'exit' automatically by of the
- definition of 'syntax-rules', so the identifier it refers to is not the
- one introduced unhygienically by 'loop':
- (define-syntax loop-while
- (syntax-rules ()
- ((LOOP-WHILE test body ...)
- (LOOP (IF (NOT test)
- (EXIT)) ; Hygienically renamed.
- body ...))))
- Instead, a transformer must be written to not hygienically rename 'exit'
- in the output:
- (define-syntax loop-while
- (lambda (form rename compare)
- (let ((test (cadr form))
- (body (cddr form)))
- `(,(rename 'LOOP)
- (,(rename 'IF) (,(rename 'NOT) ,test)
- (EXIT)) ; Not hygienically renamed.
- ,@body)))
- (LOOP IF NOT))
- To understand the necessity of annotating macros with the list of
- auxiliary names they use, consider the following definition of the
- 'delay' form, which transforms '(delay EXP)' into '(make-promise (lambda
- () EXP))', where 'make-promise' is some non-exported procedure defined
- in the same module as the 'delay' macro:
- (define-syntax delay
- (lambda (form rename compare)
- (let ((exp (cadr form)))
- `(,(rename 'MAKE-PROMISE) (,(rename 'LAMBDA) () ,exp)))))
- This preserves hygiene as necessary, but, while the compiler can know
- whether 'make-promise' is _exported_ or not, it cannot in general
- determine whether 'make-promise' is _local_, i.e. not accessible in any
- way whatsoever, even in macro output, from any other modules. In this
- case, 'make-promise' is _not_ local, but the compiler cannot in general
- know this, and it would be an unnecessarily heavy burden on the
- compiler, the linker, and related code-processing systems to assume that
- all bindings are not local. It is therefore better(2) to annotate such
- definitions with the list of auxiliary names used by the transformer:
- (define-syntax delay
- (lambda (form rename compare)
- (let ((exp (cadr form)))
- `(,(rename 'MAKE-PROMISE) (,(rename 'LAMBDA) () ,exp))))
- (MAKE-PROMISE LAMBDA))
- ---------- Footnotes ----------
- (1) For the sake of avoiding any potential copyright issues, the
- paper is not duplicated here, and instead the author of this manual has
- written the entirety of this section.
- (2) However, the current compiler in Scheme48 does not require this,
- though the static linker does.
- File: scheme48.info, Node: Condition system, Next: Bitwise manipulation, Prev: System features, Up: System facilities
- 4.2 Condition system
- ====================
- As of version 1.3 (different from all older versions), Scheme48 supports
- two different condition systems. One of them, the original one, is a
- simple system where conditions are represented as tagged lists. This
- section documents the original one. The new condition system is [SRFI
- 34, 35], and there is a complicated translation layer between the old
- one, employed by the run-time system, and the new one, which is
- implemented in a layer high above that as a library, but a library which
- is always loaded in the usual development environment. See the [SRFI
- 34, 35] documents for documentation of the new condition system. [SRFI
- 34] is available from the 'exceptions' structure; SRFI 35, from the
- 'conditions' structure.
- *Note:* The condition system changed in Scheme48 version 1.3. While
- the old one is still available, the names of the structures that
- implement it changed. 'Signals' is now 'simple-signals', and
- 'conditions' is now 'simple-conditions'. The structure that 'signals'
- _now_ names implements the same interface, but with [SRFI 34, 35]
- underlying it. The structure that the name 'conditions' _now_
- identifies [SRFI 35]. You will have to update all old code that relied
- on the old 'signals' and 'conditions' structure either by using those
- structures' new names or by invasively modifying all code to use [SRFI
- 34, 35]. Also, the only way to completely elide the use of the SRFIs is
- to evaluate this in an environment with the 'exceptions-internal' and
- 'vm-exceptions' structure open:
- (begin (initialize-vm-exceptions! really-signal-condition)
- ;; INITIALIZE-VM-EXCEPTIONS! returns a very large object,
- ;; which we probably don't want printed at the REPL.
- #t)
- 4.2.1 Signalling, handling, and representing conditions
- -------------------------------------------------------
- Scheme48 provides a simple condition system.(1) "Conditions" are
- objects that describe exceptional situations. Scheme48 keeps a registry
- of "condition types", which just have references to their supertypes.
- Conditions are simple objects that contain only two fields, the type and
- the type-specific data (the "stuff"). Accessor procedures should be
- defined for particular condition types to extract the data contained
- within the 'stuff' fields of instances of of those condition types.
- Condition types are represented as symbols. "Condition handlers" are
- part of the system's dynamic context; they are used to handle
- exceptional situations when conditions are signalled that describe such
- exceptional situations. "Signalling" a condition signals that an
- exceptional situation occurred and invokes the current condition handler
- on the condition.
- Scheme48's condition system is split up into three structures:
- 'simple-signals'
- Exports procedures to signal conditions and construct conditions,
- as well as some utilities for common kinds of conditions.
- 'handle'
- Exports facilities for handling signalled conditions.
- 'simple-conditions'
- The system of representing conditions as objects.
- The 'simple-signals' structure exports these procedures:
- -- procedure: make-condition type-name stuff --> condition
- The condition object constructor.
- -- procedure: signal-condition condition --> values (may not return)
- -- procedure: signal type-name stuff ... --> values (may not return)
- 'Signal-condition' signals the given condition. 'Signal' is a
- convenience atop the common conjunction of 'signal-condition' and
- 'make-condition': it constructs a condition with the given type
- name and stuff, whereafter it signals that condition with
- 'signal-condition'.
- -- procedure: error message irritant ... --> values (may not return)
- -- procedure: warn message irritant ... --> values (may not return)
- -- procedure: syntax-error message irritant ... --> expression (may not
- return)
- -- procedure: call-error message irritant ... --> values (may not
- return)
- -- procedure: note message irritant ... --> values (may not return)
- Conveniences for signalling standard condition types. These
- procedures generally either do not return or return an unspecified
- value, unless specified to by a user of the debugger.
- 'Syntax-error' returns the expression '(quote syntax-error)', if
- the condition handler returns to 'syntax-error' in the first place.
- By convention, the message should be lowercased (i.e. the first
- word should not be capitalized), and it should not end with
- punctuation. The message is typically not a complete sentence.
- For example, these all follow Scheme48's convention:
- argument type error
- wrong number of arguments
- invalid syntax
- ill-typed right-hand side
- out of memory, unable to continue
- These, on the other hand, do not follow the convention and should
- be avoided:
- Argument type error:
- An argument of the wrong type was passed.
- possible type mismatch:
- Luser is an idiot!
- Elaboration on a message is performed usually by wrapping an
- irritant in a descriptive list. For example, one might write:
- (error "invalid argument"
- '(not a pair)
- `(while calling ,frobbotz)
- `(received ,object))
- This might be printed as:
- Error: invalid argument
- (not a pair)
- (while calling #{Procedure 123 (frobbotz in ...)})
- (received #(a b c d))
- The 'handle' structure exports the following procedures:
- -- procedure: with-handler handler thunk --> values
- Sets up HANDLER as the condition handler for the dynamic extent of
- THUNK. HANDLER should be a procedure of two arguments: the
- condition that was signalled and a procedure of zero arguments that
- propagates the condition up to the next dynamically enclosing
- handler. When a condition is signalled, HANDLER is tail-called
- from the point that the condition was signalled at. Note that,
- because HANDLER is tail-called at that point, it will _return_ to
- that point also.
- *Warning:* 'With-handler' is potentially very dangerous. If an
- exception occurs and a condition is raised in the handler, the
- handler itself will be called with that new condition!
- Furthermore, the handler may accidentally return to an unexpecting
- signaller, which can cause very confusing errors. Be careful with
- 'with-handler'; to be perfectly safe, it might be a good idea to
- throw back out to where the handler was initially installed before
- doing anything:
- ((call-with-current-continuation
- (lambda (k)
- (lambda ()
- (with-handler (lambda (c propagate)
- (k (lambda () HANDLER BODY)))
- (lambda () BODY))))))
- -- procedure: ignore-errors thunk --> values or condition
- -- procedure: report-errors-as-warnings thunk message irritant ... -->
- values
- 'Ignore-errors' sets up a condition handler that will return error
- conditions to the point where 'ignore-errors' was called, and
- propagate all other conditions. If no condition is signalled
- during the dynamic extent of THUNK, 'ignore-errors' simply returns
- whatever THUNK returned. 'Report-errors-as-warnings' downgrades
- errors to warnings while executing THUNK. If an error occurs, a
- warning is signalled with the given message, and a list of
- irritants constructed by adding the error condition to the end of
- the list IRRITANT ....
- Finally, the 'simple-conditions' structure defines the condition type
- system. (Note that conditions themselves are constructed only by
- 'make-condition' (and 'signal') from the 'simple-signals' structure.)
- Conditions are very basic values that have only two universally defined
- fields: the type and the stuff. The type is a symbol denoting a
- condition type. The type is specified in the first argument to
- 'make-condition' or 'signal'. The stuff field contains whatever a
- particular condition type stores in conditions of that type. The stuff
- field is always a list; it is created from the arguments after the first
- to 'make-condition' or 'signal'. Condition types are denoted by
- symbols, kept in a global registry that maps condition type names to
- their supertype names.
- -- procedure: define-condition-type name supertype-names -->
- unspecified
- Registers the symbol NAME as a condition type. Its supertypes are
- named in the list SUPERTYPE-NAMES.
- -- procedure: condition-predicate ctype-name --> predicate
- Returns a procedure of one argument that returns '#t' if that
- argument is a condition whose type's name is CTYPE-NAME or '#f' if
- not.
- -- procedure: condition-type condition --> type-name
- -- procedure: condition-stuff condition --> list
- Accessors for the two immutable fields of conditions.
- -- procedure: error? condition --> boolean
- -- procedure: warning? condition --> boolean
- -- procedure: note? condition --> boolean
- -- procedure: syntax-error? condition --> boolean
- -- procedure: call-error? condition --> boolean
- -- procedure: read-error? condition --> boolean
- -- procedure: interrupt? condition --> boolean
- Condition predicates for built-in condition types.
- -- procedure: make-exception opcode reason arguments --> exception
- -- procedure: exception? condition --> boolean
- -- procedure: exception-opcode exception --> integer-opcode
- -- procedure: exception-reason exception --> symbol
- -- procedure: exception-arguments exception --> list
- "Exceptions" represent run-time errors in the Scheme48 VM. They
- contain information about what opcode the VM was executing when it
- happened, what the reason for the exception occurring was, and the
- relevant arguments.
- 4.2.2 Displaying conditions
- ---------------------------
- The 'display-conditions' structure is also relevant in this section.
- -- procedure: display-condition condition port --> unspecified
- Prints CONDITION to PORT for a user to read. For example:
- (display-condition (make-condition 'error
- "Foo bar baz"
- 'quux
- '(zot mumble: frotz))
- (current-output-port))
- -| Error: Foo bar baz
- -| quux
- -| (zot mumble: frotz)
- -- method table: &disclose-condition condition --> disclosed
- Method table (*note Generic dispatch system::) for a generic
- procedure (not exposed) used to translate a condition object into a
- more readable format. *Note Writer::.
- -- procedure: limited-write object port max-depth max-length -->
- unspecified
- A utility for avoiding excessive output: prints OBJECT to PORT, but
- will never print more than MAX-LENGTH of a subobject's components,
- leaving a '---' after the last component, and won't recur further
- down the object graph from the vertex OBJECT beyond MAX-DEPTH,
- instead printing an octothorpe ('#').
- (let ((x (cons #f #f)))
- (set-car! x x)
- (set-cdr! x x)
- (limited-write x (current-output-port) 2 2))
- -| ((# # ---) (# # ---) ---)
- ---------- Footnotes ----------
- (1) Note, however, that Scheme48's condition system is likely to be
- superseded in the near future by [SRFI 34, SRFI 35].
- File: scheme48.info, Node: Bitwise manipulation, Next: Generic dispatch system, Prev: Condition system, Up: System facilities
- 4.3 Bitwise manipulation
- ========================
- Scheme48 provides two structures for bit manipulation: bitwise integer
- operations, the 'bitwise' structure, and homogeneous vectors of bytes
- (integers between 0 and 255, inclusive), the 'byte-vectors' structure.
- 4.3.1 Bitwise integer operations
- --------------------------------
- The 'bitwise' structure exports these procedures:
- -- procedure: bitwise-and integer ... --> integer
- -- procedure: bitwise-ior integer ... --> integer
- -- procedure: bitwise-xor integer ... --> integer
- -- procedure: bitwise-not integer --> integer
- Basic twos-complement bitwise boolean logic operations.
- -- procedure: arithmetic-shift integer count --> integer
- Shifts INTEGER by the given bit count. If COUNT is positive, the
- shift is a left shift; otherwise, it is a right shift.
- 'Arithmetic-shift' preserves INTEGER's sign.
- -- procedure: bit-count integer --> integer
- Returns the number of bits that are set in INTEGER. If INTEGER is
- negative, it is flipped by the bitwise NOT operation before
- counting.
- (bit-count #b11010010) => 4
- 4.3.2 Byte vectors
- ------------------
- The structure 'byte-vectors' exports analogues of regular vector
- procedures for "byte vectors", homogeneous vectors of bytes:
- -- procedure: make-byte-vector length fill --> byte-vector
- -- procedure: byte-vector byte ... --> byte-vector
- -- procedure: byte-vector? object --> boolean
- -- procedure: byte-vector-length byte-vector --> integer
- -- procedure: byte-vector-ref byte-vector index --> byte
- -- procedure: byte-vector-set! byte-vector index byte --> unspecified
- FILL and each BYTE must be bytes, i.e. integers within the
- inclusive range 0 to 255. Note that 'make-byte-vector' is not an
- exact analogue of 'make-vector', because the FILL parameter is
- required.
- Old versions of Scheme48 referred to byte vectors as 'code vectors'
- (since they were used to denote byte code). The 'code-vectors'
- structure exports 'make-code-vector', 'code-vector?',
- 'code-vector-length', 'code-vector-ref', and 'code-vector-set!',
- identical to the analogously named byte vector operations.
- File: scheme48.info, Node: Generic dispatch system, Next: I/O system, Prev: Bitwise manipulation, Up: System facilities
- 4.4 Generic dispatch system
- ===========================
- Scheme48 supports a CLOS-style generic procedure dispatch system, based
- on type predicates. The main interface is exported by 'methods'. The
- internals of the system are exposed by the 'meta-methods' structure, but
- they are not documented here. The generic dispatch system is used in
- Scheme48's writer (*note Writer::) and numeric system.
- "Types" in Scheme48's generic dispatch system are represented using
- type predicates, rather than having every object have a single,
- well-defined 'class.' The naming convention for simple types is to
- prefix the type name with a colon. The types support multiple
- inheritance. Method specificity is determined based on descending order
- of argument importance. That is, given two methods, M & N, such that
- they are both applicable to a given sequence of arguments, and an index
- I into that sequence, such that I is the first index in M's & N's lists
- of argument type specifiers, from left to right, where the type differs:
- if the type for M's argument at I is more specific than the
- corresponding type in N's specifiers, M is considered to be more
- specific than N, even if the remaining argument type specifiers in N are
- more specific.
- -- syntax: define-simple-type name (supertype ...) predicate
- Defines NAME to be a "simple type" with the given predicate and the
- given supertypes.
- -- procedure: singleton value --> simple-type
- Creates a "singleton type" that matches only VALUE.
- -- syntax: define-generic proc-name method-table-name [prototype]
- Defines PROC-NAME to be a "generic procedure" that, when invoked,
- will dispatch on its arguments via the "method table" that
- METHOD-TABLE-NAME is defined to be and apply the most specific
- method it can determine defined in the METHOD-TABLE-NAME method
- table to its arguments. The convention for naming variables that
- will be bound to method tables is to add an ampersand to the front
- of the name. PROTOTYPE is a suggestion for what method prototypes
- should follow the shape of, but it is currently ignored.
- -- syntax: define-method method-table prototype body
- Adds a "method" to METHOD-TABLE, which is usually one defined by
- 'define-generic'.(1) PROTOTYPE should be a list whose elements may
- be either identifiers, in which case that parameter is not used for
- dispatching, or lists of two elements, the 'car' of which is the
- parameter name and the 'cadr' of which should evaluate to the type
- on which to dispatch. As in many generic dispatch systems of
- similar designs, methods may invoke the next-most-specific method.
- By default, the name 'next-method' is bound in BODY to a nullary
- procedure that calls the next-most-specific method. The name of
- this procedure may be specified by the user by putting the sequence
- '"next" NEXT-METHOD-NAME' in PROTOTYPE, in which case it will be
- NEXT-METHOD-NAME that is bound to that procedure. For example:
- (define-method &frob ((foo :bar) "next" frobozz)
- (if (mumble? foo)
- (frobozz) ; Invoke the next method.
- (yargh blargle foo)))
- A number of simple types are already defined & exported by the
- 'methods' structure. Entries are listed as 'TYPE-NAME <- (SUPERTYPE
- ...), PREDICATE'
- * ':values <- (), (lambda (x) #t)' -- Abstract supertype of all
- run-time values
- * ':value <- (:values), (lambda (x) #t)' -- Abstract supertype of all
- first-class values
- * ':zero <- (:values), (lambda (x) #f)' -- Type that no objects
- satisfy
- * ':number <- (:value), number?'
- * ':complex <- (:number), complex?' -- (This happens to be equivalent
- to ':number'.)
- * ':real <- (:complex), real?'
- * ':rational <- (:real), rational?'
- * ':integer <- (:rational), integer?'
- * ':exact-integer <- (:integer), (lambda (x) (and (integer? x)
- (exact? x)))'
- * ':boolean <- (:value), boolean?'
- * ':symbol <- (:value), symbol?'
- * ':char <- (:value), char?'
- * ':null <- (:value), null?'
- * ':pair <- (:value), pair?'
- * ':vector <- (:value), vector?'
- * ':string <- (:value), string?'
- * ':procedure <- (:value), procedure?'
- * ':input-port <- (:value), input-port?'
- * ':output-port <- (:value), output-port?'
- * ':eof-object <- (:value), eof-object?'
- * ':record <- (:value), record?'
- ---------- Footnotes ----------
- (1) There is an internal interface, a sort of meta-object protocol,
- to the method dispatch system, but it is not yet documented.
- File: scheme48.info, Node: I/O system, Next: Reader & writer, Prev: Generic dispatch system, Up: System facilities
- 4.5 I/O system
- ==============
- Scheme48 supports a sophisticated, non-blocking, user-extensible I/O
- system untied to any particular operating system's I/O facilities. It
- is based in three levels: channels, ports, and the facilities already
- built with both ports and channels in Scheme48, such as buffering.
- * Menu:
- * Ports:: Abstract & generalized I/O objects.
- * Programmatic ports:: Designing custom ports.
- * Miscellaneous I/O internals:: Various internal I/O system routines.
- * Channels:: Low-level interface to OS facilities.
- * Channel ports:: Ports built upon channels.
- File: scheme48.info, Node: Ports, Next: Programmatic ports, Up: I/O system
- 4.5.1 Ports
- -----------
- While channels provide the low-level interface directly to the OS's I/O
- facilities, "ports" provide a more abstract & generalized mechanism for
- I/O transmission. Rather than being specific to channels or being
- themselves primitive I/O devices, ports are functionally parameterized.
- This section describes the usual I/O operations on ports. The next
- section describes the programmatic port parameterization mechanism, and
- the section following that describes the most commonly used built-in
- port abstraction, ports atop channels.
- 4.5.1.1 Port operations
- .......................
- The following names are exported by the 'i/o' structure.
- -- procedure: input-port? value --> boolean
- -- procedure: output-port? value --> boolean
- These return '#t' if their argument is both a port and either an
- input port or output port, respectively, or '#f' if neither
- condition is true.
- -- procedure: close-input-port port --> unspecified
- -- procedure: close-output-port port --> unspecified
- Closes PORT, which must be an input port or an output port,
- respectively.
- -- procedure: char-ready? [port] --> boolean
- -- procedure: output-port-ready? port --> boolean
- 'Char-ready?' returns a true value if there is a character ready to
- be read from PORT and '#f' if there is no character ready. PORT
- defaults to the current input port if absent; see below on current
- ports. 'Output-port-ready?' returns a true value if PORT is ready
- to receive a single written character and '#f' if not.
- -- procedure: read-block block start count port [wait?] --> count-read
- or EOF
- -- procedure: write-block block start count port --> count-written
- -- procedure: write-string string port --> char-count-written
- 'Read-block' attempts to read COUNT elements from PORT into BLOCK,
- which may be a string or a byte vector, starting at START. If
- fewer than COUNT characters or bytes are available to read from
- PORT, and WAIT? is a true value or absent, 'read-block' will wait
- until COUNT characters are available and read into BLOCK; if WAIT?
- is '#f', 'read-block' immediately returns. 'Read-block' returns
- the number of elements read into BLOCK, or an end of file object if
- the stream's end is immediately encountered. 'Write-block' writes
- COUNT elements from BLOCK, which may be a string or a byte vector,
- starting at START to PORT. 'Write-string' is a convenience atop
- 'write-block' for writing the entirety of a string to a port.
- -- procedure: newline [port] --> unspecified
- Writes a newline character or character sequence to the output port
- PORT. PORT defaults to the current output port; see below on
- current ports.
- -- procedure: disclose-port port --> disclosed
- Returns a disclosed representation of PORT; *note Writer::.
- -- procedure: force-output port --> unspecified
- Forces all buffered output in the output port PORT to be sent.
- -- procedure: make-null-output-port --> output-port
- Returns an output port that will ignore any output it receives.
- 4.5.1.2 Current ports
- .....................
- Scheme48 keeps in its dynamic environment (*note Fluid/dynamic
- bindings::) a set of 'current' ports. These include R5RS's current
- input and output ports, as well as ports for general noise produced by
- the system, and ports for where error messages are printed. These
- procedures are exported by the 'i/o' structure.
- -- procedure: current-input-port --> input-port
- -- procedure: current-output-port --> output-port
- -- procedure: current-noise-port --> output-port
- -- procedure: current-error-port --> output-port
- These return the values in the current dynamic environment of the
- respective ports. 'Current-input-port' and 'current-output-port'
- are also exported by the 'scheme' structure.
- -- procedure: input-port-option arguments --> input-port
- -- procedure: output-port-option arguments --> output-port
- These are utilities for retrieving optional input and output port
- arguments from rest argument lists, defaulting to the current input
- or output ports. For example, assuming the newline character
- sequence is simply '#\newline', 'newline' might be written as:
- (define (newline . maybe-port)
- (write-char #\newline (output-port-option maybe-port)))
- -- procedure: silently thunk --> values
- This stifles output from the current noise port in the dynamic
- extent of THUNK, which is applied to zero arguments. 'Silently'
- returns the values that THUNK returns.
- -- procedure: with-current-ports input output error thunk --> values
- 'With-current-ports' dynamically binds the current input, output,
- and error ports to INPUT, OUTPUT, and ERROR, respectively, in the
- dynamic extent of THUNK, which is applied to zero arguments. The
- current noise port is also bound to ERROR. 'With-current-ports'
- returns the values that THUNK returns.
- Similarly to 'with-current-ports', the 'i/o-internal' structure also
- exports these procedures:
- -- procedure: call-with-current-input-port port thunk --> values
- -- procedure: call-with-current-output-port port thunk --> values
- -- procedure: call-with-current-noise-port port thunk --> values
- These bind individual current ports for the dynamic extent of each
- THUNK, which is applied to zero arguments. These all return the
- values that THUNK returns.
- File: scheme48.info, Node: Programmatic ports, Next: Miscellaneous I/O internals, Prev: Ports, Up: I/O system
- 4.5.2 Programmatic ports
- ------------------------
- Ports are user-extensible; all primitive port operations on them --
- 'read-char', 'write-block', &c. -- are completely generalized.
- Abstractions for buffered ports are also available.
- * Menu:
- * Port data type::
- * Port handlers::
- * Buffered ports & handlers::
- File: scheme48.info, Node: Port data type, Next: Port handlers, Up: Programmatic ports
- 4.5.2.1 Port data type
- ......................
- The 'ports' structure defines the basis of the port data type and
- exports the following procedures.
- -- procedure: make-port handler status lock data buffer index limit
- pending-eof? --> port
- Port constructor. The arguments are all the fields of ports, which
- are described below. Note that 'make-port' is rarely called
- directly; usually one will use one of the buffered port
- constructors instead.
- -- procedure: port-handler port --> port-handler
- -- procedure: port-buffer port --> buffer or '#f'
- -- procedure: port-lock port --> value
- -- procedure: port-status port --> integer-status
- -- procedure: port-data port --> value
- -- procedure: port-index port --> integer or '#f'
- -- procedure: port-limit port --> integer or '#f'
- -- procedure: port-pending-eof? port --> boolean
- Accessors for the port fields:
- 'handler'
- The handler is the functional parameterization mechanism: it
- provides all the port's operations, such as reading/writing
- blocks, disclosing (*note Writer::) the port, closing the
- port, &c. *Note Port handlers::.
- 'buffer'
- The buffer is used for buffered ports, where it is a byte
- vector (*note Bitwise manipulation::). It may be any value
- for unbuffered ports.
- 'lock'
- This misnamed field was originally used for a mutual exclusion
- lock, before optimistic concurrency was made the native
- synchronization mechanism in Scheme48. It is now used as a
- 'timestamp' for buffered ports: it is provisionally written to
- with a unique value when a thread resets the 'index' to reüse
- the buffer, and it is provisionally read from when reading
- from the buffer. In this way, if the buffer is reset while
- another thread is reading from it, the other thread's proposal
- is invalidated by the different value in memory than what was
- there when it logged the old timestamp in its proposal.
- 'status'
- A mask from the 'port-status-options' enumeration; *note
- Miscellaneous I/O internals::.
- 'data'
- Arbitrary data for particular kinds of ports. For example,
- for a port that tracks line & column information (*note I/O
- extensions::), this might be a record containing the
- underlying port, the line number, and the column number.
- 'index'
- The current index into a buffered port's buffer. If the port
- is not buffered, this is '#f'.
- 'limit'
- The limit of the 'index' field for a buffered port's buffer.
- When the 'index' field is equal to the 'limit' field, the
- buffer is full. If the port is not buffered, this is '#f'.
- 'pending-eof?'
- For output ports, this is a boolean flag indicating whether
- the buffer has been forced to output recently. For input
- ports, this is a boolean flag indicating whether an end of
- file is pending after reading through the current buffer.
- -- procedure: set-port-lock! port value --> unspecified
- -- procedure: set-port-status! port status --> unspecified
- -- procedure: set-port-data! port data --> unspecified
- -- procedure: set-port-index! port index --> unspecified
- -- procedure: set-port-limit! port index --> unspecified
- -- procedure: set-port-pending-eof?! port pending-eof? --> unspecified
- These assign respective fields of ports. The 'buffer' and
- 'handler' fields, however, are immutable.
- -- procedure: provisional-port-handler port --> port-handler
- -- procedure: provisional-port-lock port --> value
- -- procedure: provisional-port-status port --> integer-status
- -- procedure: provisional-port-data port --> value
- -- procedure: provisional-port-index port --> integer or '#f'
- -- procedure: provisional-port-limit port --> integer or '#f'
- -- procedure: provisional-port-pending-eof? port --> boolean
- -- procedure: provisional-set-port-lock! port value --> unspecified
- -- procedure: provisional-set-port-status! port status --> unspecified
- -- procedure: provisional-set-port-data! port data --> unspecified
- -- procedure: provisional-set-port-index! port index --> unspecified
- -- procedure: provisional-set-port-limit! port index --> unspecified
- -- procedure: provisional-set-port-pending-eof?! port pending-eof? -->
- unspecified
- Provisional versions of the above port accessors & modifiers; that
- is, accessors & modifiers that log in the current proposal, if
- there is one.
- File: scheme48.info, Node: Port handlers, Next: Buffered ports & handlers, Prev: Port data type, Up: Programmatic ports
- 4.5.2.2 Port handlers
- .....................
- "Port handlers" store a port's specific operations for the general port
- operations, such as block reads and writes, buffer flushing, &c. Port
- handler constructors, including 'make-port-handler' & the buffered port
- handlers in the next section, are available from the 'i/o-internal'
- structure.
- -- procedure: make-port-handler discloser closer char-reader/writer
- block-reader/writer readiness-tester buffer-forcer -->
- port-handler
- Basic port handler constructor. The arguments are used for the
- port handler fields. Each field contains a procedure. The
- expected semantics of each procedure depend on whether the port is
- for input or output. Input ports do not use the 'buffer-forcer'
- field. The first two fields are independent of the type of port:
- discloser port -> disclosed
- Returns a disclosed representation of the port, i.e. a list
- whose 'car' is the 'type name' of this handler (usually with a
- suffix of either '-input-port' or '-output-port') followed by
- a list of all of the components to be printed; *note Writer::.
- closer port -> ignored
- Closes PORT. This operation corresponds with the
- 'close-input-port' & 'close-output-port' procedures.
- For input ports, the remaining fields are:
- char-reader port consume? -> char
- Reads a single character from PORT. If CONSUME? is true, the
- character should be consumed from PORT; if CONSUME? is '#f',
- however, the character should be left in PORT's input stream.
- If CONSUME? is true, this operation corresponds with
- 'read-char'; if it is '#f', this operation corresponds with
- 'peek-char'.
- block-reader port block start count wait? -> count-written or EOF
- Attempts to read COUNT characters from PORT's input stream
- into the string or byte vector BLOCK, starting at START. In
- the case that an insufficient number of characters is
- available, if WAIT? is true, the procedure should wait until
- all of the wanted characters are available; otherwise, if
- WAIT? is '#f', the block reader should immediately return. In
- either case, it returns the number of characters that were
- read into BLOCK, or an end of file object if it immediately
- reached the end of the stream. Buffered ports will typically
- just copy elements from the buffer into BLOCK, rather than
- reading from any internal I/O channel in PORT. This operation
- corresponds with 'read-block'.
- readiness-tester port -> boolean
- Returns a true value if there is a character available to be
- read in PORT or '#f' if not. This operation corresponds with
- the 'char-ready?' procedure.
- For output ports, the remaining fields are:
- char-writer port char -> ignored
- Writes the single character CHAR to PORT. This operation
- corresponds with 'write-char'.
- block-writer port block start count -> count-written
- Writes COUNT characters to PORT from BLOCK, starting at START.
- BLOCK may be a string or a byte vector. This will usually
- involve copying contents of BLOCK to PORT's buffer, if it is
- buffered. This operation corresponds with 'write-block'.
- readiness-tester port -> boolean
- Returns a true value if PORT is ready to receive a character
- and '#f' if not.
- buffer-forcer port necessary? -> ignored
- For buffered ports, this is intended to force all buffered
- output to the actual internal I/O channel of PORT. NECESSARY?
- tells whether or not it is absolutely necessary to force all
- the output immediately; if it is '#t', the buffer forcer is
- required to force all output in the buffer before it returns.
- If NECESSARY? is '#f', not only may it just register an I/O
- transaction without waiting for it to complete, but it also
- should _not_ signal an error if PORT is already closed. For
- unbuffered ports, this operation need not do anything at all.
- File: scheme48.info, Node: Buffered ports & handlers, Prev: Port handlers, Up: Programmatic ports
- 4.5.2.3 Buffered ports & handlers
- .................................
- Along with bare port handlers, Scheme48 provides conveniences for many
- patterns of buffered ports & port handlers. These names are exported by
- the 'i/o-internal' structure. Buffered ports are integrated with
- Scheme48's optimistic concurrency (*note Optimistic concurrency::)
- facilities.
- *Note:* Although internally buffered ports are integrated with
- optimistic concurrency, operations on buffered ports, like operations on
- channels, cannot be reliably fusibly atomic.
- -- procedure: make-buffered-input-port handler data buffer index limit
- --> input-port
- -- procedure: make-buffered-output-port handler data buffer index limit
- --> output-port
- Constructors for buffered ports. HANDLER is the port's handler,
- which is usually constructed with one of the buffered port handler
- constructors (see below). DATA is arbitrary data to go in the
- port's 'data' field. BUFFER is a byte vector whose length is
- greater than or equal to both INDEX & LIMIT. INDEX is the initial
- index into BUFFER to go in the port's 'index' field. LIMIT is the
- limit in the port's buffer, to go into the port's 'limit' field;
- nothing will be written into BUFFER at or past LIMIT.
- -- procedure: make-unbuffered-input-port handler data --> input-port
- -- procedure: make-unbuffered-output-port handler data --> output-port
- Conveniences for ports that are explicitly _not_ buffered. Only
- the relevant fields are passed; all fields pertaining to buffering
- are initialized with '#f'.
- -- procedure: make-buffered-input-port-handler discloser closer
- buffer-filler readiness-tester --> port-handler
- This creates a port handler for buffered input ports. The
- arguments are as follows:
- discloser port-data -> disclosed
- closer port-data -> ignored
- DISCLOSER & CLOSER are like the similarly named regular port
- handler fields, but they are applied directly to the port's
- data, not to the port itself.
- buffer-filler port wait? -> committed?
- Used to fill PORT's buffer when it no longer has contents from
- which to read in its current buffer. WAIT? is a boolean flag,
- '#t' if the operation should wait until the I/O transaction
- necessary to fill the buffer completes, or '#f' if it may
- simply initiate an I/O transaction but not wait until it
- completes (e.g., use 'channel-maybe-commit-and-read', but not
- wait on the condition variable passed to
- 'channel-maybe-commit-and-read'). BUFFER-FILLER is called
- with a fresh proposal in place, and it is the responsibility
- of BUFFER-FILLER to commit it. It returns a boolean flag
- denoting whether the proposal was committed. The last call in
- BUFFER-FILLER is usually either '(maybe-commit)' or a call to
- a procedure that causes that effect (e.g., one of the
- operation on condition variables that commits the current
- proposal. *Note condition variables: Higher-level
- synchronization.)
- readiness-tester port -> [committed? ready?]
- Called when 'char-ready?' is applied to PORT and the buffer of
- PORT is empty. Like BUFFER-FILLER, READINESS-TESTER is
- applied with a fresh proposal in place, which it should
- attempt to commit. READINESS-TESTER should return two values,
- each a boolean flag: the first denotes whether or not the
- current proposal was successfully committed, and, if it was
- successful, whether or not a character is ready.
- -- procedure: make-buffered-output-port-handler discloser
- buffer-emptier readiness-tester --> port-handler
- This creates a port handler for buffered output ports. DISCLOSER &
- CLOSER are as with buffered input ports. The remaining fields are
- as follows:
- buffer-emptier port necessary? -> committed?
- BUFFER-EMPTIER is used when PORT's buffer is full and needs to
- be emptied. It is called with a fresh proposal in place. It
- should reset PORT's 'index' field, call 'note-buffer-reuse!'
- to invalidate other threads' transactions on the recycled
- buffer, and attempt to commit the new proposal installed. It
- returns a boolean flag indicating whether or not the commit
- succeeded.
- readiness-tester port -> [committed? ready?]
- READINESS-TESTER is applied to PORT when its buffer is full
- (i.e. its 'index' & 'limit' fields are equal) and
- 'output-port-ready?' is applied to PORT. After performing the
- test, it should attempt to commit the current proposal and
- then return two values: whether it succeeded in committing the
- current proposal, and, if it was successful, whether or not a
- character is ready to be outputted.
- -- constant: default-buffer-size --> integer
- The default size for port buffers. This happens to be 4096 in the
- current version of Scheme48.
- -- procedure: note-buffer-reuse! port --> unspecified
- -- procedure: check-buffer-timestamp! port --> unspecified
- These are used to signal the resetting of a buffer between multiple
- threads. 'Note-buffer-reuse!' is called -- in the case of an
- output port -- when a buffer fills up, is emptied, and flushed; or
- -- in the case of an input port -- when a buffer is emptied and
- needs to be refilled. 'Note-buffer-reuse!' logs in the current
- proposal a fresh value to store in PORT. When that proposal is
- committed, this fresh value is stored in the port. Other threads
- that were using PORT's buffer call 'check-buffer-timestamp!', which
- logs a read in the current proposal. If another thread commits a
- buffer reüse to memory, that read will be invalidated, invalidating
- the whole transaction.
- File: scheme48.info, Node: Miscellaneous I/O internals, Next: Channels, Prev: Programmatic ports, Up: I/O system
- 4.5.3 Miscellaneous I/O internals
- ---------------------------------
- All of these but 'port-status-options' are exported by the
- 'i/o-internal' structure; the 'port-status-options' enumeration is
- exported by the 'architecture' structure, but it deserves mention in
- this section.
- -- enumeration: port-status-options
- (define-enumeration port-status-options
- (input
- output
- open-for-input
- open-for-output))
- Enumeration of indices into a port's 'status' field bit set.
- -- procedure: open-input-port? port --> boolean
- -- procedure: open-output-port? port --> boolean
- These return true values if PORT is both an input or output port,
- respectively, and open.
- -- constant: open-input-port-status --> integer-status
- -- constant: open-output-port-status --> integer-status
- The bitwise masks of enumerands from the 'port-status-options'
- enumeration signifying an open input or output port, respectively.
- -- procedure: make-input-port-closed! port --> unspecified
- -- procedure: make-output-port-closed! port --> unspecified
- These set the status of PORT, which must be an input or output
- port, respectively, to indicate that it is closed.
- -- procedure: eof-object --> eof-object
- Returns the EOF object token. This is the only value that will
- answer true to R5RS's 'eof-object?' predicate.
- -- procedure: force-output-if-open port --> unspecified
- This forces PORT's output if it is an open output port, and does
- not block.
- -- procedure: periodically-force-output! port --> unspecified
- -- procedure: periodically-flushed-ports --> port-list
- 'Periodically-force-output!' registers PORT to be forced
- periodically. Only a weak reference to PORT in this registry is
- held, however, so this cannot cause accidental space leaks.
- 'Periodically-flushed-ports' returns a list of all ports in this
- registry. Note that the returned list holds strong references to
- all of its elements. 'Periodically-flushed-ports' does not permit
- thread context switches, or interrupts of any sort, while it runs.
- File: scheme48.info, Node: Channels, Next: Channel ports, Prev: Miscellaneous I/O internals, Up: I/O system
- 4.5.4 Channels
- --------------
- "Channels" represent the OS's native I/O transmission channels. On
- Unix, channels are essentially boxed file descriptors, for example. The
- only operations on channels are block reads & writes. Blocks in this
- sense may be either strings or byte vectors (*note Bitwise
- manipulation::).
- 4.5.4.1 Low-level channel operations
- ....................................
- The low-level base of the interface to channels described here is
- exported from the 'channels' structure.
- -- procedure: channel? --> boolean
- Disjoint type predicate for channels.
- -- procedure: channel-id channel --> value
- -- procedure: channel-status channel --> integer-enumerand
- -- procedure: channel-os-index channel --> integer
- 'Channel-id' returns CHANNEL's id. The id is some identifying
- characteristic of channels. For example, file channels' ids are
- usually the corresponding filenames; channels such as the standard
- input, output, or error output channels have names like '"standard
- input"' and '"standard output"'. 'Channel-status' returns the
- current status of CHANNEL; see the 'channel-status-option'
- enumeration below. 'Channel-os-index' returns the OS-specific
- integer index of CHANNEL. On Unix, for example, this is the
- channel's file descriptor.
- -- procedure: open-channel filename option close-silently? --> channel
- 'Open-channel' opens a channel for a file given its filename.
- OPTION specifies what type of channel this is; see the
- 'channel-status-option' enumeration below. CLOSE-SILENTLY? is a
- boolean that specifies whether a message should be printed (on
- Unix, to 'stderr') when the resulting channel is closed after a
- garbage collector finds it unreachable.
- -- procedure: close-channel channel --> unspecified
- Closes CHANNEL after aborting any potential pending I/O
- transactions it may have been involved with.
- -- procedure: channel-ready? channel --> boolean
- If CHANNEL is an input channel: returns '#t' if there is input
- ready to be read from CHANNEL or '#f' if not; if CHANNEL is an
- output channel: returns '#t' if a write would immediately take
- place upon calling 'channel-maybe-write', i.e.
- 'channel-maybe-write' would not return '#f', or '#f' if not.
- -- procedure: channel-maybe-read channel buffer start-index octet-count
- wait? --> octet count read, error status cell, EOF object, or
- '#f'
- -- procedure: channel-maybe-write channel buffer start-index
- octet-count --> octet count written, error status cell, or
- '#f'
- -- procedure: channel-abort channel --> unspecified
- 'Channel-maybe-read' attempts to read OCTET-COUNT octets from
- CHANNEL into BUFFER, starting at START-INDEX. If a low-level I/O
- error occurs, it returns a cell containing a token given by the
- operating system indicating what kind of error occurred. If WAIT?
- is '#t', and CHANNEL is not ready to be read from, CHANNEL is
- registered for the VM's event polling mechanism, and
- 'channel-maybe-read' returns '#f'. Otherwise, it returns either
- the number of octets read, or an EOF object if CHANNEL was was at
- the end.
- 'Channel-maybe-write' attempts to write OCTET-COUNT octets to
- CHANNEL from BUFFER, starting at START-INDEX. If a low-level I/O
- error occurs, it returns a cell indicating a token given by the
- operating system indicating what kind of error occurred. If no
- such low-level error occurs, it registers CHANNEL for the VM's
- event polling mechanism and returns '#f' iff zero octets were
- immediately written or the number of octets immediately written if
- any were.
- 'Channel-abort' aborts any pending operation registered for the
- VM's event polling mechanism.
- -- procedure: open-channels-list --> channel-list
- Returns a list of all open channels in order of the 'os-index'
- field.
- -- enumeration: channel-status-option
- (define-enumeration channel-status-option
- (closed
- input
- output
- special-input
- special-output))
- Enumeration for a channel's status. The 'closed' enumerand is used
- only after a channel has been closed. Note that this is _not_
- suitable for a bit mask; that is, one may choose exactly one of the
- enumerands, not use a bit mask of status options. For example, to
- open a file 'frob' for input that one wishes the garbage collector
- to be silent about on closing it:
- (open-channel "frob"
- (enum channel-status-option input)
- #t)
- => #{Input-channel "frob"}
- 4.5.4.2 Higher-level channel operations
- .......................................
- More convenient abstractions for operating on channels, based on
- condition variables (*note Higher-level synchronization::), are provided
- from the 'channel-i/o' structure. They are integrated with Scheme48's
- optimistic concurrency (*note Optimistic concurrency::) facilities.
- *Note:* Transactions on channels can _not_ be atomic in the sense of
- optimistic concurrency. Since they involve communication with the
- outside world, they are irrevocable transactions, and thus an
- invalidated proposal cannot retract the transaction on the channel.
- -- procedure: channel-maybe-commit-and-read channel buffer start-index
- octet-count condvar wait? --> committed?
- -- procedure: channel-maybe-commit-and-write channel buffer start-index
- octet-count condvar --> committed?
- These attempt to commit the current proposal. If they fail, they
- immediately return '#f'; otherwise, they proceed, and return '#t'.
- If the commit succeeded, these procedures attempt an I/O
- transaction, without blocking. 'Channel-maybe-commit-and-read'
- attempts to read OCTET-COUNT octets into BUFFER, starting at
- START-INDEX, from CHANNEL. 'Channel-maybe-commit-and-write'
- attempts to write OCTET-COUNT octets from BUFFER, starting at
- START-INDEX, to CHANNEL. CONDVAR is noted as waiting for the
- completion of the I/O transaction. When the I/O transaction
- finally completes -- in the case of a read, there are octets ready
- to be read into BUFFER from CHANNEL or the end of the file was
- struck; in the case of a write, CHANNEL is ready to receive some
- octets --, CONDVAR is set to the result of the I/O transaction: the
- number of octets read, an I/O error condition, or an EOF object,
- for reads; and the number of octets written or an I/O error
- condition, for writes.
- -- procedure: channel-maybe-commit-and-close channel closer -->
- committed?
- Attempts to commit the current proposal; if successful, this aborts
- any wait on CHANNEL, sets the result of any condvars waiting on
- CHANNEL to the EOF object, closes CHANNEL by applying CLOSER to
- CHANNEL (in theory, CLOSER could be anything; usually, however, it
- is 'close-channel' from the 'channels' structure or some wrapper
- around it), and returns '#t'. If the commit failed,
- 'channel-maybe-commit-and-close' immediately returns '#f'.
- -- procedure: channel-write channel buffer start-index octet-count -->
- octet-count-written
- Atomically attempts to write OCTET-COUNT octets to CHANNEL from
- BUFFER, starting at START-INDEX in BUFFER. If no I/O transaction
- immediately occurs -- what would result in 'channel-maybe-write'
- returning '#f' --, 'channel-write' blocks until something does
- happen. It returns the number of octets written to CHANNEL.
- -- procedure: wait-for-channel channel condvar --> unspecified
- Registers CONDVAR so that it will be set to the result of some
- prior I/O transaction when some I/O event regarding CHANNEL occurs.
- (Contrary to the name, this does not actually wait or block. One
- must still use 'maybe-commit-and-wait-for-condvar' on CONDVAR;
- *note condition variables: Higher-level synchronization.) This is
- useful primarily in conjunction with calling foreign I/O routines
- that register channels with the VM's event polling system.
- *Note:* 'wait-for-channel' must be called with interrupts disabled.
- File: scheme48.info, Node: Channel ports, Prev: Channels, Up: I/O system
- 4.5.5 Channel ports
- -------------------
- Built-in to Scheme48 are ports made atop channels. These are what are
- created by R5RS's standard file operations. The following names are
- exported by the 'channel-ports' structure.
- -- procedure: call-with-input-file filename receiver --> values
- -- procedure: call-with-output-file filename receiver --> values
- -- procedure: with-input-from-file filename thunk --> values
- -- procedure: with-output-to-file filename thunk --> values
- -- procedure: open-input-file filename --> input-port
- -- procedure: open-output-file filename --> output-port
- Standard R5RS file I/O operations. (These are also exported by the
- 'scheme' structure.) The 'call-with-...put-file' operations open
- the specified type of port and apply RECEIVER to it; after RECEIVER
- returns normally (i.e. nothing is done if there is a throw out of
- RECEIVER), they close the port and return the values that RECEIVER
- returned. 'With-input-from-file' & 'with-output-to-file' do
- similarly, but, rather than applying THUNK to the port, they
- dynamically bind the current input & output ports, respectively, to
- the newly opened ports. 'Call-with-input-file',
- 'call-with-output-file', 'with-input-from-file', and
- 'with-output-to-file' return the values that THUNK returns.
- 'Open-input-file' & 'open-output-file' just open input & output
- ports; users of these operations must close them manually.
- -- procedure: input-channel->port channel [buffer-size] --> port
- -- procedure: output-channel->port channel [buffer-size] --> port
- These create input & output ports atop the given channels and
- optional buffer sizes. The default buffer size is 4096 bytes.
- -- procedure: input-channel+closer->port channel closer [buffer-size]
- --> port
- -- procedure: output-channel+closer->port channel closer [buffer-size]
- --> port
- Similarly, these create input & output ports atop the given
- channels and optional buffer sizes, but they allow for extra
- cleanup when the resulting ports are closed.
- -- procedure: port->channel port --> channel or '#f'
- If PORT is a port created by the system's channel ports facility,
- 'port->channel' returns the channel it was created atop; otherwise
- 'port->channel' returns '#f'.
- -- procedure: force-channel-output-ports! --> unspecified
- This attempts to force as much output as possible from all of the
- ports based on channels. This is used by Scheme48's POSIX
- libraries before forking the current process.
- File: scheme48.info, Node: Reader & writer, Next: Records, Prev: I/O system, Up: System facilities
- 4.6 Reader & writer
- ===================
- Scheme48 has simple S-expression reader & writer libraries, with some
- facilities beyond R5RS's 'read' & 'write' procedures.
- * Menu:
- * Reader::
- * Writer::
- File: scheme48.info, Node: Reader, Next: Writer, Up: Reader & writer
- 4.6.1 Reader
- ------------
- Scheme48's reader facility is exported by the 'reading' structure. The
- 'read' binding thereby exported is identical to that of the 'scheme'
- structure, which is the binding that R5RS specifies under the name
- 'read'.
- -- procedure: read [port] --> readable-value
- Reads a single S-expression from PORT, whose default value is the
- current input port. If the end of the stream is encountered before
- the beginning of an S-expression, 'read' will return an EOF object.
- It will signal a read error if text read from PORT does not
- constitute a complete, well-formed S-expression.
- -- procedure: define-sharp-macro char proc --> unspecified
- Defines a sharp/pound/hash/octothorpe ('#') reader macro. The next
- time the reader is invoked, if it encounters an octothorpe/sharp
- followed by CHAR, it applies PROC to CHAR and the input port being
- read from. CHAR is _not_ consumed in the input port. If CHAR is
- alphabetic, it should be lowercase; otherwise the reader will not
- recognize it, since the reader converts the character following
- octothorpes to lowercase.
- -- procedure: reading-error port message irritant ... --> unspecified
- Signals an error while reading, for custom sharp macros. It is not
- likely that calls to 'reading-error' will return.
- -- procedure: gobble-line port --> unspecified
- Reads until a newline from PORT. The newline character sequence is
- consumed.
- File: scheme48.info, Node: Writer, Prev: Reader, Up: Reader & writer
- 4.6.2 Writer
- ------------
- Scheme48's 'writing' structure exports its writer facility. The 'write'
- and 'display' bindings from it are identical to those from the 'scheme'
- structure, which are the same bindings that R5RS specifies.
- -- procedure: write object [port] --> unspecified
- Writes OBJECT to PORT, which defaults to the current output port,
- in a machine-readable manner. Strings are written with double-
- quotes; characters are prefixed by '#\'. Any object that is
- unreadable -- anything that does not have a written representation
- as an S-expression -- is written based on its "disclosed"
- representation. Such unreadable objects are converted to a
- disclosed representation by the 'disclose' generic procedure (see
- below).
- -- procedure: display object [port] --> unspecified
- Displays OBJECT to PORT, which defaults to the value of the current
- output port, in a more human-readable manner. Strings are written
- without surrounding double-quotes; characters are written as
- themselves with no prefix.
- -- procedure: recurring-write object port recur --> unspecified
- Writes OBJECT to PORT. Every time this recurs upon a new object,
- rather than calling itself or its own looping procedure, it calls
- RECUR. This allows customized printing routines that still take
- advantage of the existence of Scheme48's writer. For example,
- 'display' simply calls 'recurring-write' with a recurring procedure
- that prints strings and characters specially and lets
- 'recurring-write' handle everything else.
- -- procedure: display-type-name name port --> unspecified
- If NAME is a symbol with an alphabetic initial character, this
- writes NAME to PORT with the first character uppercased and the
- remaining character lowercased; otherwise, 'display-type-name'
- simply writes NAME to PORT with 'display'.
- (display-type-name 'foo)
- -| Foo
- (display-type-name (string->symbol "42foo"))
- -| 42foo
- (display-type-name (cons "foo" "bar"))
- -| (foo . bar)
- (display-type-name (string->symbol "fOo-BaR"))
- -| Foo-bar
- This is used when printing disclosed representations (see below).
- 4.6.2.1 Object disclosure
- .........................
- The 'methods' structure (*note Generic dispatch system::) exports the
- generic procedure 'disclose' and its method table '&disclose'. When
- 'recurring-write' encounters an object it is unable to write in a
- rereadable manner, it applies 'disclose' to the unreadable object to
- acquire a "disclosed representation." (If 'disclose' returns '#f', i.e.
- the object has no disclosed representation, the writer will write
- '#{Random object}'.) After converting a value to its disclosed
- representation, e.g. a list consisting of the symbol 'foo', the symbol
- 'bar', a byte vector, and a pair '(1 . 2)', the writer will write '#{Foo
- #{Byte-vector} bar (1 . 2)}'. That is: contents of the list are
- surrounded by '#{' and '}', the first element of the list (the 'type
- name') is written with 'display-type-name', and then the remaining
- elements of the list are recursively printed out with the RECUR
- argument.
- Typically, when a programmer creates an abstract data type by using
- Scheme48's record facility, he will not add methods to '&disclose' but
- instead define the record type's discloser with the
- 'define-record-discloser' procedure; *note Records::.
- Example:
- (define-record-type pare rtd/pare
- (kons a d)
- pare?
- (a kar set-kar!)
- (d kdr set-kdr!))
- (define-record-discloser rtd/pare
- (lambda (pare)
- `(pare ,(kar pare) *dot* ,(kdr pare))))
- (write (kons (kons 5 3) (kons 'a 'b)))
- -| #{Pare #{Pare 5 *dot* 3} *dot* #{Pare a *dot* b}}
- File: scheme48.info, Node: Records, Next: Suspending and resuming heap images, Prev: Reader & writer, Up: System facilities
- 4.7 Records
- ===========
- Scheme48 provides several different levels of a record facility. Most
- programmers will probably not care about the two lower levels; the
- syntactic record type definers are sufficient for abstract data types.
- At the highest level, there are two different record type definition
- macros. Richard Kelsey's is exported from the 'defrecord' structure;
- Jonathan Rees's is exported from 'define-record-types'. They both
- export a 'define-record-type' macro and the same
- 'define-record-discloser' procedure; however, the macros are
- dramatically different. Scheme48 also provides [SRFI 9], which is
- essentially Jonathan Rees's record type definition macro with a slight
- syntactic difference, in the 'srfi-9' structure. Note, however, that
- 'srfi-9' does not export 'define-record-discloser'. The difference
- between Jonathan Rees's and Richard Kelsey's record type definition
- macros is merely syntactic convenience; Jonathan Rees's more
- conveniently allows for arbitrary naming of the generated variables,
- whereas Richard Kelsey's is more convenient if the naming scheme varies
- little.
- 4.7.1 Jonathan Rees's 'define-record-type' macro
- ------------------------------------------------
- -- syntax: define-record-type
- (define-record-type RECORD-TYPE-NAME RECORD-TYPE-VARIABLE
- (CONSTRUCTOR CONSTRUCTOR-ARGUMENT ...)
- [PREDICATE]
- (FIELD-TAG FIELD-ACCESSOR [FIELD-MODIFIER])
- ...)
- This defines RECORD-TYPE-VARIABLE to be a record type descriptor.
- CONSTRUCTOR is defined to be a procedure that accepts the listed
- field arguments and creates a record of the newly defined type with
- those fields initialized to the corresponding arguments.
- PREDICATE, if present, is defined to be the disjoint (as long as
- abstraction is not violated by the lower-level record interface)
- type predicate for the new record type. Each FIELD-ACCESSOR is
- defined to be a unary procedure that accepts a record type and
- returns the value of the field named by the corresponding
- FIELD-TAG. Each FIELD-MODIFIER, if present, is defined to be a
- binary procedure that accepts a record of the new type and a value,
- which it assigns the field named by the corresponding FIELD-TAG to.
- Every CONSTRUCTOR-ARGUMENT must have a corresponding FIELD-TAG,
- though FIELD-TAGs that are not used as arguments to the record
- type's constructor are simply uninitialized when created. They
- should have modifiers: otherwise they will never be initialized.
- It is worth noting that Jonathan Rees's 'define-record-type' macro
- does not introduce identifiers that were not in the original
- macro's input form.
- For example:
- (define-record-type pare rtd/pare
- (kons a d)
- pare?
- (a kar)
- (d kdr set-kdr!))
- (kar (kons 5 3))
- => 5
- (let ((p (kons 'a 'c)))
- (set-kdr! p 'b)
- (kdr p))
- => b
- (pare? (kons 1 2))
- => #t
- (pare? (cons 1 2))
- => #f
- There is also a variant of Jonathan Rees's 'define-record-type' macro
- for defining record types with fields whose accessors and modifiers
- respect optimistic concurrency (*note Optimistic concurrency::) by
- logging in the current proposal.
- 4.7.2 Richard Kelsey's 'define-record-type' macro
- -------------------------------------------------
- -- syntax: define-record-type
- (define-record-type TYPE-NAME
- (ARGUMENT-FIELD-SPECIFIER ...)
- (NONARGUMENT-FIELD-SPECIFIER ...))
- ARGUMENT-FIELD-SPECIFIER -->
- FIELD-TAG Immutable field
- | (FIELD-TAG) Mutable field
- NONARGUMENT-FIELD-SPECIFIER -->
- FIELD-TAG Uninitialized field
- | (FIELD-TAG EXP) Initialized with EXP's value
- This defines 'type/TYPE-NAME' to be a record type descriptor for
- the newly defined record type, 'TYPE-NAME-maker' to be a
- constructor for the new record type that accepts arguments for
- every field in the argument field specifier list, 'TYPE-NAME?' to
- be the disjoint type predicate for the new record type, accessors
- for each field tag FIELD-TAG by constructing an identifier
- 'TYPE-NAME-FIELD-TAG', and modifiers for each argument field tag
- that was specified to be mutable as well as each nonargument field
- tag. The name of the modifier for a field tag FIELD-TAG is
- constructed to be 'set-TYPE-NAME-FIELD-TAG!'.
- Note that Richard Kelsey's 'define-record-type' macro _does_
- concatenate & introduce new identifiers, unlike Jonathan Rees's.
- For example, a use of Richard Kelsey's 'define-record-type' macro
- (define-record-type pare
- (kar
- (kdr))
- (frob
- (mumble 5)))
- is equivalent to the following use of Jonathan Rees's macro
- (define-record-type pare type/pare
- (%pare-maker kar kdr mumble)
- pare?
- (kar pare-kar)
- (kdr pare-kdr set-pare-kdr!)
- (frob pare-frob set-pare-frob!)
- (mumble pare-mumble set-pare-mumble!))
- (define (pare-maker kar kdr)
- (%pare-maker kar kdr 5))
- 4.7.3 Record types
- ------------------
- Along with two general record type definition facilities, there are
- operations directly on the record type descriptors themselves, exported
- by the 'record-types' structure. (Record type descriptors are actually
- records themselves.)
- -- procedure: make-record-type name field-tags -->
- record-type-descriptor
- -- procedure: record-type? object --> boolean
- 'Make-record-type' makes a record type descriptor with the given
- name and field tags. 'Record-type?' is the disjoint type predicate
- for record types.
- -- procedure: record-type-name rtype-descriptor --> symbol
- -- procedure: record-type-field-names rtype-descriptor --> symbol-list
- Accessors for the two record type descriptor fields.
- -- procedure: record-constructor rtype-descriptor argument-field-tags
- --> constructor-procedure
- -- procedure: record-predicate rtype-descriptor --> predicate-procedure
- -- procedure: record-accessor rtype-descriptor field-tag -->
- accessor-procedure
- -- procedure: record-modifier rtype-descriptor field-tag -->
- modifier-procedure
- Constructors for the various procedures relating to record types.
- 'Record-constructor' returns a procedure that accepts arguments for
- each field in ARGUMENT-FIELD-TAGS and constructs a record whose
- record type descriptor is RTYPE-DESCRIPTOR, initialized with its
- arguments. 'Record-predicate' returns a disjoint type predicate
- for records whose record type descriptor is RTYPE-DESCRIPTOR.
- 'Record-accessor' and 'record-modifier' return accessors and
- modifiers for records whose record type descriptor is
- RTYPE-DESCRIPTOR for the given fields.
- -- procedure: define-record-discloser rtype-descriptor discloser -->
- unspecific
- Defines the method by which records of type RTYPE-DESCRIPTOR are
- disclosed (*note Writer::). This is also exported by
- 'define-record-types' and 'defrecord'.
- -- procedure: define-record-resumer rtype-descriptor resumer -->
- unspecified
- Sets RTYPE-DESCRIPTOR's record resumer to be RESUMER. If RESUMER
- is '#t' (the default), records of this type require no particular
- reinitialization when found in dumped heap images (*note Suspending
- and resuming heap images::); if RESUMER is '#f', records of the
- type RTYPE-DESCRIPTOR may not be dumped in heap images; finally, if
- it is a procedure, and the heap image is resumed with the usual
- image resumer (*note Suspending and resuming heap images::), it is
- applied to each record whose record type descriptor is
- RTYPE-DESCRIPTOR after the run-time system has been initialized and
- before the argument to 'usual-resumer' is called.
- The 'records-internal' structure also exports these:
- -- record type: :record-type
- The record type of record types.
- -- procedure: disclose-record record --> disclosed
- This applies RECORD's record type descriptor's discloser procedure
- to RECORD to acquire a disclosed representation; *note Writer::.
- For expository purposes, the record type record type might have been
- defined like so with Jonathan Rees's 'define-record-type' macro:
- (define-record-type record-type :record-type
- (make-record-type name field-names)
- record-type?
- (name record-type-name)
- (field-names record-type-field-names))
- or like so with Richard Kelsey's 'define-record-type' macro:
- (define-record-type record-type
- (name field-names)
- ())
- Of course, in reality, these definitions would have severe problems with
- circularity of definition.
- 4.7.4 Low-level record manipulation
- -----------------------------------
- Internally, records are represented very similarly to vectors, and as
- such have low-level operations on them similar to vectors, exported by
- the 'records' structure. Records usually reserve the slot at index 0
- for their record type descriptor.
- *Warning:* The procedures described here can be very easily misused
- to horribly break abstractions. Use them very carefully, only in very
- limited & extreme circumstances!
- -- procedure: make-record length init --> record
- -- procedure: record elt ... --> record
- -- procedure: record? object --> boolean
- -- procedure: record-length record --> integer
- -- procedure: record-ref record index --> value
- -- procedure: record-set! record index object --> unspecified
- Exact analogues of similarly named vector operation procedures.
- -- procedure: record-type record --> value
- This returns the record type descriptor of RECORD, i.e. the value
- of the slot at index 0 in RECORD.
- File: scheme48.info, Node: Suspending and resuming heap images, Prev: Records, Up: System facilities
- 4.8 Suspending and resuming heap images
- =======================================
- Scheme48's virtual machine operates by loading a heap image into memory
- and calling the initialization procedure specified in the image dump.
- Heap images can be produced in several different ways: programmatically
- with 'write-image', using the command processor's facilities (*note
- Image-building commands::), or with the static linker. This section
- describes only 'write-image' and the related system resumption &
- initialization.
- Heap image dumps begin with a sequence of characters terminated by an
- ASCII form-feed/page character (codepoint 12). This content may be
- anything; for example, it might be a Unix '#!' line that invokes
- 'scheme48vm' on the file, or it might be a silly message to whomever
- reads the top of the heap image dump file. (The command processor's
- ',dump' & ',build' commands (*note Image-building commands::) write a
- blank line at the top; the static linker puts a message stating that the
- image was built by the static linker.)
- 'Write-image' is exported by the 'write-images' structure.
- -- procedure: write-image filename startup-proc message --> unspecified
- Writes a heap image whose startup procedure is STARTUP-PROC and
- that consists of every object accessible in some way from
- STARTUP-PROC. MESSAGE is put at the start of the heap image file
- before the ASCII form-feed/page character. When the image is
- resumed, STARTUP-PROC is passed a vector of program arguments, an
- input channel for standard input, an output channel for standard
- output, an output channel for standard error, and a vector of
- records to be resumed. This is typically simplified by
- 'usual-resumer' (see below). On Unix, STARTUP-PROC must return an
- integer exit code; otherwise the program will crash and burn with a
- very low-level VM error message when STARTUP-PROC returns.
- 4.8.1 System initialization
- ---------------------------
- When suspended heap images are resumed by the VM, the startup procedure
- specified in the heap image is applied to five arguments: a vector of
- command-line arguments (passed after the '-a' argument to the VM), an
- input channel for standard input, an output channel for standard output,
- an output channel for standard error, and a vector of records to be
- resumed. The startup procedure is responsible for performing any
- initialization necessary -- including initializing the Scheme48 run-time
- system -- as well as simply running the program. Typically, this
- procedure is not written manually: resumers are ordinarily created using
- the "usual resumer" abstraction, exported from the structure
- 'usual-resumer'.
- -- procedure: usual-resumer startup-proc --> resumer-proc
- This returns a procedure that is suitable as a heap image resumer
- procedure. When the heap image is resumed, it initializes the
- run-time system -- it resumes all the records, initializes the
- thread system, the dynamic state, the interrupt system, I/O system,
- &c. -- and applies STARTUP-PROC to a list (not a vector) of the
- command-line arguments.
- Some records may contain machine-, OS-, or other session-specific
- data. When suspended in heap images and later resumed, this data may be
- invalidated, and it may be necessary to reinitialize this data upon
- resumption of suspended heap images. For this reason Scheme48 provides
- "record resumers"; see 'define-record-resumer' from the 'record-types'
- structure (*note Records::).
- 4.8.2 Manual system initialization
- ----------------------------------
- If a programmer chooses not to use 'usual-resumer' -- which is _not_ a
- very common thing to do --, he is responsible for manual initialization
- of the run-time system, including the I/O system, resumption of records,
- the thread system and the root thread scheduler, the interrupt system,
- and the condition system.
- *Warning:* Manual initialization of the run-time system is a _very_
- delicate operation. Although one can potentially vastly decrease the
- size of dumped heap images by doing it manually, (1) it is very
- error-prone and difficult to do without exercising great care, which is
- why the usual resumer facility exists. Unless you _*really*_ know what
- you are doing, you should just use the usual resumer.
- At the present, documentation of manual system initialization is
- absent. However, if the reader knows enough about what he is doing that
- he desires to manually initialize the run-time system, he is probably
- sufficiently familiar with it already to be able to find the necessary
- information directly from Scheme48's source code and module
- descriptions.
- ---------- Footnotes ----------
- (1) For example, the author of this manual, merely out of curiosity,
- compared the sizes of two images: one that used the usual resumer and
- printed each of its command-line arguments, and one that performed _no_
- run-time system initialization -- which eliminated the run-time system
- in the image, because it was untraceable from the resumer -- and wrote
- directly to the standard output channel. The difference was a factor of
- about twenty. However, also note that the difference is constant; the
- run-time system happened to account for nineteen twentieths of the
- larger image.
- File: scheme48.info, Node: Multithreading, Next: Libraries, Prev: System facilities, Up: Top
- 5 Multithreading
- ****************
- This chapter describes Scheme48's fully preëmptive and sophisticated
- user-level thread system. Scheme48 supports customized and nested
- thread schedulers, user-designed synchronization mechanisms, optimistic
- concurrency, useful thread synchronization libraries, a high-level event
- algebra based on Reppy's Concurrent ML [Reppy 99], and common
- pessimistic concurrency/mutual-exclusion-based thread synchronization
- facilities.
- * Menu:
- * Basic thread operations::
- * Optimistic concurrency::
- * Higher-level synchronization::
- * Concurrent ML:: High-level event synchronization
- * Pessimistic concurrency:: Mutual exclusion/locking
- * Custom thread synchronization::
- File: scheme48.info, Node: Basic thread operations, Next: Optimistic concurrency, Up: Multithreading
- 5.1 Basic thread operations
- ===========================
- This section describes the 'threads' structure.
- -- procedure: spawn thunk [name] --> thread
- 'Spawn' constructs a new thread and instructs the current thread
- scheduler to commence running the new thread. NAME, if present, is
- used for debugging. The new thread has a fresh dynamic environment
- (*note Fluid/dynamic bindings::).
- There are several miscellaneous facilities for thread operations.
- -- procedure: relinquish-timeslice --> unspecified
- -- procedure: sleep count --> unspecified
- 'Relinquish-timeslice' relinquishes the remaining quantum that the
- current thread has to run; this allows the current scheduler run
- the next thread immediately. 'Sleep' suspends the current thread
- for COUNT milliseconds.
- -- procedure: terminate-current-thread --> (does not return)
- Terminates the current thread, running all 'dynamic-wind' exit
- points. 'Terminate-current-thread' obviously does not return.
- Threads may be represented and manipulated in first-class thread
- descriptor objects.
- -- procedure: current-thread --> thread
- -- procedure: thread? object --> boolean
- -- procedure: thread-name thread --> value
- -- procedure: thread-uid thread --> unique-integer-id
- 'Current-thread' returns the thread descriptor for the currently
- running thread. 'Thread?' is the thread descriptor disjoint type
- predicate. 'Thread-name' returns the name that was passed to
- 'spawn' when spawning THREAD, or '#f' if no name was passed.
- 'Thread-uid' returns a thread descriptor's unique integer
- identifier, assigned by the thread system.
- File: scheme48.info, Node: Optimistic concurrency, Next: Higher-level synchronization, Prev: Basic thread operations, Up: Multithreading
- 5.2 Optimistic concurrency
- ==========================
- Scheme48's fundamental thread synchronization mechanism is based on a
- device often used in high-performance database systems: optimistic
- concurrency. The basic principle of optimistic concurrency is that,
- rather than mutually excluding other threads from data involved in one
- thread's transaction, a thread keeps a log of its transaction, not
- actually modifying the data involved, only touching the log. When the
- thread is ready to commit its changes, it checks that all of the reads
- from memory retained their integrity -- that is, all of the memory that
- was read from during the transaction has remained the same, and is
- consistent with what is there at the time of the commit. If, and only
- if, all of the reads remained valid, the logged writes are committed;
- otherwise, the transaction has been invalidated. While a thread is
- transacting, any number of other threads may be also transacting on the
- same resource. All that matters is that the values each transaction
- read are consistent with every write that was committed during the
- transaction. This synchronization mechanism allows for wait-free,
- lockless systems that easily avoid confusing problems involving careful
- sequences of readily deadlock-prone mutual exclusion.
- In the Scheme48 system, every thread has its own log of transactions,
- called a "proposal". There are variants of all data accessors &
- modifiers that operate on the current thread's proposal, rather than
- actual memory: after the initial read of a certain part of memory --
- which _does_ perform a real read --, the value from that location in
- memory is cached in the proposal, and thenceforth reads from that
- location in memory will actually read the cache; modifications touch
- only the proposal, until the proposal is committed.
- All of the names described in this section are exported by the
- 'proposals' structure.
- 5.2.1 High-level optimistic concurrency
- ---------------------------------------
- There are several high-level operations that abstract the manipulation
- of the current thread's proposal.
- -- procedure: call-ensuring-atomicity thunk --> values
- -- procedure: call-ensuring-atomicity! thunk --> unspecified
- These ensure that the operation of THUNK is atomic. If there is
- already a current proposal in place, these are equivalent to
- calling THUNK. If there is not a current proposal in place, these
- install a new proposal, call THUNK, and attempt to commit the new
- proposal. If the commit succeeded, these return. If it failed,
- these retry with a new proposal until they do succeed.
- 'Call-ensuring-atomicity' returns the values that THUNK returned
- when the commit succeeded; 'call-ensuring-atomicity!' returns zero
- values -- it is intended for when THUNK is used for its effects
- only.
- -- procedure: call-atomically thunk --> values
- -- procedure: call-atomically! thunk --> unspecified
- These are like CALL-ENSURING-ATOMICITY and
- CALL-ENSURING-ATOMICITY!, respectively, except that they always
- install a new proposal (saving the old one and restoring it when
- they are done).
- -- syntax: ensure-atomicity body --> values
- -- syntax: ensure-atomicity! body --> unspecified
- -- syntax: atomically body --> values
- -- syntax: atomically! body --> unspecified
- These are syntactic sugar over 'call-ensuring-atomicity',
- 'call-ensuring-atomicity!', 'call-atomically', and
- 'call-atomically!', respectively.
- Use these high-level optimistic concurrency operations to make the
- body atomic. 'Call-ensuring-atomicity' &c. simply ensure that the
- transaction will be atomic, and may 'fuse' it with an enclosing atomic
- transaction if there already is one, i.e. use the proposal for that
- transaction already in place, creating one only if there is not already
- one. 'Call-atomically' &c. are for what might be called 'subatomic'
- transactions, which cannot be fused with other atomic transactions, and
- for which there is always created a new proposal.
- However, code within 'call-ensuring-atomicity' &c. or
- 'call-atomically' &c. should _not_ explicitly commit the current
- proposal; those operations above _automatically_ commit the current
- proposal when the atomic transaction is completed. (In the case of
- 'call-atomically' &c., this is when the procedure passed returns; in the
- case of 'call-ensuring-atomicity' &c., this is when the outermost
- enclosing atomic transaction completes, or the same as 'call-atomically'
- if there was no enclosing atomic transaction.) To explicitly commit the
- current proposal -- for example, to perform some particular action if
- the commit fails rather than just to repeatedly retry the transaction,
- or to use operations from the customized thread synchronization (*note
- Custom thread synchronization::) facilities that commit the current
- proposal after their regular function, or the operations on condition
- variables (*note Higher-level synchronization::) that operate on the
- condition variable and then commit the current proposal --, one must use
- the 'with-new-proposal' syntax as described below, not these operations.
- 5.2.2 Logging variants of Scheme procedures
- -------------------------------------------
- -- procedure: provisional-car pair --> value
- -- procedure: provisional-cdr pair --> value
- -- procedure: provisional-set-car! pair value --> unspecified
- -- procedure: provisional-set-cdr! pair value --> unspecified
- -- procedure: provisional-cell-ref cell --> value
- -- procedure: provisional-cell-set! cell value --> unspecified
- -- procedure: provisional-vector-ref vector index --> value
- -- procedure: provisional-vector-set! vector index value -->
- unspecified
- -- procedure: provisional-string-ref string index --> char
- -- procedure: provisional-string-set! string index value -->
- unspecified
- -- procedure: provisional-byte-vector-ref byte-vector index --> char
- -- procedure: provisional-byte-vector-set! byte-vector index byte -->
- unspecified
- -- procedure: attempt-copy-bytes! from fstart to tstart count -->
- unspecified
- These are variants of most basic Scheme memory accessors &
- modifiers that log in the current proposal, rather than performing
- the actual memory access/modification. All of these do perform the
- actual memory access/modification, however, if there is no current
- proposal in place when they are called. 'Attempt-copy-bytes!'
- copies a sequence of COUNT bytes from the byte vector or string
- FROM, starting at the index FSTART, to the byte vector or string
- TO, starting at the index TSTART.
- 5.2.3 Synchronized records
- --------------------------
- -- syntax: define-synchronized-record-type
- (define-synchronized-record-type TAG TYPE-NAME
- (CONSTRUCTOR-NAME PARAMETER-FIELD-TAG ...)
- [(SYNC-FIELD-TAG ...)]
- PREDICATE-NAME
- (FIELD-TAG ACCESSOR-NAME [MODIFIER-NAME])
- ...)
- This is exactly like 'define-record-type' from the
- 'define-record-types' structure, except that the accessors &
- modifiers for each field in SYNC-FIELD-TAG ... are defined to be
- provisional, i.e. to log in the current proposal. If the list of
- synchronized fields is absent, all of the fields are synchronized,
- i.e. it is as if all were specified in that list.
- The 'proposals' structure also exports 'define-record-discloser'
- (*note Records::). Moreover, the 'define-sync-record-types' structure,
- too, exports 'define-synchronized-record-type', though it does not
- export 'define-record-discloser'.
- 5.2.4 Optimistic concurrency example
- ------------------------------------
- Here is a basic example of using optimistic concurrency to ensure the
- synchronization of memory. We first present a simple mechanism for
- counting integers by maintaining internal state, which is expressed
- easily with closures:
- (define (make-counter value)
- (lambda ()
- (let ((v value))
- (set! value (+ v 1))
- v)))
- This has a problem: between obtaining the value of the closure's slot
- for 'value' and updating that slot, another thread might be given
- control and modify the counter, producing unpredictable results in
- threads in the middle of working with the counter. To remedy this, we
- might add a mutual exclusion lock to counters to prevent threads from
- simultaneously accessing the cell:
- (define (make-counter value)
- (let ((lock (make-lock)))
- (lambda ()
- (dynamic-wind
- (lambda () (obtain-lock lock))
- (lambda ()
- (let ((v value))
- (set! value (+ v 1))
- v))
- (lambda () (release-lock lock))))))
- This poses another problem, however. Suppose we wish to write an
- atomic '(step-counters! COUNTER ...)' procedure that increments each of
- the supplied counters by one; supplying a counter N times should have
- the effect of incrementing it by N. The naïve definition of it is this:
- (define (step-counters! . counters)
- (for-each (lambda (counter) (counter))
- counters))
- Obviously, though, this is not atomic, because each individual
- counter is locked when it is used, but not the whole iteration across
- them. To work around this, we might use an obfuscated control structure
- to allow nesting the locking of counters:
- (define (make-counter value)
- (let ((lock (make-lock)))
- (lambda args
- (dynamic-wind
- (lambda () (obtain-lock lock))
- (lambda ()
- (if (null? args)
- (let ((v value))
- (set! value (+ v 1))
- v)
- ((car args))))
- (lambda () (release-lock lock))))))
- (define (step-counters! . counters)
- (let loop ((cs counters))
- (if (null? cs)
- (for-each (lambda (counter) (counter))
- counters)
- ((car cs) (lambda () (loop (cdr cs)))))))
- Aside from the obvious matter of the obfuscation of the control
- structures used here, however, this has another problem: we cannot step
- one counter multiple times atomically. Though different locks can be
- nested, nesting is very dangerous, because accidentally obtaining a lock
- that is already obtained can cause deadlock, and there is no modular,
- transparent way to avoid this in the general case.
- Instead, we can implement counters using optimistic concurrency to
- synchronize the shared data. The state of counters is kept explicitly
- in a cell (*note Cells::), in order to use a provisional accessor &
- modifier, as is necessary to make use of optimistic concurrency, and we
- surround with 'call-ensuring-atomicity' any regions we wish to be
- atomic:
- (define (make-counter initial)
- (let ((cell (make-cell initial)))
- (lambda ()
- (call-ensuring-atomicity
- (lambda ()
- (let ((value (provisional-cell-ref cell)))
- (provisional-cell-set! cell (+ value 1))
- value))))))
- (define (step-counters! . counters)
- (call-ensuring-atomicity!
- (lambda ()
- (for-each (lambda (counter) (counter))
- counters))))
- This approach has a number of advantages:
- * The original control structure is preserved, only with provisional
- operators for shared memory access that we explicitly wish to be
- synchronized and with 'call-ensuring-atomicity' wrapping the
- portions of code that we explicitly want to be atomic.
- * Composition of transactions is entirely transparent; it is
- accomplished automatically simply by 'call-ensuring-atomicity'.
- * Transactions can be nested arbitrarily deeply, and there is no
- problem of accidentally locking the same resource again at a deeper
- nesting level to induce deadlock.
- * No explicit mutual exclusion or blocking is necessary. Threads
- proceed without heed to others, but do not actually write data to
- the shared memory until its validity is ensured. There is no
- deadlock at all.
- 5.2.5 Low-level optimistic concurrency
- --------------------------------------
- Along with the higher-level operations described above, there are some
- lower-level primitives for finer control over optimistic concurrency.
- -- procedure: make-proposal --> proposal
- -- procedure: current-proposal --> proposal
- -- procedure: set-current-proposal! proposal --> unspecified
- -- procedure: remove-current-proposal! --> unspecified
- 'Make-proposal' creates a fresh proposal. 'Current-proposal'
- returns the current thread's proposal. 'Set-current-proposal!'
- sets the current thread's proposal to PROPOSAL.
- 'Remove-current-proposal!' sets the current thread's proposal to
- '#f'.
- -- procedure: maybe-commit --> boolean
- -- procedure: invalidate-current-proposal! --> unspecified
- 'Maybe-commit' checks that the current thread's proposal is still
- valid. If it is, the proposal's writes are committed, and
- 'maybe-commit' returns '#t'; if not, the current thread's proposal
- is set to '#f' and 'maybe-commit' returns '#f'.
- 'Invalidate-current-proposal!' causes an inconsistency in the
- current proposal by caching a read and then directly writing to the
- place that read was from.
- -- syntax: with-new-proposal (lose) body --> values
- Convenience for repeating a transaction. 'With-new-proposal' saves
- the current proposal and will reinstates it when everything is
- finished. After saving the current proposal, it binds LOSE to a
- nullary procedure that installs a fresh proposal and that evaluates
- BODY; it then calls LOSE. Typically, the last thing, or close to
- last thing, that BODY will do is attempt to commit the current
- proposal, and, if that fails, call LOSE to retry.
- 'With-new-proposal' expands to a form that returns the values that
- BODY returns.
- This 'retry-at-most' example tries running the transaction of
- THUNK, and, if it fails to commit, retries at most N times. If the
- transaction is successfully committed before N repeated attempts,
- it returns true; otherwise, it returns false.
- (define (retry-at-most n thunk)
- (with-new-proposal (lose)
- (thunk)
- (cond ((maybe-commit) #t)
- ((zero? n) #f)
- (else (set! n (- n 1))
- (lose)))))
- File: scheme48.info, Node: Higher-level synchronization, Next: Concurrent ML, Prev: Optimistic concurrency, Up: Multithreading
- 5.3 Higher-level synchronization
- ================================
- This section details the various higher-level thread synchronization
- devices that Scheme48 provides.
- 5.3.1 Condition variables
- -------------------------
- "Condition variables" are multiple-assignment cells on which readers
- block. Threads may wait on condition variables; when some other thread
- assigns a condition variable, all threads waiting on it are revived.
- The 'condvars' structure exports all of these condition-variable-related
- names.
- In many concurrency systems, condition variables are operated in
- conjunction with mutual exclusion locks. On the other hand, in
- Scheme48, they are used in conjunction with its optimistic concurrency
- devices.
- -- procedure: make-condvar [id] --> condvar
- -- procedure: condvar? object --> boolean
- Condition variable constructor & disjoint type predicate. ID is
- used purely for debugging.
- -- procedure: maybe-commit-and-wait-for-condvar condvar --> boolean
- -- procedure: maybe-commit-and-set-condvar! condvar value --> boolean
- 'Maybe-commit-and-wait-for-condvar' attempts to commit the current
- proposal. If the commit succeeded, the current thread is blocked
- on CONDVAR, and when the current thread is woken up,
- 'maybe-commit-and-wait-for-condvar' returns '#t'. If the commit
- did not succeed, 'maybe-commit-and-wait-for-condvar' immediately
- returns '#f'. 'Maybe-commit-and-set-condvar!' attempts to commit
- the current proposal as well. If it succeeds, it is noted that
- CONDVAR has a value, CONDVAR's value is set to be VALUE, and all
- threads waiting on CONDVAR are woken up.
- *Note:* Do not use these in atomic transactions as delimited by
- 'call-ensuring-atomicity' &c.; see the note in *note Optimistic
- concurrency:: on this matter for details.
- -- procedure: condvar-has-value? condvar --> boolean
- -- procedure: condvar-value condvar --> value
- 'Condvar-has-value?' tells whether or not CONDVAR has been
- assigned. If it has been assigned, 'condvar-value' accesses the
- value to which it was assigned.
- -- procedure: set-condvar-has-value?! condvar boolean --> unspecified
- -- procedure: set-condvar-value! condvar value --> unspecified
- 'Set-condvar-has-value?!' is used to tell whether or not CONDVAR is
- assigned. 'Set-condvar-value!' sets CONDVAR's value.
- *Note:* 'Set-condvar-has-value?!' should be used only with a second
- argument of '#f'. 'Set-condvar-value!' is a very dangerous
- routine, and 'maybe-commit-and-set-condvar!' is what one should
- almost always use, except if one wishes to clean up after
- unassigning a condition variable.
- 5.3.2 Placeholders
- ------------------
- "Placeholders" are similar to condition variables, except that they may
- be assigned only once; they are in general a much simpler mechanism for
- throw-away temporary synchronization devices. They are provided by the
- 'placeholders' structure.
- -- procedure: make-placeholder [id] --> placeholder
- -- procedure: placeholder? object --> boolean
- Placeholder constructor & disjoint type predicate. ID is used only
- for debugging purposes when printing placeholders.
- -- procedure: placeholder-value placeholder --> value
- -- procedure: placeholder-set! placeholder value --> unspecified
- 'Placeholder-value' blocks until PLACEHOLDER is assigned, at which
- point it returns the value assigned. 'Placeholder-set!' assigns
- PLACEHOLDER's value to VALUE, awakening all threads waiting for
- PLACEHOLDER. It is an error to assign a placeholder with
- 'placeholder-set!' that has already been assigned.
- 5.3.3 Value pipes
- -----------------
- "Value pipes" are asynchronous communication pipes between threads. The
- 'value-pipes' structure exports these value pipe operations.
- -- procedure: make-pipe [size [id]] --> value-pipe
- -- procedure: pipe? object --> boolean
- 'Make-pipe' is the value pipe constructor. SIZE is a limit on the
- number of elements the pipe can hold at one time. ID is used for
- debugging purposes only in printing pipes. 'Pipe?' is the disjoint
- type predicate for value pipes.
- -- procedure: empty-pipe? pipe --> boolean
- -- procedure: empty-pipe! pipe --> unspecified
- 'Empty-pipe?' returns '#t' if PIPE has no elements in it and '#f'
- if not. 'Empty-pipe!' removes all elements from 'pipe'.
- -- procedure: pipe-read! pipe --> value
- -- procedure: pipe-maybe-read! pipe --> value or '#f'
- -- procedure: pipe-maybe-read?! pipe --> [boolean value]
- 'Pipe-read!' reads a value from PIPE, removing it from the queue.
- It blocks if there are no elements available in the queue.
- 'Pipe-maybe-read!' attempts to read & return a single value from
- PIPE; if no elements are available in its queue, it instead returns
- '#f'. 'Pipe-maybe-read?!' does similarly, but it returns two
- values: a boolean, signifying whether or not a value was read; and
- the value, or '#f' if no value was read. 'Pipe-maybe-read?!' is
- useful when PIPE may contain the value '#f'.
- -- procedure: pipe-write! pipe value --> unspecified
- -- procedure: pipe-push! pipe value --> unspecified
- -- procedure: pipe-maybe-write! pipe value --> boolean
- 'Pipe-write!' attempts to add VALUE to PIPE's queue. If PIPE's
- maximum size, as passed to 'make-pipe' when constructing the pipe,
- is either '#f' or greater than the number of elements in PIPE's
- queue, 'pipe-write!' will not block; otherwise it will block until
- a space has been made available in the pipe's queue by another
- thread reading from it. 'Pipe-push!' does similarly, but, in the
- case where the pipe is full, it pushes the first element to be read
- out of the pipe. 'Pipe-maybe-write!' is also similar to
- 'pipe-write!', but it returns '#t' if the pipe was not full, and it
- _immediately_ returns '#f' if the pipe was full.
- File: scheme48.info, Node: Concurrent ML, Next: Pessimistic concurrency, Prev: Higher-level synchronization, Up: Multithreading
- 5.4 Concurrent ML
- =================
- Scheme48 provides a high-level event synchronization facility based on
- on Reppy's "Concurrent ML" [Reppy 99]. The primary object in CML is the
- "rendezvous"(1), which represents a point of process synchronization. A
- rich library for manipulating rendezvous and several useful, high-level
- synchronization abstractions are built atop rendezvous.
- * Menu:
- * Rendezvous concepts::
- * Rendezvous base combinators::
- * Rendezvous communication channels::
- * Rendezvous-synchronized cells::
- * Concurrent ML to Scheme correspondence::
- ---------- Footnotes ----------
- (1) In the original CML, these were called "events", but that term
- was deemed too overloaded and confusing when Scheme48's library was
- developed.
- File: scheme48.info, Node: Rendezvous concepts, Next: Rendezvous base combinators, Up: Concurrent ML
- 5.4.1 Rendezvous concepts
- -------------------------
- When access to a resource must be synchronized between multiple
- processes, for example to transmit information from one process to
- another over some sort of communication channel, the resource provides a
- "rendezvous" to accomplish this, which represents a potential point of
- synchronization between processes. The use of rendezvous occurs in two
- stages: "synchronization" and "enablement". Note that creation of
- rendezvous is an unrelated matter, and it does not (or should not)
- itself result in any communication or synchronization between processes.
- When a process requires an external resource for which it has a
- rendezvous, it "synchronizes" that rendezvous. This first polls whether
- the resource is immediately available; if so, the rendezvous is already
- "enabled", and a value from the resource is immediately produced from
- the synchronization. Otherwise, the synchronization of the rendezvous
- is recorded somehow externally, and the process is blocked until the
- rendezvous is enabled by an external entity, usually one that made the
- resource available. Rendezvous may be reüsed arbitrarily many times;
- the value produced by an enabled, synchronized rendezvous is not cached.
- Note, however, that the construction of a rendezvous does not (or should
- not) have destructive effect, such as sending a message to a remote
- server or locking a mutex; the only destructive effects should be
- incurred at synchronization or enablement time. For effecting
- initialization prior to the synchronization of a rendezvous, see below
- on "delayed rendezvous".
- Rendezvous may consist of multiple rendezvous choices, any of which
- may be taken when enabled but only one of which actually is. If, when a
- composite rendezvous is initially synchronized, several components are
- immediately enabled, each one has a particular numeric priority which is
- used to choose among them. If several are tied for the highest
- priority, a random one is chosen. If none is enabled when the choice is
- synchronized, however, the synchronizer process is suspended until the
- first one is enabled and revives the process. When this happens, any or
- all of the other rendezvous components may receive a negative
- acknowledgement; see below on "delayed rendezvous with negative
- acknowledgement".
- A rendezvous may also be a rendezvous "wrapped" with a procedure,
- which means that, when the internal rendezvous becomes enabled, the
- wrapper one also becomes enabled, and the value it produces is the
- result of applying its procedure to the value that the internal
- rendezvous produced. This allows the easy composition of complex
- rendezvous from simpler ones, and it also provides a simple mechanism
- for performing different actions following the enablement of different
- rendezvous, rather than conflating the results of several possible
- rendezvous choices into one value and operating on that (though this,
- too, can be a useful operation).
- 5.4.2 Delayed rendezvous
- ------------------------
- A rendezvous may be "delayed", which means that its synchronization
- requires some processing that could not or would not be reasonable to
- perform at its construction. It consists of a nullary procedure to
- generate the actual rendezvous to synchronize when the delayed
- rendezvous is itself synchronized.
- For example, a rendezvous for generating unique identifiers, by
- sending a request over a network to some server and waiting for a
- response, could not be constructed by waiting for a response from the
- server, because that may block, which should not occur until
- synchronization. It also could not be constructed by first sending a
- request to the server at all, because that would have a destructive
- effect, which is not meant to happen when creating a rendezvous, only
- when synchronizing or enabling one.
- Instead, the unique identifier rendezvous would be implemented as a
- delayed rendezvous that, when synchronized, would send a request to the
- server and generate a rendezvous for the actual synchronization that
- would become enabled on receiving the server's response.
- 5.4.2.1 Negative acknowledgements
- .................................
- Delayed rendezvous may also receive negative acknowledgements. Rather
- than a simple nullary procedure being used to generate the actual
- rendezvous for synchronization, the procedure is unary, and it is passed
- a "negative acknowledgement rendezvous", or "nack" for short. This nack
- is enabled if the actual rendezvous was not chosen among a composite
- group of rendezvous being synchronized. This allows not only delaying
- initialization of rendezvous until necessary but also aborting or
- rescinding initialized transactions if their rendezvous are unchosen and
- therefore unused.
- For example, a complex database query might be the object of some
- rendezvous, but it is pointless to continue constructing the result if
- that rendezvous is not chosen. A nack can be used to prematurely abort
- the query to the database if another rendezvous was chosen in the stead
- of that for the database query.
- File: scheme48.info, Node: Rendezvous base combinators, Next: Rendezvous communication channels, Prev: Rendezvous concepts, Up: Concurrent ML
- 5.4.3 Rendezvous combinators
- ----------------------------
- The 'rendezvous' structure exports several basic rendezvous combinators.
- -- Constant: never-rv --> rendezvous
- A rendezvous that is never enabled. If synchronized, this will
- block the synchronizing thread indefinitely.
- -- procedure: always-rv value --> rendezvous
- Returns a rendezvous that is always enabled with the given value.
- This rendezvous will never block the synchronizing thread.
- -- procedure: guard rv-generator --> rendezvous
- -- procedure: with-nack rv-generator --> rendezvous
- 'Guard' returns a delayed rendezvous, generated by the given
- procedure RV-GENERATOR, which is passed zero arguments whenever the
- resultant rendezvous is synchronized. 'With-nack' returns a
- delayed rendezvous for which a negative acknowledgement rendezvous
- is constructed. If the resultant rendezvous is synchronized as a
- part of a composite rendezvous, the procedure 'rv-generator' is
- passed a nack for the synchronization, and it returns the
- rendezvous to actually synchronize. If the delayed rendezvous was
- synchronized as part of a composite group of rendezvous, and
- another rendezvous among that group is enabled and chosen first,
- the nack is enabled.
- -- procedure: choose rendezvous ... --> composite-rendezvous
- Returns a rendezvous that, when synchronized, synchronizes all of
- the given components, and chooses only the first one to become
- enabled, or the highest priority one if there are any that are
- already enabled. If any of the rendezvous that were not chosen
- when the composite became enabled were delayed rendezvous with
- nacks, their nacks are enabled.
- -- procedure: wrap rendezvous procedure --> rendezvous
- Returns a rendezvous equivalent to RENDEZVOUS but wrapped with
- PROCEDURE, so that, when the resultant rendezvous is synchronized,
- RENDEZVOUS is transitively synchronized, and when RENDEZVOUS is
- enabled, the resultant rendezvous is also enabled, with the value
- that PROCEDURE returns when passed the value produced by
- RENDEZVOUS.
- (sync (wrap (always-rv 4)
- (lambda (x) (* x x)))) --> 16
- -- procedure: sync rendezvous --> value (may block)
- -- procedure: select rendezvous ... --> value (may block)
- 'Sync' and 'select' synchronize rendezvous. 'Sync' synchronizes a
- single one; 'select' synchronizes any from the given set of them.
- 'Select' is equivalent to '(sync (apply choose RENDEZVOUS ...))',
- but it may be implemented more efficiently.
- 5.4.3.1 Timing rendezvous
- .........................
- The 'rendezvous-time' structure exports two constructors for rendezvous
- that become enabled only at a specific time or after a delay in time.
- -- procedure: at-real-time-rv milliseconds --> rendezvous
- -- procedure: after-time-rv milliseconds --> rendezvous
- 'At-real-time-rv' returns a rendezvous that becomes enabled at the
- time MILLISECONDS relative to the start of the Scheme program.
- 'After-time-rv' returns a rendezvous that becomes enabled at least
- MILLISECONDS after synchronization (_not_ construction).
- File: scheme48.info, Node: Rendezvous communication channels, Next: Rendezvous-synchronized cells, Prev: Rendezvous base combinators, Up: Concurrent ML
- 5.4.4 Rendezvous communication channels
- ---------------------------------------
- 5.4.4.1 Synchronous channels
- ............................
- The 'rendezvous-channels' structure provides a facility for "synchronous
- channels": channels for communication between threads such that any
- receiver blocks until another thread sends a message, or any sender
- blocks until another thread receives the sent message. In CML,
- synchronous channels are also called merely 'channels.'
- -- procedure: make-channel --> channel
- -- procedure: channel? object --> boolean
- 'Make-channel' creates and returns a new channel. 'Channel?' is
- the disjoint type predicate for channels.
- -- procedure: send-rv channel message --> rendezvous
- -- procedure: send channel message --> unspecified (may block)
- 'Send-rv' returns a rendezvous that, when synchronized, becomes
- enabled when a reception rendezvous for CHANNEL is synchronized, at
- which point that reception rendezvous is enabled with a value of
- MESSAGE. When enabled, the rendezvous returned by 'send-rv'
- produces an unspecified value. 'Send' is like 'send-rv', but it
- has the effect of immediately synchronizing the rendezvous, so it
- therefore may block, and it does not return a rendezvous; '(send
- CHANNEL MESSAGE)' is equivalent to '(sync (send-rv CHANNEL
- MESSAGE))'.
- -- procedure: receive-rv channel --> rendezvous
- -- procedure: receive channel --> value (may block)
- 'Receive-rv' returns a rendezvous that, when synchronized, and when
- a sender rendezvous for CHANNEL with some message is synchronized,
- becomes enabled with that message, at which point the sender
- rendezvous is enabled with an unspecified value. 'Receive' is like
- 'receive-rv', but it has the effect of immediately synchronizing
- the reception rendezvous, so it therefore may block, and it does
- not return the rendezvous but rather the message that was sent;
- '(receive CHANNEL)' is equivalent to '(sync (receive-rv CHANNEL))'.
- 5.4.4.2 Asynchronous channels
- .............................
- The 'rendezvous-async-channels' provides an "asynchronous channel"(1)
- facility. Like synchronous channels, any attempts to read from an
- asynchronous channel will block if there are no messages waiting to be
- read. Unlike synchronous channels, however, sending a message will
- never block. Instead, a queue of messages or a queue of recipients is
- maintained: if a message is sent and there is a waiting recipient, the
- message is delivered to that recipient; otherwise it is added to the
- queue of messages. If a thread attempts to receive a message from an
- asynchronous channel and there is a pending message, it receives that
- message; otherwise it adds itself to the list of waiting recipients and
- then blocks.
- *Note:* Operations on synchronous channels from the structure
- 'rendezvous-channels' do not work on asynchronous channels.
- -- procedure: make-async-channel --> async-channel
- -- procedure: async-channel? obj --> boolean
- 'Make-async-channel' creates and returns an asynchronous channel.
- 'Async-channel?' is the disjoint type predicate for asynchronous
- channels.
- -- procedure: receive-async-rv channel --> rendezvous
- -- procedure: receive-async channel --> value (may block)
- 'Receive-async-rv' returns a rendezvous that, when synchronized,
- becomes enabled when a message is available in CHANNEL's queue of
- messages. 'Receive-async' has the effect of immediately
- synchronizing such a rendezvous and, when the rendezvous becomes
- enabled, returning the value itself, rather than the rendezvous;
- '(receive-async CHANNEL)' is equivalent to '(sync (receive-async-rv
- CHANNEL))'.
- -- procedure: send-async channel message --> unspecified
- Sends a message to the asynchronous channel CHANNEL. Unlike the
- synchronous channel 'send' operation, this procedure never blocks
- arbitrarily long.(2) There is, therefore, no need for a
- 'send-async-rv' like the 'send-rv' for synchronous channels. If
- there is a waiting message recipient, the message is delivered to
- that recipient; otherwise, it is added to the channel's message
- queue.
- ---------- Footnotes ----------
- (1) Known as "mailboxes" in Reppy's original CML.
- (2) However, asynchronous channels are implemented by a thread that
- manages two synchronous channels (one for sends & one for receives), so
- this may block briefly if the thread is busy receiving other send or
- receive requests.
- File: scheme48.info, Node: Rendezvous-synchronized cells, Next: Concurrent ML to Scheme correspondence, Prev: Rendezvous communication channels, Up: Concurrent ML
- 5.4.5 Rendezvous-synchronized cells
- -----------------------------------
- 5.4.5.1 Placeholders: single-assignment cells
- .............................................
- "Placeholders"(1) are single-assignment cells on which readers block
- until they are assigned.
- *Note:* These placeholders are disjoint from and incompatible with
- the placeholder mechanism provided in the 'placeholders' structure, and
- attempts to apply operations on one to values of the other are errors.
- -- procedure: make-placeholder [id] --> empty placeholder
- -- procedure: placeholder? object --> boolean
- 'Make-placeholder' creates and returns a new, empty placeholder.
- ID is used only for debugging purposes; it is included in the
- printed representation of the placeholder. 'Placeholder?' is the
- disjoint type predicate for placeholders.
- -- procedure: placeholder-value-rv placeholder --> rendezvous
- -- procedure: placeholder-value placeholder --> value (may block)
- 'Placeholder-value-rv' returns a rendezvous that, when
- synchronized, becomes enabled when PLACEHOLDER has a value, with
- that value. 'Placeholder-value' has the effect of immediately
- synchronizing such a rendezvous, and it returns the value directly,
- but possibly after blocking.
- -- procedure: placeholder-set! placeholder value --> unspecified
- Sets PLACEHOLDER's value to be VALUE, and enables all rendezvous
- for PLACEHOLDER's value with that value. It is an error if
- PLACEHOLDER has already been assigned.
- 5.4.5.2 Jars: multiple-assignment cells
- .......................................
- "Jars"(2) are multiple-assignment cells on which readers block. Reading
- from a full jar has the effect of emptying it, enabling the possibility
- of subsequent assignment, unlike placeholders; and jars may be assigned
- multiple times, but, like placeholders, only jars that are empty may be
- assigned.
- -- procedure: make-jar [id] --> empty jar
- -- procedure: jar? object --> boolean
- 'Make-jar' creates and returns a new, empty jar. ID is used only
- for debugging purposes; it is included in the printed
- representation of the jar. 'Jar?' is the disjoint type predicate
- for jars.
- -- procedure: jar-take-rv jar --> rendezvous
- -- procedure: jar-take jar --> value (may block)
- 'Jar-take-rv' returns a rendezvous that, when synchronized, becomes
- enabled when JAR has a value, which is what value the rendezvous
- becomes enabled with; when that rendezvous is enabled, it also
- removes the value from JAR, putting the jar into an empty state.
- 'Jar-take' has the effect of synchronizing such a rendezvous, may
- block because of that, and returns the value of the jar directly,
- not a rendezvous.
- -- procedure: jar-put! jar value --> unspecified
- 'Jar-put!' puts VALUE into the empty jar JAR. If any taker
- rendezvous are waiting, the first is enabled with the value, and
- the jar is returned to its empty state; otherwise, the jar is put
- in the full state. 'Jar-put!' is an error if applied to a full
- jar.
- ---------- Footnotes ----------
- (1) Called "I-variables" in Reppy's CML, and "I-structures" in ID-90.
- (2) Termed "M-variables" in Reppy's CML.
- File: scheme48.info, Node: Concurrent ML to Scheme correspondence, Prev: Rendezvous-synchronized cells, Up: Concurrent ML
- 5.4.6 Concurrent ML to Scheme correspondence
- --------------------------------------------
- CML name Scheme name
- structure 'CML' structure 'threads'
- 'version' (no equivalent)
- 'banner' (no equivalent)
- 'spawnc' (no equivalent; use 'spawn' and 'lambda')
- 'spawn' 'spawn'
- 'yield' 'relinquish-timeslice'
- 'exit' 'terminate-current-thread'
- 'getTid' 'current-thread'
- 'sameTid' 'eq?' (R5RS)
- 'tidToString' (no equivalent; use the writer)
- structure 'threads-internal'
- 'hashTid' 'thread-uid'
- structure 'rendezvous'
- 'wrap' 'wrap'
- 'guard' 'guard'
- 'withNack' 'with-nack'
- 'choose' 'choose'
- 'sync' 'sync'
- 'select' 'select'
- 'never' 'never-rv'
- 'alwaysEvt' 'always-rv'
- 'joinEvt' (no equivalent)
- structure 'rendezvous-channels'
- 'channel' 'make-channel'
- 'sameChannel' 'eq?' (R5RS)
- 'send' 'send'
- 'recv' 'receive'
- 'sendEvt' 'send-rv'
- 'recvEvt' 'receive-rv'
- 'sendPoll' (no equivalent)
- 'recvPoll' (no equivalent)
- structure 'rendezvous-time'
- 'timeOutEvt' 'after-time-rv'
- 'atTimeEvt' 'at-real-time-rv'
- structure 'SyncVar' structure 'rendezvous-placeholders'
- exception 'Put' (no equivalent)
- 'iVar' 'make-placeholder'
- 'iPut' 'placeholder-set!'
- 'iGet' 'placeholder-value'
- 'iGetEvt' 'placeholder-value-rv'
- 'iGetPoll' (no equivalent)
- 'sameIVar' 'eq?' (R5RS)
- structure 'jars'
- 'mVar' 'make-jar'
- 'mVarInit' (no equivalent)
- 'mPut' 'jar-put!'
- 'mTake' 'jar-take'
- 'mTakeEvt' 'jar-take-rv'
- 'mGet' (no equivalent)
- 'mGetEvt' (no equivalent)
- 'mTakePoll' (no equivalent)
- 'mGetPoll' (no equivalent)
- 'mSwap' (no equivalent)
- 'mSwapEvt' (no equivalent)
- 'sameMVar' 'eq?' (R5RS)
- structure 'Mailbox' structure 'rendezvous-async-channels'
- 'mailbox' 'make-async-channel'
- 'sameMailbox' 'eq?' (R5RS)
- 'send' 'send-async'
- 'recv' 'receive-async'
- 'recvEvt' 'receive-async-rv'
- 'recvPoll' (no equivalent)
- File: scheme48.info, Node: Pessimistic concurrency, Next: Custom thread synchronization, Prev: Concurrent ML, Up: Multithreading
- 5.5 Pessimistic concurrency
- ===========================
- While Scheme48's primitive thread synchronization mechanisms revolve
- around optimistic concurrency, Scheme48 still provides the more
- well-known mechanism of pessimistic concurrency, or mutual exclusion,
- with locks. Note that Scheme48's pessimistic concurrency facilities are
- discouraged, and very little of the system uses them (at the time this
- documentation was written, none of the system uses locks), and the
- pessimistic concurrency libraries are limited to just locks; condition
- variables are integrated only with optimistic concurrency. Except for
- inherent applications of pessimistic concurrency, it is usually better
- to use optimistic concurrency in Scheme48.
- These names are exported by the 'locks' structure.
- -- procedure: make-lock --> lock
- -- procedure: lock? --> boolean
- -- procedure: obtain-lock lock --> unspecified
- -- procedure: maybe-obtain-lock lock --> boolean
- -- procedure: release-lock lock --> unspecified
- 'Make-lock' creates a new lock in the 'released' lock state.
- 'Lock?' is the disjoint type predicate for locks. 'Obtain-lock'
- atomically checks to see if LOCK is in the 'released' state: if it
- is, LOCK is put into the 'obtained' lock state; otherwise,
- 'obtain-lock' waits until LOCK is ready to be obtained, at which
- point it is put into the 'obtained' lock state.
- 'Maybe-obtain-lock' atomically checks to see if LOCK is in the
- 'released' state: if it is, LOCK is put into the 'obtained' lock
- state, and 'maybe-obtain-lock' returns '#t'; if it is in the
- 'obtained' state, 'maybe-obtain-lock' immediately returns '#f'.
- 'Release-lock' sets LOCK's state to be 'released,' letting the next
- thread waiting to obtain it do so.
- File: scheme48.info, Node: Custom thread synchronization, Prev: Pessimistic concurrency, Up: Multithreading
- 5.6 Custom thread synchronization
- =================================
- Along with several useful thread synchronization abstraction facilities
- built-in to Scheme48, there is also a simple and lower-level mechanism
- for suspending & resuming threads. The following bindings are exported
- from the 'threads-internal' structure.
- Threads have a field for a cell (*note Cells::) that is used when the
- thread is suspended. When it is ready to run, it is simply '#f'.
- Suspending a thread involves setting its cell to a cell accessible
- outside, so the thread can later be awoken. When the thread is awoken,
- its cell field and the contents of the cell are both set to '#f'.
- Often, objects involved in the synchronization of threads will have a
- queue (*note Queues::) of thread cells. There are two specialized
- operations on thread cell queues that simplify filtering out cells of
- threads that have already been awoken.
- -- procedure: maybe-commit-and-block cell --> boolean
- -- procedure: maybe-commit-and-block-on-queue --> boolean
- These attempt to commit the current proposal. If the commit fails,
- they immediately return '#f'. Otherwise, they suspend the current
- thread. 'Maybe-commit-and-block' first sets the current thread's
- cell to CELL, which should contain the current thread.
- 'Maybe-commit-and-block-on-queue' adds a cell containing the
- current thread to QUEUE first. When the current thread is finally
- resumed, these return '#t'.
- -- procedure: maybe-commit-and-make-ready thread-or-queue --> boolean
- Attempts to commit the current proposal. If the commit fails, this
- returns '#f'. Otherwise, 'maybe-commit-and-make-ready' awakens the
- specified thread[s] by clearing the thread/each thread's cell and
- sending a message to the relevant scheduler[s] and returns '#t'.
- If THREAD-OR-QUEUE is a thread, it simply awakens that; if it is a
- queue, it empties the queue and awakens each thread in it.
- -- procedure: maybe-dequeue-thread! thread-cell-queue --> thread or
- boolean
- -- procedure: thread-queue-empty? thread-cell-queue --> boolean
- 'Maybe-dequeue-thread!' returns the next thread cell's contents in
- the queue of thread cells THREAD-CELL-QUEUE. It removes cells that
- have been emptied, i.e. whose threads have already been awoken.
- 'Thread-queue-empty?' returns '#t' if there are no cells in
- THREAD-CELL-QUEUE that contain threads, i.e. threads that are still
- suspended. It too removes cells that have been emptied.
- For example, the definition of placeholders (*note Higher-level
- synchronization::) is presented here. Placeholders contain two fields:
- the cached value (set when the placeholder is set) & a queue of threads
- waiting (set to '#f' when the placeholder is assigned).
- (define-synchronized-record-type placeholder :placeholder
- (really-make-placeholder queue)
- (value queue) ; synchronized fields
- placeholder?
- (queue placeholder-queue set-placeholder-queue!)
- (value placeholder-real-value set-placeholder-value!))
- (define (make-placeholder)
- (really-make-placeholder (make-queue)))
- (define (placeholder-value placeholder)
- ;; Set up a new proposal for the transaction.
- (with-new-proposal (lose)
- (cond ((placeholder-queue placeholder)
- ;; There's a queue of waiters. Attempt to commit the
- ;; proposal and block. We'll be added to the queue if the
- ;; commit succeeds; if it fails, retry.
- => (lambda (queue)
- (or (maybe-commit-and-block-on-queue queue)
- (lose))))))
- ;; Once our thread has been awoken, the placeholder will be set.
- (placeholder-real-value placeholder))
- (define (placeholder-set! placeholder value)
- ;; Set up a new proposal for the transaction.
- (with-new-proposal (lose)
- (cond ((placeholder-queue placeholder)
- => (lambda (queue)
- ;; Clear the queue, set the value field.
- (set-placeholder-queue! placeholder #f)
- (set-placeholder-value! placeholder value)
- ;; Attempt to commit our changes and awaken all of the
- ;; waiting threads. If the commit fails, retry.
- (if (not (maybe-commit-and-make-ready queue))
- (lose))))
- (else
- ;; Someone assigned it first. Since placeholders are
- ;; single-assignment cells, this is an error.
- (error "placeholder is already assigned"
- placeholder
- (placeholder-real-value placeholder))))))
- File: scheme48.info, Node: Libraries, Next: C interface, Prev: Multithreading, Up: Top
- 6 Libraries
- ***********
- This chapter details a number of useful libraries built-in to Scheme48.
- * Menu:
- * Boxed bitwise-integer masks::
- * Enumerated/finite types and sets::
- * Macros for writing loops::
- * Library data structures::
- * I/O extensions::
- * TCP & UDP sockets::
- * Common-Lisp-style formatting::
- * Library utilities::
- File: scheme48.info, Node: Boxed bitwise-integer masks, Next: Enumerated/finite types and sets, Up: Libraries
- 6.1 Boxed bitwise-integer masks
- ===============================
- Scheme48 provides a facility for generalized boxed bitwise-integer
- masks. Masks represent sets of elements. An element is any arbitrary
- object that represents an index into a bit mask; mask types are
- parameterized by an isomorphism between elements and their integer
- indices. Usual abstract set operations are available on masks. The
- mask facility is divided into two parts: the 'mask-types' structure,
- which provides the operations on the generalized mask type descriptors;
- and the 'masks' structure, for the operations on masks themselves.
- 6.1.1 Mask types
- ----------------
- -- procedure: make-mask-type name elt? index->elt elt->index size -->
- mask-type
- -- procedure: mask-type? object --> boolean
- -- procedure: mask? object --> boolean
- 'Make-mask-type' constructs a mask type with the given name.
- Elements of this mask type must satisfy the predicate ELT?.
- INTEGER->ELT is a unary procedure that maps bit mask indices to
- possible set elements; ELT->INTEGER maps possible set elements to
- bit mask indices. SIZE is the number of possible elements of masks
- of the new type, i.e. the number of bits needed to represent the
- internal bit mask. 'Mask?' is the disjoint type predicate for mask
- objects.
- -- procedure: mask-type mask --> mask-type
- -- procedure: mask-has-type? mask type --> boolean
- 'Mask-type' returns MASK's type. 'Mask-has-type?' returns '#t' if
- MASK's type is the mask type TYPE or '#f' if not.
- The 'mask-types' structure, not the 'masks' structure, exports
- 'mask?' and 'mask-has-type?': it is expected that programmers who
- implement mask types will define type predicates for masks of their type
- based on 'mask?' and 'mask-has-type?', along with constructors &c. for
- their masks.
- -- procedure: integer->mask type integer --> mask
- -- procedure: list->mask type elts --> mask
- 'Integer->mask' returns a mask of type TYPE that contains all the
- possible elements E of the type TYPE such that the bit at E's index
- is set. 'List->mask' returns a mask whose type is TYPE containing
- all of the elements in the list ELTS.
- 6.1.2 Masks
- -----------
- -- procedure: mask->integer mask --> integer
- -- procedure: mask->list mask --> element-list
- 'Mask->integer' returns the integer bit set that MASK uses to
- represent the element set. 'Mask->list' returns a list of all the
- elements that MASK contains.
- -- procedure: mask-member? mask elt --> boolean
- -- procedure: mask-set mask elt ... --> mask
- -- procedure: mask-clear mask elt ... --> mask
- 'Mask-member?' returns true if ELT is a member of the mask MASK, or
- '#f' if not. 'Mask-set' returns a mask with all the elements in
- MASK as well as each ELT .... 'Mask-clear' returns a mask with all
- the elements in MASK but with none of ELT ....
- -- procedure: mask-union mask_{1} mask_{2} ... --> mask
- -- procedure: mask-intersection mask_{1} mask_{2} ... --> mask
- -- procedure: mask-subtract mask--_{a} mask--_{b} --> mask
- -- procedure: mask-negate mask --> mask
- Set operations on masks. 'Mask-union' returns a mask containing
- every element that is a member of any one of its arguments.
- 'Mask-intersection' returns a mask containing every element that is
- a member of every one of its arguments. 'Mask-subtract' returns a
- mask of every element that is in MASK-_{A} but not also in
- MASK-_{B}. 'Mask-negate' returns a mask whose members are every
- possible element of MASK's type that is not in MASK.
- File: scheme48.info, Node: Enumerated/finite types and sets, Next: Macros for writing loops, Prev: Boxed bitwise-integer masks, Up: Libraries
- 6.2 Enumerated/finite types and sets
- ====================================
- (This section was derived from work copyrighted (C) 1993-2005 by Richard
- Kelsey, Jonathan Rees, and Mike Sperber.)
- The structure 'finite-types' has two macros for defining "finite" or
- "enumerated record types". These are record types for which there is a
- fixed set of instances, all of which are created at the same time as the
- record type itself. Also, the structure 'enum-sets' has several
- utilities for building sets of the instances of those types, although it
- is generalized beyond the built-in enumerated/finite type device. There
- is considerable overlap between the boxed bitwise-integer mask library
- (*note Boxed bitwise-integer masks::) and the enumerated set facility.
- 6.2.1 Enumerated/finite types
- -----------------------------
- -- syntax: define-enumerated-type
- (define-enumerated-type DISPATCHER TYPE
- PREDICATE
- INSTANCE-VECTOR
- NAME-ACCESSOR
- INDEX-ACCESSOR
- (INSTANCE-NAME
- ...))
- This defines a new record type, to which TYPE is bound, with as
- many instances as there are INSTANCE-NAMEs. PREDICATE is defined
- to be the record type's predicate. INSTANCE-VECTOR is defined to
- be a vector containing the instances of the type in the same order
- as the INSTANCE-NAME list. DISPATCHER is defined to be a macro of
- the form (DISPATCHER INSTANCE-NAME); it evaluates to the instance
- with the given name, which is resolved at macro-expansion time.
- NAME-ACCESSOR & INDEX-ACCESSOR are defined to be unary procedures
- that return the symbolic name & index into the instance vector,
- respectively, of the new record type's instances.
- For example,
- (define-enumerated-type colour :colour
- colour?
- colours
- colour-name
- colour-index
- (black white purple maroon))
- (colour-name (vector-ref colours 0)) => black
- (colour-name (colour white)) => white
- (colour-index (colour purple)) => 2
- -- syntax: define-finite-type
- (define-finite-type DISPATCHER TYPE
- (FIELD-TAG ...)
- PREDICATE
- INSTANCE-VECTOR
- NAME-ACCESSOR
- INDEX-ACCESSOR
- (FIELD-TAG ACCESSOR [MODIFIER])
- ...
- ((INSTANCE-NAME FIELD-VALUE ...)
- ...))
- This is like 'define-enumerated-type', but the instances can also
- have added fields beyond the name and the accessor. The first list
- of field tags lists the fields that each instance is constructed
- with, and each instance is constructed by applying the unnamed
- constructor to the initial field values listed. Fields not listed
- in the first field tag list must be assigned later.
- For example,
- (define-finite-type colour :colour
- (red green blue)
- colour?
- colours
- colour-name
- colour-index
- (red colour-red)
- (green colour-green)
- (blue colour-blue)
- ((black 0 0 0)
- (white 255 255 255)
- (purple 160 32 240)
- (maroon 176 48 96)))
- (colour-name (colour black)) => black
- (colour-name (vector-ref colours 1)) => white
- (colour-index (colour purple)) => 2
- (colour-red (colour maroon)) => 176
- 6.2.2 Sets over enumerated types
- --------------------------------
- -- syntax: define-enum-set-type
- (define-enum-set-type SET-SYNTAX TYPE
- PREDICATE
- LIST->X-SET
- ELEMENT-SYNTAX
- ELEMENT-PREDICATE
- ELEMENT-VECTOR
- ELEMENT-INDEX)
- This defines SET-SYNTAX to be a syntax for constructing sets, TYPE
- to be an object that represents the type of enumerated sets,
- PREDICATE to be a predicate for those sets, and LIST->X-SET to be a
- procedure that converts a list of elements into a set of the new
- type.
- ELEMENT-SYNTAX must be the name of a macro for constructing set
- elements from names (akin to the DISPATCHER argument to the
- 'define-enumerated-type' & 'define-finite-type' forms).
- ELEMENT-PREDICATE must be a predicate for the element type,
- ELEMENT-VECTOR a vector of all values of the element type, and
- ELEMENT-INDEX a procedure that returns the index of an element
- within ELEMENT-VECTOR.
- -- procedure: enum-set->list enum-set --> element list
- -- procedure: enum-set-member? enum-set element --> boolean
- -- procedure: enum-set=? enum-set--_{a} enum-set--_{b} --> boolean
- -- procedure: enum-set-union enum-set--_{a} enum-set--_{b} --> enum-set
- -- procedure: enum-set-intersection enum-set--_{a} enum-set--_{b} -->
- enum-set
- -- procedure: enum-set-negation enum-set --> enum-set
- 'Enum-set->list' returns a list of elements within ENUM-SET.
- 'Enum-set-member?' tests whether ELEMENT is a member of ENUM-SET.
- 'Enum-set=?' tests whether two enumerated sets are equal, i.e.
- contain all the same elements. The other procedures perform
- standard set algebra operations on enumerated sets. It is an error
- to pass an element that does not satisfy ENUM-SET's predicate to
- 'enum-set-member?' or to pass two enumerated sets of different
- types to 'enum-set=?' or the enumerated set algebra operators.
- Here is a simple example of enumerated sets built atop the enumerated
- types described in the previous section:
- (define-enumerated-type colour :colour
- colour?
- colours
- colour-name
- colour-index
- (red blue green))
- (define-enum-set-type colour-set :colour-set
- colour-set?
- list->colour-set
- colour colour? colours colour-index)
- (enum-set->list (colour-set red blue))
- => (#{Colour red} #{Colour blue})
- (enum-set->list (enum-set-negation (colour-set red blue)))
- => (#{Colour green})
- (enum-set-member? (colour-set red blue) (colour blue))
- => #t
- File: scheme48.info, Node: Macros for writing loops, Next: Library data structures, Prev: Enumerated/finite types and sets, Up: Libraries
- 6.3 Macros for writing loops
- ============================
- (This section was derived from work copyrighted (C) 1993-2005 by Richard
- Kelsey, Jonathan Rees, and Mike Sperber.)
- 'Iterate' & 'reduce' are extensions of named-'let' for writing loops
- that walk down one or more sequences, such as the elements of a list or
- vector, the characters read from a port, or an arithmetic series.
- Additional sequences can be defined by the user. 'Iterate' & 'reduce'
- are exported by the structure 'reduce'.
- * Menu:
- * Main looping macros::
- * Sequence types::
- * Synchronous sequences::
- * Examples::
- * Defining sequence types::
- * Loop macro expansion::
- File: scheme48.info, Node: Main looping macros, Next: Sequence types, Up: Macros for writing loops
- 6.3.1 Main looping macros
- -------------------------
- -- syntax: iterate
- (iterate LOOP-NAME ((SEQ-TYPE ELT-VAR ARG ...)
- ...)
- ((STATE-VAR INIT)
- ...)
- BODY
- [TAIL-EXP])
- 'Iterate' steps the ELT-VARs in parallel through the sequences,
- while each STATE-VAR has the corresponding INIT for the first
- iteration and later values supplied by the body. If any sequence
- has reached the limit, the value of the 'iterate' expression is the
- value of TAIL-EXP, if present, or the current values of the
- STATE-VARs, returned as multiple values. If no sequence has
- reached its limit, BODY is evaluated and either calls LOOP-NAME
- with new values for the STATE-VARs or returns some other value(s).
- The LOOP-NAME and the STATE-VARs & INITs behave exactly as in
- named-'let', in that LOOP-NAME is bound only in the scope of BODY,
- and each INIT is evaluated parallel in the enclosing scope of the
- whole expression. Also, the arguments to the sequence constructors
- will be evaluated in the enclosing scope of the whole expression,
- or in an extension of that scope peculiar to the sequence type.
- The named-'let' expression
- (let LOOP-NAME ((STATE-VAR INIT) ...)
- BODY
- ...)
- is equivalent to an iterate expression with no sequences (and with
- an explicit 'let' wrapped around the body expressions to take care
- of any internal definitions):
- (iterate LOOP-NAME ()
- ((STATE-VAR INIT) ...)
- (let () BODY ...))
- The SEQ-TYPEs are keywords (actually, macros of a particular form,
- which makes it easy to add additional types of sequences; see
- below). Examples are 'list*', which walks down the elements of a
- list, and 'vector*', which does the same for vectors. For each
- iteration, each ELT-VAR is bound to the next element of the
- sequence. The ARGs are supplied to the sequence processors as
- other inputs, such as the list or vector to walk down.
- If there is a TAIL-EXP, it is evaluated when the end of one or more
- sequences is reached. If the body does not call LOOP-NAME,
- however, the TAIL-EXP is not evaluated. Unlike named-'let', the
- behaviour of a non-tail-recursive call to LOOP-NAME is unspecified,
- because iterating down a sequence may involve side effects, such as
- reading characters from a port.
- -- syntax: reduce
- (reduce ((SEQ-TYPE ELT-VAR ARG ...)
- ...)
- ((STATE-VAR INIT)
- ...)
- BODY
- [TAIL-EXP])
- If an 'iterate' expression is not meant to terminate before a
- sequence has reached its end, the body will always end with a tail
- call to LOOP-NAME. 'Reduce' is a convenient macro that makes this
- common case explicit. The syntax of 'reduce' is the same as that
- of 'iterate', except that there is no LOOP-NAME, and the body
- updates the state variables by returning multiple values in the
- stead of passing the new values to LOOP-NAME: the body must return
- as many values as there are state variables. By special
- dispension, if there are no state variables, then the body may
- return any number of values, all of which are ignored.
- The value(s) returned by an instance of 'reduce' is (are) the
- value(s) returned by the TAIL-EXP, if present, or the current
- value(s) of the state variables when the end of one or more
- sequences is reached.
- A 'reduce' expression can be rewritten as an equivalent 'iterate'
- expression by adding a LOOP-NAME and a wrapper for the body that
- calls the LOOP-NAME:
- (iterate loop ((SEQ-TYPE ELT-VAR ARG ...)
- ...)
- ((STATE-VAR INIT)
- ...)
- (call-with-values (lambda () BODY)
- loop)
- [TAIL-EXP])
- File: scheme48.info, Node: Sequence types, Next: Synchronous sequences, Prev: Main looping macros, Up: Macros for writing loops
- 6.3.2 Sequence types
- --------------------
- -- sequence type: list* elt-var list
- -- sequence type: vector* elt-var vector
- -- sequence type: string* elt-var string
- -- sequence type: count* elt-var start [end [step]]
- -- sequence type: input* elt-var input-port reader-proc
- -- sequence type: stream* elt-var proc initial-seed
- For lists, vectors, & strings, the ELT-VAR is bound to the
- successive elements of the list or vector, or the successive
- characters of the string.
- For 'count*', the ELT-VAR is bound to the elements of the sequence
- 'start, start + step, start + 2*step, ..., end', inclusive of START
- and exclusive of END. The default STEP is '1', and the sequence
- does not terminate if no END is given or if there is no N > 0 such
- that END = START + NSTEP. ('=' is used to test for termination.)
- For example, '(count* i 0 -1)' does not terminate because it begins
- past the END value, and '(count* i 0 1 2)' does not terminate
- because it skips over the END value.
- For 'input*', the elements are the results of successive
- applications of READER-PROC to INPUT-PORT. The sequence ends when
- the READER-PROC returns an end-of-file object, i.e. a value that
- satisfies 'eof-object?'.
- For 'stream*', the PROC receives the current seed as an argument
- and must return two values, the next value of the sequence & the
- next seed. If the new seed is '#f', then the previous element was
- the last one. For example, '(list* elt list)' is the same as
- (stream* elt
- (lambda (list)
- (if (null? list)
- (values 'ignored #f)
- (values (car list) (cdr list))))
- list)
- File: scheme48.info, Node: Synchronous sequences, Next: Examples, Prev: Sequence types, Up: Macros for writing loops
- 6.3.3 Synchronous sequences
- ---------------------------
- When using the sequence types described above, a loop terminates when
- any of its sequences terminate. To help detect bugs, it is useful to
- also have sequence types that check whether two or more sequences end on
- the same iteration. For this purpose, there is a second set of sequence
- types called "synchronous sequences". Synchronous sequences are like
- ordinary asynchronous sequences in every respect except that they cause
- an error to be signalled if a loop is terminated by a synchronous
- sequence and some other synchronous sequence did not reach its end on
- the same iteration.
- Sequences are checked for termination in order from left to right,
- and if a loop is terminated by an asynchronous sequence no further
- checking is done.
- -- synchronous sequence type: list% elt-var list
- -- synchronous sequence type: vector% elt-var vector
- -- synchronous sequence type: string% elt-var string
- -- synchronous sequence type: count% elt-var start end [step]
- -- synchronous sequence type: input% elt-var input-port reader-proc
- -- synchronous sequence type: stream% elt-var proc initial-seed
- These are all identical to their asynchronous equivalents above,
- except that they are synchronous. Note that 'count%''s END
- argument is required, unlike 'count*''s, because it would be
- nonsensical to check for termination of a sequence that does not
- terminate.
- File: scheme48.info, Node: Examples, Next: Defining sequence types, Prev: Synchronous sequences, Up: Macros for writing loops
- 6.3.4 Examples
- --------------
- Gathering the indices of list elements that answer true to some
- predicate.
- (define (select-matching-items list pred)
- (reduce ((list* elt list)
- (count* i 0))
- ((hits '()))
- (if (pred elt)
- (cons i hits)
- hits)
- (reverse hits)))
- Finding the index of an element of a list that satisfies a predicate.
- (define (find-matching-item list pred)
- (iterate loop ((list* elt list)
- (count* i 0))
- () ; no state variables
- (if (pred elt)
- i
- (loop))))
- Reading one line of text from an input port.
- (define (read-line port)
- (iterate loop ((input* c port read-char))
- ((chars '()))
- (if (char=? c #\newline)
- (list->string (reverse chars))
- (loop (cons c chars)))
- (if (null? chars)
- (eof-object) ; from the PRIMITIVES structure
- (list->string (reverse chars)))))
- Counting the lines in a file. This must be written in a way other
- than with 'count*' because it needs the value of the count after the
- loop has finished, but the count variable would not be bound then.
- (define (line-count filename)
- (call-with-input-file filename
- (lambda (inport)
- (reduce ((input* line inport read-line))
- ((count 0))
- (+ count 1)))))
- File: scheme48.info, Node: Defining sequence types, Next: Loop macro expansion, Prev: Examples, Up: Macros for writing loops
- 6.3.5 Defining sequence types
- -----------------------------
- The sequence types are object-oriented macros similar to enumerations.
- An asynchronous sequence macro needs to supply three values: '#f' to
- indicate that it is not synchronous, a list of state variables and their
- initializers, and the code for one iteration. The first two methods are
- written in continuation-passing style: they take another macro and
- argument to which to pass their result. See [Friedman 00] for more
- details on the theory behind how CPS macros work. The 'sync' method
- receives no extra arguments. The 'state-vars' method is passed a list
- of names that will be bound to the arguments of the sequence. The final
- method, for stepping the sequence forward, is passed the list of names
- bound to the arguments and the list of state variables. In addition,
- there is a variable to be bound to the next element of the sequence, the
- body expression for the loop, and an expression for terminating the
- loop.
- As an example, the definition of 'list*' is:
- (define-syntax list*
- (syntax-rules (SYNC STATE-VARS STEP)
- ((LIST* SYNC (next more))
- (next #F more))
- ((LIST* STATE-VARS (start-list) (next more))
- (next ((list-var start-list)) more))
- ((LIST* STEP (start-list) (list-var) value-var loop-body tail-exp)
- (IF (NULL? list-var)
- tail-exp
- (LET ((value-var (CAR list-var))
- (list-var (CDR list-var)))
- loop-body)))))
- Synchronized sequences are similar, except that they need to provide
- a termination test to be used when some other synchronized method
- terminates the loop. To continue the example:
- (define-syntax list%
- (syntax-rules (SYNC DONE)
- ((LIST% SYNC (next more))
- (next #T more))
- ((LIST% DONE (start-list) (list-var))
- (NULL? list-var))
- ((LIST% . anything-else)
- (LIST* . anything-else))))
- File: scheme48.info, Node: Loop macro expansion, Prev: Defining sequence types, Up: Macros for writing loops
- 6.3.6 Loop macro expansion
- --------------------------
- Here is an example of the expansion of the 'reduce' macro:
- (reduce ((list* x '(1 2 3)))
- ((r '()))
- (cons x r))
- ==>
- (let ((final (lambda (r) (values r)))
- (list '(1 2 3))
- (r '()))
- (let loop ((list list) (r r))
- (if (null? list)
- (final r)
- (let ((x (car list))
- (list (cdr list)))
- (let ((continue (lambda (r)
- (loop list r))))
- (continue (cons x r)))))))
- The only mild inefficiencies in this code are the 'final' &
- 'continue' procedures, both of which could trivially be substituted
- in-line. The macro expander could easily perform the substitution for
- 'continue' when there is no explicit proceed variable, as in this case,
- but not in general.
- File: scheme48.info, Node: Library data structures, Next: I/O extensions, Prev: Macros for writing loops, Up: Libraries
- 6.4 Library data structures
- ===========================
- Scheme48 includes several libraries for a variety of data structures.
- 6.4.1 Multi-dimensional arrays
- ------------------------------
- The 'arrays' structure exports a facility for multi-dimensional arrays,
- based on Alan Bawden's interface.
- -- procedure: make-array value dimension ... --> array
- -- procedure: array dimensions element ... --> array
- -- procedure: copy-array array ... --> array
- Array constructors. 'Make-array' constructs an array with the
- given dimensions, each of which must be an exact, non-negative
- integer, and fills all of the elements with VALUE. 'Array' creates
- an array with the given list of dimensions, which must be a list of
- exact, non-negative integers, and fills it with the given elements
- in row-major order. The number of elements must be equal to the
- product of DIMENSIONS. 'Copy-array' constructs an array with the
- same dimensions and contents as ARRAY.
- -- procedure: array? object --> boolean
- Disjoint type predicate for arrays.
- -- procedure: array-shape array --> integer-list
- Returns the list of dimensions of ARRAY.
- -- procedure: array-ref array index ... --> value
- -- procedure: array-set! array value index ... --> unspecified
- Array element dereferencing and assignment. Each INDEX must be in
- the half-open interval [0,D), where D is the respective dimension
- of ARRAY corresponding with that index.
- -- procedure: array->vector array --> vector
- Creates a vector of the elements in ARRAY in row-major order.
- -- procedure: make-shared-array array linear-map dimension ... -->
- array
- Creates a new array that shares storage with ARRAY and uses the
- procedure LINEAR-MAP to map indices in the new array to indices in
- ARRAY. LINEAR-MAP must accept as many arguments as DIMENSION ...,
- each of which must be an exact, non-negative integer; and must
- return a list of exact, non-negative integers equal in length to
- the number of dimensions of ARRAY, and which must be valid indices
- into ARRAY.
- 6.4.2 Red/black search trees
- ----------------------------
- Along with hash tables for general object maps, Scheme48 also provides
- red/black binary search trees generalized across key equality comparison
- & ordering functions, as opposed to key equality comparison & hash
- functions with hash tables. These names are exported by the
- 'search-trees' structure.
- -- procedure: make-search-tree key= key< --> search-tree
- -- procedure: search-tree? object --> boolean
- 'Make-search-tree' creates a new search tree with the given key
- equality comparison & ordering functions. 'Search-tree?' is the
- disjoint type predicate for red/black binary search trees.
- -- procedure: search-tree-ref search-tree key --> value or '#f'
- -- procedure: search-tree-set! search-tree key value --> unspecified
- -- procedure: search-tree-modify! search-tree key modifier -->
- unspecified
- 'Search-tree-ref' returns the value associated with KEY in
- SEARCH-TREE, or '#f' if no such association exists.
- 'Search-tree-set!' assigns the value of an existing association in
- SEARCH-TREE for KEY to be VALUE, if the association already exists;
- or, if not, it creates a new association with the given key and
- value. If VALUE is '#f', however, any association is removed.
- 'Search-tree-modify!' modifies the association in SEARCH-TREE for
- KEY by applying MODIFIER to the previous value of the association.
- If no association previously existed, one is created whose key is
- KEY and whose value is the result of applying MODIFIER to '#f'. If
- MODIFIER returns '#f', the association is removed. This is
- equivalent to '(search-tree-set! SEARCH-TREE KEY (MODIFIER
- (search-tree-ref SEARCH-TREE KEY)))', but it is implemented more
- efficiently.
- -- procedure: search-tree-max search-tree --> value or '#f'
- -- procedure: search-tree-min search-tree --> value or '#f'
- -- procedure: pop-search-tree-max! search-tree --> value or '#f'
- -- procedure: pop-search-tree-min! search-tree --> value or '#f'
- These all return two values: the key & value for the association in
- SEARCH-TREE whose key is the maximum or minimum of the tree.
- 'Search-tree-max' and 'search-tree-min' do not remove the
- association from SEARCH-TREE; 'pop-search-tree-max!' and
- 'pop-search-tree-min!' do. If SEARCH-TREE is empty, these all
- return the two values '#f' and '#f'.
- -- procedure: walk-search-tree proc search-tree --> unspecified
- This applies PROC to two arguments, the key & value, for every
- association in SEARCH-TREE.
- 6.4.3 Sparse vectors
- --------------------
- Sparse vectors, exported by the structure 'sparse-vectors', are vectors
- that grow as large as necessary without leaving large, empty spaces in
- the vector. They are implemented as trees of subvectors.
- -- procedure: make-sparse-vector --> sparse-vector
- Sparse vector constructor.
- -- procedure: sparse-vector-ref sparse-vector index --> value or '#f'
- -- procedure: sparse-vector-set! sparse-vector index value -->
- unspecified
- Sparse vector element accessor and modifier. In the case of
- 'sparse-vector-ref', if INDEX is beyond the highest index that was
- inserted into SPARSE-VECTOR, it returns '#f'; if
- 'sparse-vector-set!' is passed an index beyond what was already
- assigned, it simply extends the vector.
- -- procedure: sparse-vector->list sparse-vector --> list
- Creates a list of the elements in SPARSE-VECTOR. Elements that
- uninitialized gaps comprise are denoted by '#f' in the list.
- File: scheme48.info, Node: I/O extensions, Next: TCP & UDP sockets, Prev: Library data structures, Up: Libraries
- 6.5 I/O extensions
- ==================
- These facilities are all exported from the 'extended-ports' structure.
- Tracking ports track the line & column number that they are on.
- -- procedure: make-tracking-input-port sub-port --> input-port
- -- procedure: make-tracking-output-port sub-port --> output-port
- Tracking port constructors. These simply create wrapper ports
- around SUB-PORT that track the line & column numbers.
- -- procedure: current-row port --> integer or '#f'
- -- procedure: current-column port --> integer or '#f'
- Accessors for line (row) & column number information. If PORT is a
- not a tracking port, these simply return '#f'.
- -- procedure: fresh-line port --> unspecified
- This writes a newline to port with 'newline', unless it can be
- determined that the previous character was a newline -- that is, if
- '(current-column PORT)' does not evaluate to zero.
- These are ports based on procedures that produce and consume single
- characters at a time.
- -- procedure: char-source->input-port char-producer [readiness-tester
- closer] --> input-port
- -- procedure: char-sink->output-port char-consumer --> output-port
- 'Char-source->input-port' creates an input port that calls
- CHAR-PRODUCER with zero arguments when a character is read from it.
- If READINESS-TESTER is present, it is used for the 'char-ready?'
- operation on the resulting port; likewise with CLOSER and
- 'close-input-port'. 'Char-sink->output-port' creates an output
- port that calls CHAR-CONSUMER for every character written to it.
- Scheme48 also provides ports that collect and produce output to and
- from strings.
- -- procedure: make-string-input-port string --> input-port
- Constructs an input port whose contents are read from STRING.
- -- procedure: make-string-output-port --> output-port
- -- procedure: string-output-port-output string-port --> string
- -- procedure: call-with-string-output-port receiver --> string
- 'Make-string-output-port' makes an output port that collects its
- output in a string. 'String-output-port-output' returns the string
- that STRING-PORT collected. 'Call-with-string-output-port' creates
- a string output port, applies RECEIVER to it, and returns the
- string that the string output port collected.
- Finally, there is a facility for writing only a limited quantity of
- output to a given port.
- -- procedure: limit-output port count receiver --> unspecified
- 'Limit-output' applies RECEIVER to a port that will write at most
- COUNT characters to PORT.
- File: scheme48.info, Node: TCP & UDP sockets, Next: Common-Lisp-style formatting, Prev: I/O extensions, Up: Libraries
- 6.6 TCP & UDP sockets
- =====================
- Scheme48 provides a simple facility for TCP & UDP sockets. Both the
- structures 'sockets' and 'udp-sockets' export several general
- socket-related procedures:
- -- procedure: close-socket socket --> unspecified
- -- procedure: socket-port-number socket --> integer
- -- procedure: get-host-name --> string
- 'Close-socket' closes SOCKET, which may be any type of socket.
- 'Socket-port-number' returns the port number through which SOCKET
- is communicating. 'Get-host-name' returns the network name of the
- current machine.
- *Note:* Programmers should be wary of storing the result of a call
- to 'get-host-name' in a dumped heap image, because the actual
- machine's host name may vary from invocation to invocation of the
- Scheme48 VM on that image, since heap images may be resumed on
- multiple different machines.
- 6.6.1 TCP sockets
- -----------------
- The 'sockets' structure provides simple TCP socket facilities.
- -- procedure: open-socket [port-number] --> socket
- -- procedure: socket-accept socket --> [input-port output-port]
- The server interface. 'Open-socket' creates a socket that listens
- on PORT-NUMBER, which defaults to a random number above 1024.
- 'Socket-accept' blocks until there is a client waiting to be
- accepted, at which point it returns two values: an input port & an
- output port to send & receive data to & from the client.
- -- procedure: socket-client host-name port-number --> [input-port
- output-port]
- Connects to the server at PORT-NUMBER denoted by the machine name
- HOST-NAME and returns an input port and an output port for sending
- & receiving data to & from the server. 'Socket-client' blocks the
- current thread until the server accepts the connection request.
- 6.6.2 UDP sockets
- -----------------
- The 'udp-sockets' structure defines a UDP socket facility.
- -- procedure: open-udp-socket [port-number] --> socket
- Opens a UDP socket on PORT-NUMBER, or a random port number if none
- was passed. 'Open-udp-socket' returns two values: an input UDP
- socket and an output UDP socket.
- -- procedure: udp-send socket address buffer count --> count-sent
- -- procedure: udp-receive socket buffer --> [count-received
- remote-address]
- 'Udp-send' attempts to send COUNT elements from the string or byte
- vector BUFFER from the output UDP socket SOCKET to the UDP address
- ADDRESS, and returns the number of octets it successfully sent.
- 'Udp-receive' receives a UDP message from SOCKET, reading it into
- BUFFER destructively. It returns two values: the number of octets
- read into BUFFER and the address whence the octets came.
- -- procedure: lookup-udp-address name port --> udp-address
- -- procedure: udp-address? object --> boolean
- -- procedure: udp-address-address address --> c-byte-vector
- -- procedure: udp-address-port address --> port-number
- -- procedure: udp-address-hostname address --> string-address
- 'Lookup-udp-address' returns a UDP address for the machine name
- NAME at the port number PORT. 'Udp-address?' is the disjoint type
- predicate for UDP addresses. 'Udp-address-address' returns a byte
- vector that contains the C representation of ADDRESS, suitable for
- passing to C with Scheme48's C FFI. 'Udp-address-port' returns the
- port number of ADDRESS. 'Udp-address-hostname' returns a string
- representation of the IP address of ADDRESS.
|