|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547 |
- <!DOCTYPE html>
-
- <html lang="en">
- <!-- documentation for s7 -->
-
- <head>
- <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
-
- <title>s7</title>
-
- <style type="text/css">
- EM.red {color:red; font-style:normal}
- EM.normal {font-style:normal}
- EM.redb {color:red; font-weight: bold; font-style: normal}
- EM.error {color:chocolate; font-style:normal}
- EM.emdef {font-weight: bold; font-style: normal}
- EM.green {color:green; font-style:normal}
- EM.gray {color:#505050; font-style:normal}
- EM.big {font-size: 20px;
- font-style: normal;
- }
- EM.bigger {font-size: 30px;
- font-style: normal;
- }
- EM.def {font-style: normal}
-
- H1 {text-align: center}
- UL {list-style-type: none}
-
- A {text-decoration:none}
- A:hover {text-decoration:underline}
-
- A.def {font-weight: bold;
- font-style: normal;
- text-decoration:none;
- text-color:black;
- }
-
- PRE.indented {padding-left: 1.0cm;}
-
- DIV.indented {background-color: #F8F8F0;
- padding-left: 0.5cm;
- padding-right: 0.5cm;
- padding-top: 0.5cm;
- padding-bottom: 0.5cm;
- margin-bottom: 0.5cm;
- border: 1px solid gray;
- border-radius: 20px;
- -moz-border-radius: 20px;
- -webkit-border-radius: 20px;
- }
-
- DIV.header {margin-top: 60px;
- margin-bottom: 30px;
- border: 4px solid #00ff00; /* green */
- background-color: #eefdee; /* lightgreen */
- padding-left: 30px;
- }
- DIV.topheader {margin-top: 10px;
- margin-bottom: 40px;
- border: 4px solid #00ff00; /* green */
- background-color: #f5f5dc; /* beige */
- font-family: 'Helvetica';
- font-size: 30px;
- text-align: center;
- padding-top: 10px;
- padding-bottom: 10px;
- }
- DIV.separator {margin-top: 30px;
- margin-bottom: 10px;
- border: 2px solid #00ff00; /* green */
- background-color: #f5f5dc; /* beige */
- padding-top: 4px;
- width: 30%;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- }
-
- DIV.bluishbordered {background-color: #f2f4ff;
- border: 1px solid #000000;
- padding-left: 10px;
- padding-right: 10px;
- padding-top: 10px;
- padding-bottom: 10px;
- }
- DIV.brownishbordered {background-color: #fbfbf0;
- border: 1px solid #000000;
- padding-left: 10px;
- padding-right: 10px;
- padding-top: 10px;
- padding-bottom: 10px;
- }
- BODY.body {background-color: #ffffff; /* white */
- margin-right: 20px;
- margin-left: 20px;
- }
- DIV.orange {background-color: #ffa500; /* orange */
- padding-left: 6px;
- padding-right: 6px;
- padding-top: 4px;
- padding-bottom: 4px;
- border: 4px solid #000000;
- font-family: 'Helvetica';
- font-size: 12px;
- font-weight: bold;
- text-align: center;
- margin-left: 0.5cm;
- margin-right: 1.0cm;
- float: right;
- border-radius: 8px;
- -moz-border-radius: 8px;
- -webkit-border-radius: 8px;
- }
- DIV.listener {background-color: #f0f8ff;
- font-family: 'Monospace';
- padding-left: 6px;
- padding-right: 6px;
- padding-bottom: 4px;
- margin-left: 1.0cm;
- margin-right: 4.0cm;
- border: 2px solid #a0a0a0;
- }
- LI.li_header {padding-top: 20px;}
-
- SUMMARY.indented {background-color: #F8F8F0;
- padding-top: 8px;
- padding-bottom: 8px;
- margin-bottom: 0.5cm;
- border: 1px solid gray;
- text-align: center;
- width: 30%;
- border-radius: 8px;
- -moz-border-radius: 8px;
- -webkit-border-radius: 8px;
- }
- </style>
- </head>
- <body class="body">
-
- <!-- INDEX s7doc:s7 scheme -->
-
-
- <div class="topheader" id="s7doc">s7
- </div>
-
-
- <p>s7 is a Scheme implementation intended as an extension language
- for other applications, primarily Snd, Radium, and Common Music. It exists as just two files, s7.c and
- s7.h, that want only to disappear into someone else's source tree. There are no libraries,
- no run-time init files, and no configuration scripts.
- It can be built as a stand-alone
- interpreter (see <a href="#repl">below</a>). s7test.scm is a regression test for s7.
- A tarball is available: <a href="ftp://ccrma-ftp.stanford.edu/pub/Lisp/s7.tar.gz">s7 tarball</a>.
- </p>
-
- <p>
- s7 is the default extension language of Snd and sndlib (<a href="http://ccrma.stanford.edu/software/snd/index.html">snd</a>),
- Rick Taube's Common Music (commonmusic at sourceforge), and Kjetil Matheussen's Radium music editor.
- There are X, Motif, Gtk, and openGL bindings
- in libxm in the Snd tarball, or at ftp://ccrma-ftp.stanford.edu/pub/Lisp/libxm.tar.gz.
- If you're running s7 in a context
- that has getenv, file-exists?, and system, you can use s7-slib-init.scm
- to gain easy access to slib. This init file is named "s7.init" in the slib distribution.
- </p>
-
- <p>Although it is a descendant of tinyScheme, s7 is closest as a Scheme dialect to Guile 1.8.
- I believe it is compatible with <a href="#s7vsr5rs">r5rs</a> and <a href="#r7rs">r7rs</a>: you can just ignore all the additions discussed in this file.
- It has continuations,
- ratios, complex numbers,
- macros, keywords, hash-tables,
- multiprecision arithmetic,
- generalized set!, unicode, and so on.
- It does not have syntax-rules or any of
- its friends, and it does not think there is any such thing
- as an inexact integer.
- </p>
-
- <p>This file assumes you know about Scheme and all its problems,
- and want a quick tour of where s7 is different. (Well, it was quick once upon a time).
- The main difference: if it's in s7, it's a first-class citizen of s7, and that includes
- macros, environments, and syntactic forms.
- </p>
-
- <br>
- <blockquote>
- <div class="indented">
- <p>I originally used a small font for scholia, but now I have to squint
- to read that tiny text, so less-than-vital commentaries are shown in the normal font, but
- indented and on a sort of brownish background.
- </p>
- </div>
- </blockquote>
- <br>
-
- <!--
- <div class="orange">
- <p>Danger!</p>
- <p>Men Working</p>
- <p>in Trees</p>
- </div>
- -->
-
- <ul>
- <li><a href="#multiprecision">arbitrary precision arithmetic</a>
- <li><a href="#math">math functions</a>
- <li><a href="#define*">define*, named let*</a>
- <li><a href="#macros">define-macro</a>
- <li><a href="#pws">procedure-setter</a>
- <li><a href="#generalizedset">generalized set!</a>
- <li><a href="#multidimensionalvectors">multidimensional vectors</a>
- <li><a href="#hashtables">hash tables</a>
- <li><a href="#environments">environments</a>
- <li><a href="#multiplevalues">multiple-values</a>
- <li><a href="#callwithexit1">call-with-exit</a>
- <li><a href="#format1">format</a>
- <li><a href="#hooks">hooks</a>
- <li><a href="#procedureinfo">procedure info</a>
- <li><a href="#evalstring">eval</a>
- <li><a href="#IO">IO and other OS functions</a>
- <li><a href="#errors">errors</a>
- <li><a href="#autoload">autoload</a>
- <li><a href="#constants">define-constant, symbol-access</a>
- <li><a href="#miscellanea">marvels and curiousities:</a>
-
- <ul>
- <li><a href="#loadpath">*load-path*</a>, <a href="#featureslist">*features*</a>, <a href="#sharpreaders">*#readers*</a>,
- <li><a href="#makelist">make-list</a>, <a href="#charposition">char-position</a>, <a href="#keywords">keywords</a>
- <li><a href="#symboltable">symbol-table</a>, <a href="#s7help">help</a>, <a href="#s7gc">gc</a>, <a href="#morallyequalp">morally-equal?</a>
- <li><a href="#expansion">define-expansion</a>, <a href="#s7env">*s7*</a>, <a href="#s7vsr5rs">r5rs</a>, <a href="#r7rs">r7rs</a>,
- <li><a href="#profiling">profiling</a>, <a href="#circle">circular lists</a>, <a href="#legolambda">legolambda</a>, etc...
- </ul>
-
- <li class="li_header"><a href="#FFIexamples">FFI examples</a>
- <ul>
- <li><a href="#repl">read-eval-print loop (and emacs)</a>
- <li><a href="#defun">define a function with arguments and a returned value, and define a variable </a>
- <li><a href="#defvar">call a Scheme function from C, and get/set Scheme variable values in C</a>
- <li><a href="#juce">C++ and Juce</a>
- <li><a href="#sndlib">load sndlib using the Xen functions and macros</a>
- <li><a href="#pwstype">add a new Scheme type and a procedure with a setter</a>
- <li><a href="#functionportexample">redirect display output to a C procedure</a>
- <li><a href="#extendop">extend a built-in operator ("+" in this case)</a>
- <li><a href="#definestar1">C-side define* (s7_define_function_star)</a>
- <li><a href="#definemacro1">C-side define-macro (s7_define_macro)</a>
- <li><a href="#definegeneric">define a generic function in C</a>
- <li><a href="#signal">signal handling (C-C to break out of an infinite loop)</a>
- <li><a href="#vector">direct multidimensional vector element access</a>
- <li><a href="#notify">notification in C that a Scheme variable has been set!</a>
- <li><a href="#namespace">Load C defined stuff into a separate namespace</a>
- <li><a href="#Cerrors">Error handling in C</a>
- <li><a href="#testhook">Hooks in C and Scheme</a>
- <li><a href="#dload">Load a C module dynamically</a>
- <li><a href="#gmpex">gmp and friends</a>
- <li><a href="#glistener">glistener.c</a>
- <li><a href="#gdb">gdb</a>
- </ul>
-
- <li class="li_header"><a href="#s7examples">s7 examples</a>
- <ul>
- <li><a href="#cload">cload.scm</a>
- <ul>
- <li><a href="#libc">libc</a>
- <li><a href="#libgsl">libgsl</a>
- <li><a href="#libgdbm">libgdbm</a>
- </ul>
- <li><a href="#schemerepl">repl.scm</a>
- <li><a href="#lint">lint.scm</a>
- </ul>
- </ul>
-
-
- <div class="header" id="multiprecision"><h4>multiprecision arithmetic</h4></div>
-
- <p>All numeric types, integers, ratios, reals, and complex numbers are supported.
- The basic integer and real
- types are defined in s7.h, defaulting to long long int and double.
- A ratio consists of two integers, a complex number two reals.
- pi is predefined, as are
- most-positive-fixnum and most-negative-fixnum.
- s7 can be built with multiprecision support
- for all types, using the gmp, mpfr, and mpc libraries (set WITH_GMP to 1 in s7.c).
- If multiprecision arithmetic is
- enabled, the following functions are included: bignum, and bignum?, and the variable (*s7* 'bignum-precision).
- (*s7* 'bignum-precision) defaults to 128; it sets the number of bits each float takes.
- pi automatically reflects the current (*s7* 'bignum-precision):
- </p>
-
- <pre class="indented">
- > pi
- <em class="gray">3.141592653589793238462643383279502884195E0</em>
- > (*s7* 'bignum-precision)
- <em class="gray">128</em>
- > (set! (*s7* 'bignum-precision) 256)
- <em class="gray">256</em>
- > pi
- <em class="gray">3.141592653589793238462643383279502884197169399375105820974944592307816406286198E0</em>
- </pre>
-
- <p>
- <em class=def id="bignump">bignum?</em> returns #t if its argument is a big number of some type; I use "bignum"
- for any big number, not just integers. To create a big number,
- either include enough digits to overflow the default types, or use the <em class=def id="bignum">bignum</em> function.
- Its argument is a string representing the desired number:
- </p>
-
- <pre class="indented">
- > (bignum "123456789123456789")
- <em class="gray">123456789123456789</em>
- > (bignum "1.123123123123123123123123123")
- <em class="gray">1.12312312312312312312312312300000000009E0</em>
- </pre>
-
-
- <blockquote>
- <div class="indented">
-
- <p>In the non-gmp case, if s7 is built using doubles (s7_double in s7.h), the float "epsilon" is
- around (expt 2 -53), or about 1e-16. In the gmp case, it is around (expt 2 (- (*s7* 'bignum-precision))).
- So in the default case (precision = 128), using gmp:
- </p>
-
- <pre class="indented">
- > (= 1.0 (+ 1.0 (expt 2.0 -128)))
- <em class="gray">#t</em>
- > (= 1.0 (+ 1.0 (expt 2.0 -127)))
- <em class="gray">#f</em>
- </pre>
-
- <p>and in the non-gmp case:
- </p>
-
- <pre class="indented">
- > (= 1.0 (+ 1.0 (expt 2 -53)))
- <em class="gray">#t</em>
- > (= 1.0 (+ 1.0 (expt 2 -52)))
- <em class="gray">#f</em>
- </pre>
-
- <p>In the gmp case, integers and ratios are limited only by the size of memory,
- but reals are limited by (*s7* 'bignum-precision). This means, for example, that
- </p>
-
- <pre class="indented">
- > (floor 1e56) ; (*s7* 'bignum-precision) is 128
- <em class="gray">99999999999999999999999999999999999999927942405962072064</em>
- > (set! (*s7* 'bignum-precision) 256)
- <em class="gray">256</em>
- > (floor 1e56)
- <em class="gray">100000000000000000000000000000000000000000000000000000000</em>
- </pre>
-
- <p>The non-gmp case is similar, but it's easy to find the edge cases:
- </p>
-
- <pre class="indented">
- > (floor (+ 0.9999999995 (expt 2.0 23)))
- <em class="gray">8388609</em>
- </pre>
- </div>
- </blockquote>
-
-
-
-
-
- <div class="header" id="math"><h4>math functions</h4></div>
-
-
- <p>
- s7 includes:
- </p>
-
- <ul>
- <li>sinh, cosh, tanh, asinh, acosh, atanh
- <li>logior, logxor, logand, lognot, logbit?, ash, integer-length, integer-decode-float
- <li>random
- <li>nan?, infinite?
- </ul>
-
- <p>
- The random function can take any numeric argument, including 0.
- The following constants are predefined: pi, most-positive-fixnum, most-negative-fixnum.
- Other math-related differences between s7 and r5rs:
- </p>
-
- <ul>
- <li>rational? and exact mean integer or ratio (i.e. not floating point), inexact means not exact.
- <li>floor, ceiling, truncate, and round return (exact) integer results.
- <li>"#" does not stand for an unknown digit.
- <li>the "@" complex number notation is not supported ("@" is an exponent marker in s7).
- <li>"+i" is not considered a number; include the real part.
- <li>modulo, remainder, and quotient take integer, ratio, or real arguments.
- <li>lcm and gcd can take integer or ratio arguments.
- <li>log takes an optional second argument, the base.
- <li>'.' and an exponent can occur in a number in any base.
- <li>rationalize returns a ratio!
- <li>case is significant in numbers, as elsewhere: #b0 is 0, but #B0 is an error.
- </ul>
-
- <pre class="indented">
- > (exact? 1.0)
- <em class="gray">#f</em>
- > (rational? 1.5)
- <em class="gray">#f</em>
- > (floor 1.4)
- <em class="gray">1</em>
- > (remainder 2.4 1)
- <em class="gray">0.4</em>
- > (modulo 1.4 1.0)
- <em class="gray">0.4</em>
- > (lcm 3/4 1/6)
- <em class="gray">3/2</em>
- > (log 8 2)
- <em class="gray">3</em>
- > (number->string 0.5 2)
- <em class="gray">"0.1"</em>
- > (string->number "0.1" 2)
- <em class="gray">0.5</em>
- > (rationalize 1.5)
- <em class="gray">3/2</em>
- > (complex 1/2 0)
- <em class="gray">1/2</em>
- > (logbit? 6 1) ; argument order, (logbit? int index), follows gmp, not CL
- <em class="gray">#t</em>
- </pre>
-
- <p>See <a href="#libgsl">cload and libgsl.scm</a> for easy access to GSL,
- and similarly libm.scm for the C math library.
- </p>
-
- <blockquote>
- <div class="indented">
-
- <p>The exponent itself is always in base 10; this follows gmp usage.
- Scheme normally uses "@" for its useless polar notation, but that
- means <code>(string->number "1e1" 16)</code> is ambiguous — is the "e" a digit or an exponent marker?
- In s7, "@" is an exponent marker.
- </p>
-
- <pre class="indented">
- > (string->number "1e9" 2) ; (expt 2 9)
- <em class="gray">512.0</em>
- > (string->number "1e1" 12) ; "e" is not a digit in base 12
- <em class="gray">#f</em>
- > (string->number "1e1" 16) ; (+ (* 1 16 16) (* 14 16) 1)
- <em class="gray">481</em>
- > (string->number "1.2e1" 3); (* 3 (+ 1 2/3))
- <em class="gray">5.0</em>
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>Should s7 predefine the numbers +inf.0, -inf.0, and nan.0? It doesn't currently, but you can
- get them via log or 1/0 (see <a href="#r7rs">below</a>).
- But what is <code>(/ 1.0 0.0)</code>? s7 gives a "division by zero" error here, and also in <code>(/ 1 0)</code>.
- Guile returns +inf.0 in the first case, which seems reasonable, but a "numerical overflow" error in the second.
- Slightly weirder is <code>(expt 0.0 0+i)</code>. Currently s7 returns 0.0, Guile returns +nan.0+nan.0i,
- Clisp and sbcl throw an error. Everybody agrees that <code>(expt 0 0)</code> is 1, and Guile thinks
- that <code>(expt 0.0 0.0)</code> is 1.0. But <code>(expt 0 0.0)</code> and <code>(expt 0.0 0)</code> return different
- results in Guile (1 and 1.0), both are 0.0 in s7, the first is an error in Clisp, but the second returns 1,
- and so on — what a mess! This mess was made a lot worse than it needs to be when the IEEE decreed that
- 0.0 equals -0.0, so we can't tell them apart, but that they produce different results in nearly every use!
- </p>
-
- <pre class="indented">
- scheme@(guile-user)> (= -0.0 0.0)
- <em class="gray">#t</em>
- scheme@(guile-user)> (negative? -0.0)
- <em class="gray">#f</em>
- scheme@(guile-user)> (= (/ 1.0 0.0) (/ 1.0 -0.0))
- <em class="gray">#f</em>
- scheme@(guile-user)> (< (/ 1.0 -0.0) -1e100 1e100 (/ 1.0 0.0))
- <em class="gray">#t</em>
- </pre>
-
- <p>
- How can they be equal? In s7, the sign
- of -0.0 is ignored, and they really are equal.
- One other oddity: two floats can satisfy eq? and yet not be eqv?:
- <code>(eq? nan.0 nan.0)</code> might be #t (it is unspecified), but <code>(eqv? nan.0 nan.0)</code> is #f.
- The same problem afflicts memq and assq.
- </p>
- </div>
-
-
- <div class="indented">
-
- <p>The <em class=def id="random">random</em> function takes a range and an optional state, and returns a number
- between zero and the range, of the same type as the range. It is perfectly reasonable
- to use a range of 0, in which case random returns 0.
- <em class=def id="randomstate">random-state</em> creates a new random state from a seed. If no state is passed,
- random uses some default state initialized from the current time. <em class=def id="randomstatep">random-state?</em> returns #t if passed a random state object.
- s7 ought to have a function named random? that always returns #t.
- </p>
-
- <pre class="indented">
- > (random 0)
- <em class="gray">0</em>
- > (random 1.0)
- <em class="gray">0.86331198514245</em>
- > (random 3/4)
- <em class="gray">654/1129</em>
- > (random 1+i)
- <em class="gray">0.86300308872748+0.83601002730848i</em>
- > (random -1.0)
- <em class="gray">-0.037691127513267</em>
- > (define r0 (random-state 1234))
- <em class="gray">r0</em>
- > (random 100 r0)
- <em class="gray">94</em>
- > (random 100 r0)
- <em class="gray">19</em>
- > (define r1 (random-state 1234))
- <em class="gray">r1</em>
- > (random 100 r1)
- <em class="gray">94</em>
- > (random 100 r1)
- <em class="gray">19</em>
- </pre>
-
- <p>copy the random-state to save a spot in a random number sequence, or save the random-state as a list via
- random-state->list, then to restart from that point, apply random-state to that list.
- </p>
- </div>
-
-
- <div class="indented">
-
- <p>I can't find the right tone for this section; this is the 400-th revision; I wish I were a better writer!
- </p>
-
- <p>In some Schemes,
- "rational" means "could possibly be
- expressed equally well as a ratio: floats are approximations". In s7 it's: "is actually expressed (at the scheme level) as a ratio (or an integer of course)";
- otherwise "rational?" is the same as "real?":
- </p>
-
- <pre class="indented">
- (not-s7)> (rational? (sqrt 2))
- <em class="gray">#t</em>
- </pre>
-
- <p>That 1.0 is represented at the IEEE-float level as a sort of
- ratio does not mean it has to be a scheme ratio; the two notions are independent.
- </p>
-
- <p>But that confusion is trivial compared to the completely nutty "inexact integer".
- As I understand it, "inexact" originally meant "floating point", and "exact" meant integer or ratio of integers.
- But words have a life of their own.
- 0.0 somehow became an "inexact" integer (although it can be represented exactly in floating
- point).
- +inf.0 must be an integer —
- its fractional part is explicitly zero! But +nan.0...
- And then there's:
- </p>
-
- <pre class="indented">
- (not-s7)> (integer? 9007199254740993.1)
- <em class="gray">#t</em>
- </pre>
-
- <p>
- When does this matter? I often need to index into a vector, but the index is a float (a "real" in Scheme-speak: its
- fractional part can be non-zero).
- In one Scheme:
- </p>
-
- <pre class="indented">
- (not-s7)> (vector-ref #(0) (floor 0.1))
- <em class="gray">ERROR: Wrong type (expecting exact integer): 0.0 </em>; [why? "it's probably a programmer mistake"!]
- </pre>
-
- <p>Not to worry, I'll use inexact->exact:
- </p>
-
- <pre class="indented">
- (not-s7)> (inexact->exact 0.1)
- <em class="gray">3602879701896397/36028797018963968 </em>; [why? "floats are ratios"!]
- </pre>
-
- <p>So I end up using the verbose <code>(floor (inexact->exact ...))</code> everywhere, and even
- then I have no guarantee that I'll get a legal vector index.
- I have never seen any use made of the exact/inexact distinction — just
- wild flailing to try get around it.
- I think the whole idea is confused and useless, and leads
- to verbose and buggy code.
- If we discard it,
- we can maintain backwards compatibility via:
- </p>
-
- <pre class="indented">
- (define exact? rational?)
- (define (inexact? x) (not (rational? x)))
- (define inexact->exact rationalize) ; or floor
- (define (exact->inexact x) (* x 1.0))
- </pre>
-
- <p>#i and #e are also useless because you can
- have any number after, for example, #b:
- </p>
-
- <pre class="indented">
- > #b1.1
- <em class="gray">1.5</em>
- > #b1e2
- <em class="gray">4.0</em>
- > #o17.5+i
- <em class="gray">15.625+1i</em>
- </pre>
-
- <p>Speaking of #b and friends, what should <code>(string->number "#xffff" 2)</code> return?
- </p>
- </div>
-
-
- <details>
- <summary class="indented">more examples</summary>
- <div class="indented">
-
- <pre>
- (define (log-n-of n . ints) ; return the bits on in exactly n of ints
- (let ((len (length ints)))
- (cond ((= len 0) (if (= n 0) -1 0))
- ((= n 0) (lognot (apply logior ints)))
- ((= n len) (apply logand ints))
- ((> n len) 0)
- (#t
- (do ((1s 0)
- (prev ints)
- (i 0 (+ i 1)))
- ((= i len) 1s)
- (let ((cur (ints i)))
- (if (= i 0)
- (set! 1s (logior 1s (logand cur (apply log-n-of (- n 1) (cdr ints)))))
- (let ((mid (cdr prev)))
- (set! (cdr prev) (if (= i (- len 1)) () (cdr mid)))
- (set! 1s (logior 1s (logand cur (apply log-n-of (- n 1) ints))))
- (set! (cdr prev) mid)
- (set! prev mid)))))))))
- </pre>
- </div>
- </details>
-
- </blockquote>
-
-
-
-
- <div class="header" id="define*"><h4>define*, lambda*</h4></div>
-
-
- <p><em class=def id="definestar">define*</em> and
- <em class=def id="lambdastar">lambda*</em>
- are extensions of define and lambda that make it easier
- to deal with optional, keyword, and rest arguments.
- The syntax is very simple: every argument to define* has a default value
- and is automatically available as a keyword argument. The default value
- is either #f if unspecified, or given in a list whose first member is
- the argument name.
- The last argument
- can be preceded by :rest or a dot to indicate that all other trailing arguments
- should be packaged as a list under that argument's name. A trailing or rest
- argument's default value is ().
- </p>
-
- <pre class="indented">
- (<em class=red>define*</em> (hi a (b 32) (c "hi")) (list a b c))
- </pre>
-
- <p>Here the argument "a" defaults to #f, "b" to 32, etc.
- When the function is called,
- the argument names are set from the values passed the function,
- then any unset arguments are bound to their default values, evaluated in left-to-right order.
- As the current argument list is scanned, any name that occurs as a keyword, :arg for example where the parameter name is arg,
- sets that argument's new value. Otherwise, as values occur, they
- are plugged into the actual argument list based on their position, counting a keyword/value pair as one argument.
- This is called an optional-key list in CLM. So, taking the function
- above as an example:
- </p>
-
- <pre class="indented">
- > (hi 1)
- <em class="gray">(1 32 "hi")</em>
- > (hi :b 2 :a 3)
- <em class="gray">(3 2 "hi")</em>
- > (hi 3 2 1)
- <em class="gray">(3 2 1)</em>
- </pre>
-
- <p>See s7test.scm for many examples. (s7's define* is very close to srfi-89's define*).
- </p>
-
-
- <blockquote>
-
- <div class="indented">
- <p>The combination of optional and keyword arguments is viewed with disfavor in the Lisp
- community, but the problem is in CL's implementation of the idea, not the idea itself.
- I've used the s7 style since around 1976, and have never found it confusing. The mistake
- in CL is to require the optional arguments if a keyword argument occurs, and to consider them as distinct from the
- keyword arguments. So everyone forgets and puts a keyword where CL expects a required-optional
- argument. CL then does something ridiculous, and the programmer stomps around shouting about keywords, but the fault lies with CL.
- If s7's way is considered too loose, one way to tighten it might be to insist that once a keyword
- is used, only keyword argument pairs can follow.
- </p>
- </div>
-
-
- <div class="indented">
- <p>A natural companion of lambda* is named let*. In named let, the implicit function's
- arguments have initial values, but thereafter, each call requires the full set of arguments.
- Why not treat the initial values as default values?
- </p>
-
- <pre class="indented">
- > (let* func ((i 1) (j 2))
- (+ i j (if (> i 0) (func (- i 1)) 0)))
- <em class="gray">5</em>
- > (letrec ((func (lambda* ((i 1) (j 2))
- (+ i j (if (> i 0) (func (- i 1)) 0)))))
- (func))
- <em class="gray">5</em>
- </pre>
-
- <p>This is consistent with the lambda* arguments because their defaults are
- already set in left-to-right order, and as each parameter is set to its default value,
- the binding is added to the default value expression environment (just as if it were a let*).
- The let* name itself (the implicit function) is not defined until after the bindings
- have been evaluated (as in named let).
- </p>
-
- <p>In CL, keyword default values are handled in the same way:
- </p>
-
- <pre class="indented">
- > (defun foo (&key (a 0) (b (+ a 4)) (c (+ a 7))) (list a b c))
- <em class="gray">FOO </em>
- > (foo :b 2 :a 60)
- <em class="gray">(60 2 67) </em>
- </pre>
-
- <p>In s7, we'd use:
- </p>
-
- <pre class="indented">
- (define* (foo (a 0) (b (+ a 4)) (c (+ a 7))) (list a b c))
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>To try to catch what I believe are usually mistakes, I added two
- error checks. One is triggered if you set the same parameter twice
- in the same call, and the other if an unknown keyword is encountered
- in the key position. These problems arise in a case such as
- </p>
-
- <pre class="indented">
- (define* (f (a 1) (b 2)) (list a b))
- </pre>
-
- <p>You could do any of the following by accident:
- </p>
-
- <pre class="indented">
- (f 1 :a 2) ; what is a?
- (f :b 1 2) ; what is b?
- (f :c 3) ; did you really want a to be :c and b to be 3?
- </pre>
-
- <p>In the last case, to pass a keyword deliberately, either include the
- argument keyword: <code>(f :a :c)</code>, or make the default value a keyword:
- <code>(define* (f (a :c) ...))</code>.
- To turn off this error check, add :allow-other-keys at the end of the parameter list.
- </p>
-
- </div>
-
-
- <details>
- <summary class="indented">rest arg nits</summary>
- <div class="indented">
-
- <p>s7's lambda* arglist handling is not the same as CL's lambda-list. First,
- you can have more than one :rest parameter:
- </p>
-
- <pre class="indented">
- > ((lambda* (:rest a :rest b) (map + a b)) 1 2 3 4 5)
- <em class="gray">'(3 5 7 9)</em>
- </pre>
-
- <p>and second, the rest parameter, if any, takes up an argument slot just like any other
- argument:
- </p>
-
- <pre class="indented">
- > ((lambda* ((b 3) :rest x (c 1)) (list b c x)) 32)
- <em class="gray">(32 1 ())</em>
- > ((lambda* ((b 3) :rest x (c 1)) (list b c x)) 1 2 3 4 5)
- <em class="gray">(1 3 (2 3 4 5))</em>
- </pre>
-
- <p>CL would agree with the first case if we used &key for 'c', but would give an error in the second.
- Of course, the major difference is that s7 keyword arguments don't insist that the key be present.
- The :rest argument is needed in cases like these because we can't use an expression
- such as:
- </p>
-
- <pre class="indented">
- > ((lambda* ((a 3) . b c) (list a b c)) 1 2 3 4 5)
- <em class="red">error: </em><em class="gray">stray dot?</em>
- > ((lambda* (a . (b 1)) b) 1 2) ; the reader turns the arglist into (a b 1)
- <em class="red">error: </em><em class="gray">lambda* parameter '1 is a constant</em>
- </pre>
-
- <p>Yet another nit: the :rest argument is not considered a keyword argument, so
- </p>
-
- <pre class="indented">
- > (define* (f :rest a) a)
- <em class="gray">f</em>
- > (f :a 1)
- <em class="gray">(:a 1)</em>
- </pre>
- </div>
- </details>
-
- </blockquote>
-
-
-
- <div class="header" id="macros"><h4>macros</h4></div>
-
-
- <p><em class=def id="definemacro">define-macro</em>,
- <em class=def id="definemacrostar">define-macro*</em>,
- <em class=def id="macroexpand">macroexpand</em>,
- <em class=def id="gensym">gensym</em>,
- <em class=def id="gensym?">gensym?</em>, and
- <em class=def id="macrop">macro?</em>
- implement the standard old-time macros.
- </p>
-
- <pre class="indented">
- > (define-macro (and-let* vars . body)
- `(let () (and ,@(map (lambda (v) `(define ,@v)) vars) (begin ,@body))))
-
- > (define-macro (<em class=def id="trace">trace</em> f)
- `(define ,f
- (apply lambda 'args
- `((format () "(~A ~{~A~^ ~}) -> " ',',f args)
- (let ((val (apply ,,f args)))
- (format () "~A~%" val)
- val)))))
- <em class="gray">trace</em>
- > (trace abs)
- <em class="gray">abs</em>
- > (abs -1.5)
- <em class="gray">(abs -1.5) -> 1.5</em>
- <em class="gray">1.5</em>
- </pre>
-
- <p>macroexpand can help debug a macro. I always forget that it
- wants an expression:
- </p>
-
- <pre class="indented">
- > (define-macro (add-1 arg) `(+ 1 ,arg))
- <em class="gray">add-1</em>
- > (macroexpand (add-1 32))
- <em class="gray">(+ 1 32)</em>
- </pre>
-
- <p>gensym returns a symbol that is guaranteed to be unused. It takes an optional string argument
- giving the new symbol name's prefix. gensym? returns #t if its argument is a symbol created by gensym.
- </p>
-
- <pre class="indented">
- (define-macro (pop! sym)
- (let ((v (<em class=red>gensym</em>)))
- `(let ((,v (car ,sym)))
- (set! ,sym (cdr ,sym))
- ,v)))
- </pre>
-
- <p>As in define*, the starred forms give optional and keyword arguments:
- </p>
-
- <pre class="indented">
- > (define-macro* (add-2 a (b 2)) `(+ ,a ,b))
- <em class="gray">add-2</em>
- > (add-2 1 3)
- <em class="gray">4</em>
- > (add-2 1)
- <em class="gray">3</em>
- > (add-2 :b 3 :a 1)
- <em class="gray">4</em>
- </pre>
-
- <p>A bacro is a macro that expands its body and evaluates
- the result in the calling environment.
- </p>
-
- <pre class="indented">
- (define setf
- (let ((args (gensym))
- (name (gensym)))
- (apply <em class=red>define-bacro</em> `((,name . ,args)
- (unless (null? ,args)
- (apply set! (car ,args) (cadr ,args) ())
- (apply setf (cddr ,args)))))))
- </pre>
-
-
- <p>
- The setf argument is a gensym (created when setf is defined) so that its name does not shadow any existing
- variable. Bacros expand in the calling environment, and a normal argument name
- might shadow something in that environment while the bacro is being expanded.
- Similarly, if you introduce bindings in the bacro expansion code, you need to
- keep track of which environment you want things to happen in. Use with-let
- and gensym liberally.
- stuff.scm has bacro-shaker which can find inadvertent name collisions,
- but it is flighty and easily confused.
- See s7test.scm for many examples of macros including such perennial favorites as
- loop, dotimes, do*, enum, pushnew, and defstruct.
- The calling environment itself is (outlet (curlet)) from within a bacro, so
- </p>
-
- <pre class="indented">
- (define-bacro (holler)
- `(format *stderr* "(~S~{ ~S ~S~^~})~%"
- (let ((f __func__))
- (if (pair? f) (car f) f))
- (map (lambda (slot)
- (values (symbol->keyword (car slot)) (cdr slot)))
- (reverse (map values ,(outlet (curlet)))))))
-
- (define (f1 a b)
- (holler)
- (+ a b))
-
- (f1 2 3) ; prints out "(f1 :a 2 :b 3)" and returns 5
- </pre>
-
-
- <blockquote>
-
- <div class="indented">
-
- <p>
- Since a bacro (normally) sheds its define-time environment:
- </p>
-
- <pre class="indented">
- (define call-bac
- (let ((<em class=red>x</em> 2))
- (define-bacro (m a) `(+ ,a ,<em class=red>x</em>))))
-
- > (call-bac 1)
- <em class="red">error: </em><em class="gray">x: unbound variable</em>
- </pre>
- <p>
- A macro here returns 3. But don't be hasty! The bacro can get its define-time environment (its closure)
- via funclet, so in fact, define-macro is a special case of define-bacro! We can define
- macros that work in all four ways: the expansion can happen in either the definition or calling environment,
- as can the evaluation of that expansion. In a bacro, both happen in the calling environment
- if we take no other action, and in a normal macro (define-macro), the expansion happens in the definition
- environment, and the evaluation in the calling environment.
- Here's a brief example of all four:
- </p>
-
- <pre class="indented">
- (let ((x 1) (y 2))
- (define-bacro (bac1 a)
- `(+ ,x y ,a)) ; expand and eval in calling env
- (let ((x 32) (y 64))
- (bac1 3))) ; (with-let (inlet 'x 32 'y 64) (+ 32 y 3))
- -> 99 ; with-let and inlet refer to <a href="#environments">environments</a>
-
- (let ((x 1) (y 2)) ; this is like define-macro
- (define-bacro (bac2 a)
- (with-let (sublet (funclet bac2) :a a)
- `(+ ,x y ,a))) ; expand in definition env, eval in calling env
- (let ((x 32) (y 64))
- (bac2 3))) ; (with-let (inlet 'x 32 'y 64) (+ 1 y 3))
- -> 68
-
- (let ((x 1) (y 2))
- (define-bacro (bac3 a)
- (let ((e (with-let (sublet (funclet bac3) :a a)
- `(+ ,x y ,a))))
- `(with-let ,(sublet (funclet bac3) :a a)
- ,e))) ; expand and eval in definition env
- (let ((x 32) (y 64))
- (bac3 3))) ; (with-let (inlet 'x 1 'y 2) (+ 1 y 3))
- -> 6
-
- (let ((x 1) (y 2))
- (define-bacro (bac4 a)
- (let ((e `(+ ,x y ,a)))
- `(with-let ,(sublet (funclet bac4) :a a)
- ,e))) ; expand in calling env, eval in definition env
- (let ((x 32) (y 64))
- (bac4 3))) ; (with-let (inlet 'x 1 'y 2) (+ 32 y 3))
- -> 37
- </pre>
- </div>
-
- <div class="indented">
-
- <p>As the setf example shows, a macro is a first-class citizen of s7. You can
- pass it as a function argument, apply it to a list, return it from a function,
- call it recursively,
- and assign it to a variable. You can even set its procedure-setter!
- </p>
-
- <pre class="indented">
- > (define-macro (hi a) `(+ ,a 1))
- <em class="gray">hi</em>
- > (apply hi '(4))
- <em class="gray">5</em>
- > (define (fmac mac) (apply mac '(4)))
- <em class="gray">fmac</em>
- > (fmac hi)
- <em class="gray">5</em>
- > (define (fmac mac) (mac 4))
- <em class="gray">fmac</em>
- > (fmac hi)
- <em class="gray">5</em>
- > (define (make-mac)
- (define-macro (hi a) `(+ ,a 1)))
- <em class="gray">make-mac</em>
- > (let ((x (make-mac)))
- (x 2))
- <em class="gray">3</em>
- > (define-macro (ref v i) `(vector-ref ,v ,i))
- <em class="gray">ref</em>
- > (define-macro (set v i x) `(vector-set! ,v ,i ,x))
- <em class="gray">set</em>
- > (set! (procedure-setter ref) set)
- <em class="gray">set</em>
- > (let ((v (vector 1 2 3))) (set! (ref v 0) 32) v)
- <em class="gray">#(32 2 3)</em>
- </pre>
-
- <p>To expand all the macros in a piece of code:
- </p>
- <pre class="indented">
- (define-macro (fully-macroexpand form)
- (list 'quote
- (let expand ((form form))
- (cond ((not (pair? form)) form)
- ((and (symbol? (car form))
- (macro? (symbol->value (car form))))
- (expand (apply macroexpand (list form))))
- ((and (eq? (car form) 'set!) ; look for (set! (mac ...) ...) and use mac's procedure-setter
- (pair? (cdr form))
- (pair? (cadr form))
- (macro? (symbol->value (caadr form))))
- (expand
- (apply (eval
- (procedure-source (procedure-setter (symbol->value (caadr form)))))
- (append (cdadr form) (cddr form)))))
- (else (cons (expand (car form)) (expand (cdr form))))))))
- </pre>
- <p>This does not always handle bacros correctly because their expansion can depend on the run-time
- state.
- </p>
- </div>
-
-
- <details>
- <summary class="indented">backquote details</summary>
- <div class="indented">
-
- <p>Backquote (quasiquote) in s7 is almost trivial. Constants are unchanged, symbols are quoted,
- ",arg" becomes "arg", and ",@arg" becomes "(apply values arg)" — hooray for real multiple values!
- It's almost as easy to write the actual macro body as the backquoted version of it.
- </p>
-
- <pre class="indented">
- > (define-macro (hi a) `(+ 1 ,a))
- <em class="gray">hi</em>
- > (procedure-source hi)
- <em class="gray">(lambda (a) ({list} '+ 1 a))</em>
-
- > (define-macro (hi a) `(+ 1 ,@a))
- <em class="gray">hi</em>
- > (procedure-source hi)
- <em class="gray">(lambda (a) ({list} '+ 1 ({apply_values} a)))</em>
- </pre>
-
- <p>{list} is a special version of list to avoid name collisions
- and handle a few tricky details (similarly for {apply_values}). There is no unquote-splicing
- macro in s7; ",@(...)" becomes "(unquote ({apply_values} ...))" at read-time. There shouldn't be any unquote
- either. In Scheme the reader turns ,x into (unquote x), so:
- </p>
-
- <pre>
- > (let (,'a) unquote)
- <em class="gray">a</em>
- > (let (, (lambda (x) (+ x 1))) ,,,,'3)
- <em class="gray">7</em>
- </pre>
- <p>comma becomes a sort of symbol macro! I think I'll remove unquote; ,x and ,@x will still work
- as expected, but there will not be any "unquote" or "unquote-splicing" in the resultant source code. Just to make life difficult:
- </p>
- <pre>
- > (let (' 1) quote)
- <em class="gray">1</em>
- </pre>
- <p>but that translation is so ingrained in lisp
- that I'm reluctant to change it. The two unquote names, on the
- other hand, seem unnecessary.
- </p>
- </div>
- </details>
-
-
- <p>s7 macros are not hygienic. For example,
- </p>
-
- <pre class="indented">
- > (define-macro (mac b)
- `(let ((a 12))
- (+ a ,b)))
- <em class="gray">mac</em>
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">144</em>
- </pre>
-
- <p>This returns 144 because '+' has turned into '*', and 'a' is the internal 'a',
- not the argument 'a'. We get <code>(* 12 12)</code> where we might have expected
- <code>(+ 12 1)</code>.
- Starting with the '+' problem,
- as long as the redefinition of '+' is local (that is, it happens after the macro definition), we can unquote the +:
- </p>
-
- <pre class="indented">
- > (define-macro (mac b)
- `(let ((a 12))
- (,+ a ,b))) ; ,+ picks up the definition-time +
- <em class="gray">mac</em>
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">24 ; (+ a a) where a is 12</em>
- </pre>
-
- <p>But the unquote trick won't work if we have previously loaded some file that redefined '+'
- at the top-level (so at macro definition time, + is *, but we want the built-in +).
- Although this example is silly, the problem is real in Scheme
- because Scheme has no reserved words and only one name space.
- </p>
-
- <pre class="indented">
- > (define + *)
- <em class="gray">+</em>
- > (define (add a b) (+ a b))
- <em class="gray">add</em>
- > (add 2 3)
- <em class="gray">6</em>
- > (define (divide a b) (/ a b))
- <em class="gray">divide</em>
- > (divide 2 3)
- <em class="gray">2/3</em>
- > (set! / -) ; a bad idea — this turns off s7's optimizer
- <em class="gray">-</em>
- > (divide 2 3)
- <em class="gray">-1</em>
- </pre>
-
- <p>Obviously macros are not the problem here. Since
- we might be loading
- code written by others, it's sometimes hard to tell what names
- that code depends on or redefines.
- We need a way to get the pristine (start-up, built-in) value of '+'.
- One long-winded way in s7 uses <a href="#unlet">unlet</a>:
- </p>
-
- <pre class="indented">
- > (define + *)
- <em class="gray">+</em>
- > (define (add a b) (with-let (unlet) (+ a b)))
- <em class="gray">add</em>
- > (add 2 3)
- <em class="gray">5</em>
- </pre>
-
- <p>But this is hard to read, and it's not inconceivable that we might want all three
- values of a symbol, the start-up value, the definition-time value, and the
- current value. The latter can be accessed with the bare symbol, the definition-time
- value with unquote (','), and the start-up value with either unlet
- or #_<name>. That is, #_+ is a reader macro for <code>(with-let (unlet) +)</code>.
- </p>
-
- <pre class="indented">
- > (define-macro (mac b)
- `(<em class=red>#_let</em> ((a 12))
- (<em class=red>#_+</em> a ,b))) ; #_+ and #_let are start-up values
- <em class="gray">mac</em>
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">24 ; (+ a a) where a is 12 and + is the start-up +</em>
-
- ;;; make + generic (there's a similar C-based example below)
- > (define (+ . args)
- (if (null? args) 0
- (apply (if (number? (car args)) <em class=red>#_+ #_string-append</em>) args)))
- <em class="gray">+</em>
- > (+ 1 2)
- <em class="gray">3</em>
- > (+ "hi" "ho")
- <em class="gray">"hiho"</em>
- </pre>
-
- <p>#_<name> could be implemented via *#readers*:
- </p>
-
- <pre class="indented">
- (set! *#readers*
- (cons (cons #\_ (lambda (str)
- (with-let (unlet)
- (string->symbol (substring str 1)))))
- *#readers*))
- </pre>
-
- <p>
- So, now we have only the variable capture problem ('a' has been captured in the preceding examples).
- This is the only thing that the gigantic "hygienic macro" systems actually deal with:
- a microscopic problem that you'd think, from the hype, was up there with malaria and the
- national debt. gensym is the standard approach:
- </p>
-
- <pre class="indented">
- > (define-macro (mac b)
- (let ((var (<em class=red>gensym</em>)))
- `(#_let ((,var 12))
- (#_+ ,var ,b))))
- <em class="gray">mac</em>
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">13</em>
-
- ;; or use lambda:
- > (define-macro (mac b)
- `((lambda (b) (let ((a 12)) (#_+ a b))) ,b))
- <em class="gray">mac</em>
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">13</em>
- </pre>
-
- <p>But in s7, the simplest approach uses environments. You have complete
- control over the environment at any point:
- </p>
-
- <pre>
- (define-macro (mac b)
- `(with-let (inlet 'b ,b)
- (let ((a 12))
- (+ a b))))
-
- > (let ((a 1) (+ *)) (mac a))
- <em class="gray">13</em>
-
- (define-macro (mac1 . b) ; originally `(let ((a 12)) (+ a ,@b ,@b))
- `(with-let (inlet 'e (curlet)) ; this 'e will not collide with the calling env
- (let ((a 12)) ; nor will 'a (so no gensyms are needed etc)
- (+ a (with-let e ,@b) (with-let e ,@b)))))
-
- > (let ((a 1) (e 2)) (mac1 (display a) (+ a e)))
- <em class="gray">18</em> ; (and it displays "11")
-
- (define-macro (mac2 x) ; this will use mac2's definition environment for its body
- `(with-let (sublet (funclet mac2) :x ,x)
- (let ((a 12))
- (+ a b x)))) ; a is always 12, b is whatever b happens to be in mac2's env
-
- > (define b 10) ; this is mac2's b
- <em class="gray">10</em>
- > (let ((+ *) (a 1) (b 15)) (mac2 (+ a b)))
- <em class="gray">37</em> ; mac2 uses its own a (12), b (10), and + (+)
- ; but (+ a b) is 15 because at that point + is *: (* 1 15)
- </pre>
-
- <p>Hygienic macros are trivial! So s7 does not have syntax-rules because it is not needed.
- s7's lint.scm will warn you about any such problematic macro expansion, so I'd
- say just write macros as simply as possible, then let lint tell you
- that it's time to do the with-let shuffle. When that happens, wrap the macro body in
- a with-let that captures the current environment, and at each use of a macro argument
- wrap it in a with-let that re-establishes that environment.
- </p>
-
- <div class="indented">
- <pre>
- (define-macro (swap a b) ; assume a and b are symbols
- `(with-let (inlet 'e (curlet) 'tmp ,a)
- (set! (e ',a) (e ',b))
- (set! (e ',b) tmp)))
-
- > (let ((b 1) (tmp 2)) (swap b tmp) (list b tmp))
- <em class="gray">(2 1)</em>
-
- (define-macro (swap a b) ; here a and b can be any settable expressions
- `(set! ,b (with-let (inlet 'e (curlet) 'tmp ,a)
- (with-let e (set! ,a ,b))
- tmp)))
-
- > (let ((v (vector 1 2))) (swap (v 0) (v 1)) v)
- <em class="gray">#(2 1)</em>
- > (let ((tmp (cons 1 2))) (swap (car tmp) (cdr tmp)) tmp)
- <em class="gray">(2 . 1)</em>
-
- (set! (procedure-setter swap) (define-macro (set-swap a b c) `(set! ,b ,c)))
-
- > (let ((a 1) (b 2) (c 3) (d 4)) (swap a (swap b (swap c d))) (list a b c d))
- <em class="gray">(2 3 4 1)</em>
-
- ;;; but this is simpler:
- (define-macro (rotate! . args)
- `(set! ,(args (- (length args) 1))
- (with-let (inlet 'e (curlet) 'tmp ,(car args))
- (with-let e
- ,@(map (lambda (a b) `(set! ,a ,b)) args (cdr args)))
- tmp)))
-
- > (let ((a 1) (b 2) (c 3)) (rotate! a b c) (list a b c))
- <em class="gray">(2 3 1)</em>
- </pre>
- </div>
-
- <p>
- If you want the macro's expanded result
- to be evaluated in its definition environment:
- </p>
- <pre>
- (let ((a 3))
- (define-macro (mac b)
- `(with-let (inlet 'b ,b (funclet mac))
- (+ a b))) ; definition-time "a", call-time "b"
- (define-macro (mac-1 b)
- `(+ a ,b)) ; call-time "a" and "b"
- (let ((a 32))
- (list (mac 1)
- (mac-1 1))))
- </pre>
-
-
- <!--
- (define (tree-quote tree args)
- (if (pair? tree)
- (if (eq? (car tree) 'quote)
- tree
- (cons (tree-quote (car tree) args)
- (tree-quote (cdr tree) args)))
- (if (memq tree args)
- (list 'quote tree)
- tree)))
-
- (define-macro (define-hacro name-and-args . body)
- (let ((name (car name-and-args))
- (args (cdr name-and-args)))
- `(define-macro ,name-and-args
- (list 'with-let
- (list 'inlet ,@(map (lambda (arg)
- (values (symbol->keyword arg) arg))
- args))
- ,@(tree-quote body args)))))
-
- ; (define-hacro (mac a b) `(+ ,a ,b))
- ; (macroexpand (mac 2 3))
- ; (with-let (inlet :a 2 :b 3) (+ a b))
- ; (procedure-source mac)
- ; (lambda (a b) (list 'with-let (list 'inlet :a a :b b) ({list} '+ 'a 'b)))
- -->
-
-
-
- <div class="indented">
-
- <p>Here is Peter Seibel's wonderful once-only macro:
- </p>
-
- <pre class="indented">
- (define-macro (once-only names . body)
- (let ((gensyms (map (lambda (n) (gensym)) names)))
- `(let (,@(map (lambda (g) (list g '(gensym))) gensyms))
- `(let (,,@(map (lambda (g n) (list list g n)) gensyms names))
- ,(let (,@(map list names gensyms))
- ,@body)))))
- </pre>
-
- <!-- this was:
- (define-macro (once-only names . body)
- (let ((gensyms (map (lambda (n) (gensym)) names)))
- `(let (,@(map (lambda (g) `(,g (gensym))) gensyms))
- `(let (,,@(map (lambda (g n) ``(,,g ,,n)) gensyms names))
- ,(let (,@(map (lambda (n g) `(,n ,g)) names gensyms))
- ,@body)))))
- -->
-
- <p>From the land of sparkling bacros:
- </p>
-
- <pre class="indented">
- (define once-only
- (let ((names (gensym))
- (body (gensym)))
- (apply define-bacro `((,(gensym) ,names . ,body)
- `(let (,@(map (lambda (name) `(,name ,(eval name))) ,names))
- ,@,body)))))
- </pre>
- <p>Sadly, with-let is simpler.
- </p>
- </div>
-
- </blockquote>
-
- <!--
- when is (define-macro (f a) `(+ ,a 1)) not the same as (define (f a) (+ a 1))?
- (f (values 2 3))
- (f most-positive-fixnum) but only because the optimizer messes this up
- -->
-
-
-
- <div class="header" id="pws"><h4>procedure-setter</h4></div>
-
- <pre class="indented">
- (<em class=def>procedure-setter</em> proc)
- (<em class=def id="dilambda">dilambda</em> proc setter)
- </pre>
-
- <p>Each function (or macro!) can have an associated setter, much like defsetf in CL. As a convenience, there's also
- a way to associate the two functions under one name: dilambda.
- The setter is called when the procedure is the target of set! Its last argument is the
- value passed to set!:
- </p>
-
- <pre class="indented">
- > (procedure-setter cadr)
- <em class="gray">#f</em>
- > (set! (procedure-setter cadr)
- (lambda (lst val)
- (set! (car (cdr lst)) val)))
- <em class="gray">#<lambda (lst val)></em>
- > (procedure-source (procedure-setter cadr))
- <em class="gray">(lambda (lst val) (set! (car (cdr lst)) val))</em>
- > (let ((lst (list 1 2 3)))
- (set! (cadr lst) 4)
- lst)
- <em class="gray">(1 4 3)</em>
- </pre>
-
- <p>In some cases, the setter needs to be a macro:
- </p>
- <pre class="indented">
- > (set! (procedure-setter logbit?)
- (define-macro (m var index on) ; here we want to set "var", so we need a macro
- `(if ,on
- (set! ,var (logior ,var (ash 1 ,index)))
- (set! ,var (logand ,var (lognot (ash 1 ,index)))))))
- <em class="gray">m</em>
- > (define (mingle a b)
- (let ((r 0))
- (do ((i 0 (+ i 1)))
- ((= i 31) r)
- (set! (logbit? r (* 2 i)) (logbit? a i))
- (set! (logbit? r (+ (* 2 i) 1)) (logbit? b i)))))
- <em class="gray">mingle</em>
- > (mingle 6 3) ; the INTERCAL mingle operator?
- <em class="gray">30</em>
- </pre>
-
- <blockquote>
-
-
- <div class="indented">
-
- <p>Here is a pretty example of dilambda:
- </p>
-
- <pre class="indented">
- (define-macro (c?r path)
- ;; "path" is a list and "X" marks the spot in it that we are trying to access
- ;; (a (b ((c X)))) — anything after the X is ignored, other symbols are just placeholders
- ;; c?r returns a dilambda that gets/sets X
-
- (define (X-marks-the-spot accessor tree)
- (if (pair? tree)
- (or (X-marks-the-spot (cons 'car accessor) (car tree))
- (X-marks-the-spot (cons 'cdr accessor) (cdr tree)))
- (and (eq? tree 'X) accessor)))
-
- (let ((body 'lst))
- (for-each
- (lambda (f)
- (set! body (list f body)))
- (reverse (X-marks-the-spot () path)))
-
- `(<em class=red>dilambda</em>
- (lambda (lst)
- ,body)
- (lambda (lst val)
- (set! ,body val)))))
-
- > ((c?r (a b (X))) '(1 2 (3 4) 5))
- <em class="gray">3</em>
- > (let ((lst (list 1 2 (list 3 4) 5)))
- (set! ((c?r (a b (X))) lst) 32)
- lst)
- <em class="gray">(1 2 (32 4) 5)</em>
- > (procedure-source (c?r (a b (X))))
- <em class="gray">(lambda (lst) (car (car (cdr (cdr lst)))))</em>
- > ((c?r (a b . X)) '(1 2 (3 4) 5))
- <em class="gray">((3 4) 5)</em>
- > (let ((lst (list 1 2 (list 3 4) 5)))
- (set! ((c?r (a b . X)) lst) '(32))
- lst)
- <em class="gray">(1 2 32)</em>
- > (procedure-source (c?r (a b . X)))
- <em class="gray">(lambda (lst) (cdr (cdr lst)))</em>
- > ((c?r (((((a (b (c (d (e X)))))))))) '(((((1 (2 (3 (4 (5 6))))))))))
- <em class="gray">6</em>
- > (let ((lst '(((((1 (2 (3 (4 (5 6)))))))))))
- (set! ((c?r (((((a (b (c (d (e X)))))))))) lst) 32)
- lst)
- <em class="gray">(((((1 (2 (3 (4 (5 32)))))))))</em>
- > (procedure-source (c?r (((((a (b (c (d (e X)))))))))))
- <em class="gray">(lambda (lst) (car (cdr (car (cdr (car (cdr (car (cdr (car (cdr (car (car (car (car lst)))))))))))))))</em>
- </pre>
- </div>
-
-
-
- <div class="indented">
- <p>Speaking of INTERCAL, COME-FROM:
- </p>
-
- <pre class="indented">
- (define-macro (define-with-goto-and-come-from name-and-args . body)
- (let ((labels ())
- (gotos ())
- (come-froms ()))
-
- (let collect-jumps ((tree body))
- (when (pair? tree)
- (when (pair? (car tree))
- (case (caar tree)
- ((label) (set! labels (cons tree labels)))
- ((goto) (set! gotos (cons tree gotos)))
- ((come-from) (set! come-froms (cons tree come-froms)))
- (else (collect-jumps (car tree)))))
- (collect-jumps (cdr tree))))
-
- (for-each
- (lambda (goto)
- (let* ((name (cadr (cadar goto)))
- (label (member name labels (lambda (a b) (eq? a (cadr (cadar b)))))))
- (if label
- (set-cdr! goto (car label))
- (error 'bad-goto "can't find label: ~S" name))))
- gotos)
-
- (for-each
- (lambda (from)
- (let* ((name (cadr (cadar from)))
- (label (member name labels (lambda (a b) (eq? a (cadr (cadar b)))))))
- (if label
- (set-cdr! (car label) from)
- (error 'bad-come-from "can't find label: ~S" name))))
- come-froms)
-
- `(define ,name-and-args
- (let ((label (lambda (name) #f))
- (goto (lambda (name) #f))
- (come-from (lambda (name) #f)))
- ,@body))))
- </pre>
- </div>
- </blockquote>
-
-
-
-
-
- <div class="header" id="generalizedset"><h4>applicable objects, generalized set!, generic functions</h4></div>
-
-
- <p>A procedure with a setter can be viewed as one generalization of set!. Another
- treats objects as having predefined get and set functions. In s7
- lists, strings, vectors, hash-tables, environments, and any cooperating C or Scheme-defined objects
- are both applicable and settable. newLisp calls this implicit indexing, Kawa has it, Gauche implements it
- via object-apply, Guile via procedure-with-setter; CL's funcallable instance might be the same idea.
- </p>
-
- <p>
- In <code>(vector-ref #(1 2) 0)</code>, for example, vector-ref is just a type
- declaration. But in Scheme, type declarations are unnecessary, so we get exactly
- the same result from <code>(#(1 2) 0)</code>. Similarly, <code>(lst 1)</code> is the
- same as <code>(list-ref lst 1)</code>, and <code>(set! (lst 1) 2)</code> is the same
- as <code>(list-set! lst 1 2)</code>.
- I like this syntax: the less noise, the better!
- </p>
-
- <blockquote>
-
- <div class="indented">
-
- <p>Well, maybe applicable strings look weird: <code>("hi" 1)</code> is #\i, but worse,
- so is <code>(cond (1 => "hi"))</code>! Even though a string, list, or vector is "applicable", it is
- not currently considered to be a procedure, so <code>(procedure? "hi")</code> is #f. map and for-each, however,
- accept anything that apply can handle, so
- <code>(map #(0 1) '(1 0))</code> is '(1 0). (On the first call to map in this case, you get the result of
- <code>(#(0 1) 1)</code> and so on).
- string->list, vector->list, and let->list are <code>(map values object)</code>.
- Their inverses are (and always have been) equally trivial.
- </p>
-
-
- <p>The applicable object syntax makes it easy to write generic functions.
- For example, s7test.scm has implementations of Common Lisp's sequence functions.
- length, copy, reverse, fill!, iterate, map and for-each are generic in this sense (map always returns a list).
- </p>
-
- <pre class="indented">
- > (map (lambda (a b) (- a b)) (list 1 2) (vector 3 4))
- <em class="gray">(5 -3 9)</em>
- > (length "hi")
- <em class="gray">2</em>
- </pre>
-
- <p>
- Here's a generic FFT:
- </p>
-
- <pre class="indented">
- (define* (cfft! data n (dir 1)) ; (complex data)
- (if (not n) (set! n (length data)))
- (do ((i 0 (+ i 1))
- (j 0))
- ((= i n))
- (if (> j i)
- (let ((temp (data j)))
- (set! (data j) (data i))
- (set! (data i) temp)))
- (do ((m (/ n 2)))
- ((or (< m 2)
- (< j m))
- (set! j (+ j m)))
- (set! j (- j m))
- (set! m (/ m 2))))
- (do ((ipow (floor (log n 2)))
- (prev 1)
- (lg 0 (+ lg 1))
- (mmax 2 (* mmax 2))
- (pow (/ n 2) (/ pow 2))
- (theta (complex 0.0 (* pi dir)) (* theta 0.5)))
- ((= lg ipow))
- (let ((wpc (exp theta))
- (wc 1.0))
- (do ((ii 0 (+ ii 1)))
- ((= ii prev))
- (do ((jj 0 (+ jj 1))
- (i ii (+ i mmax))
- (j (+ ii prev) (+ j mmax)))
- ((>= jj pow))
- (let ((tc (* wc (data j))))
- (set! (data j) (- (data i) tc))
- (set! (data i) (+ (data i) tc))))
- (set! wc (* wc wpc)))
- (set! prev mmax)))
- data)
-
- > (cfft! (list 0.0 1+i 0.0 0.0))
- <em class="gray">(1+1i -1+1i -1-1i 1-1i)</em>
- > (cfft! (vector 0.0 1+i 0.0 0.0))
- <em class="gray">#(1+1i -1+1i -1-1i 1-1i)</em>
- </pre>
-
- <p>And a generic function that copies one sequence's elements into another sequence:
- </p>
- <pre class="indented">
- (define (copy-into source dest) ; this is equivalent to (copy source dest)
- (do ((i 0 (+ i 1)))
- ((= i (min (length source) (length dest)))
- dest)
- (set! (dest i) (source i))))
- </pre>
-
- <p>but that is already built-in as the two-argument version of the copy function.
- </p>
- </div>
-
-
- <div class="indented">
-
- <p>There is one place where list-set! and friends are not the same as set!: the former
- evaluate their first argument, but set! does not (with a quibble; see below):
- </p>
-
- <pre class="indented">
- > (let ((str "hi")) (string-set! (let () str) 1 #\a) str)
- <em class="gray">"ha"</em>
- > (let ((str "hi")) (set! (let () str) 1 #\a) str)
- <em class="gray">;((let () str) 1 #\a): too many arguments to set!</em>
- > (let ((str "hi")) (set! ((let () str) 1) #\a) str)
- <em class="gray">"ha"</em>
- > (let ((str "hi")) (set! (str 1) #\a) str)
- <em class="gray">"ha"</em>
- </pre>
-
- <p>set! looks at its first argument to decide what to set.
- If it's a symbol, no problem. If it's a list, set! looks at its car to see if it is
- some object that has a setter. If the car is itself a list, set! evaluates the internal
- expression, and tries again. So the second case above is the only one that won't work.
- And of course:
- </p>
-
- <pre class="indented">
- > (let ((x (list 1 2)))
- (set! ((((lambda () (list x))) 0) 0) 3)
- x)
- <em class="gray">(3 2)</em>
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>By my count, around 20 of the Scheme built-in functions are already generic in the sense
- that they accept arguments of many types (leaving aside the numeric and type checking functions, take for example equal?, display,
- member, assoc, apply, eval, quasiquote, and values). s7 extends that list with map, for-each, reverse,
- and length, and adds a few others such as copy, fill!, sort!, object->string, object->let, and append.
- newLisp takes a more radical approach than s7: it extends operators such as '>'
- to compare strings and lists, as well as numbers. In map and for-each, however, you can mix the argument
- types, so I'm not as attracted to making '>' generic; you can't, for example, <code>(> "hi" 32.1)</code>,
- or even <code>(> 1 0+i)</code>.
- </p>
- </div>
- </blockquote>
-
- <div class="separator"></div>
-
- <p>The somewhat non-standard generic sequence functions in s7 are:
- </p>
-
- <pre class="indented">
- (<em class=def id="sortb">sort!</em> sequence less?)
- (<em class=def id="reverseb">reverse!</em> sequence) and (reverse sequence)
- (<em class=def id="fillb">fill!</em> sequence value (start 0) end)
- (<em class=def id="s7copy">copy</em> obj) and (copy source destination (start 0) end)
- (<em class=def id="objecttostring">object->string</em> obj)
- (object->let obj)
- (length obj)
- (append . sequences)
- (map func . sequences) and (for-each func . sequences)
- (<a href="#morallyequalp">morally-equal?</a> obj1 obj2)
- </pre>
-
- <p><b>reverse!</b> is an in-place version of reverse. That is,
- it modifies the sequence passed to it in the process of reversing its contents.
- If the sequence is a list, remember to use set!:
- <code>(set! p (reverse! p))</code>. This is somewhat inconsistent with other cases,
- but historically, lisp programmers have treated the in-place reverse as the fast
- version, so s7 follows suit.
- </p>
-
- <p>Leaving aside the weird list case,
- <b>append</b> returns a sequence of the same type as its first argument.
- </p>
-
- <pre class="indented">
- > (append #(1 2) '(3 4))
- <em class="gray">#(1 2 3 4)</em>
- > (append (float-vector) '(1 2) (byte-vector 3 4))
- <em class="gray">(float-vector 1.0 2.0 3.0 4.0)</em>
- </pre>
-
- <p>
- <b>sort!</b> sorts a sequence using the
- function passed as its second argument:
- </p>
-
- <pre class="indented">
- > (sort! (list 3 4 8 2 0 1 5 9 7 6) <)
- <em class="gray">(0 1 2 3 4 5 6 7 8 9)</em>
- </pre>
-
- <p>Underlying some of these functions are generic iterators, also built-into s7:
- </p>
-
- <pre class="indented">
- (<em class=def id="makeiterator">make-iterator</em> sequence)
- (<em class=def id="iteratorp">iterator?</em> obj)
- (<em class=def id="iterate">iterate</em> iterator)
- (<em class=def id="iteratorsequence">iterator-sequence</em> iterator)
- (<em class=def id="iteratoratend">iterator-at-end?</em> iterator)
- </pre>
-
- <p><b>make-iterator</b> takes a sequence argument and returns an iterator object that traverses
- that sequence as it is called. The iterator itself can be treated as a function of no arguments,
- or (for code clarity) it can be the argument to <b>iterate</b>, which does the same thing.
- That is <code>(iter)</code> is the same as <code>(iterate iter)</code>. The sequence that an iterator is traversing
- is <b>iterator-sequence</b>.
- </p>
- <p>
- If the sequence is a hash-table or let, the iterator normally returns a cons of the key and value.
- There are many cases where this overhead is objectionable, so make-iterator takes a third optional
- argument, the cons to use (changing its car and cdr directly on each call).
- </p>
-
- <p>When an iterator reaches the end of its sequence, it returns #<eof>. It used to
- return nil; I can't decide whether this change is an improvement. If an iterator over a
- list notices that its list is circular, it returns #<eof>. map and for-each use
- iterators, so if you pass a circular list to either, it will stop eventually. (An
- arcane consequence for method writers: specialize make-iterator, not map or for-each).
- </p>
-
- <pre class="indented">
- (define (find-if f sequence)
- (let ((iter (make-iterator sequence)))
- (do ((x (iter) (iter)))
- ((or (eof-object? x) (f x))
- (and (not (eof-object? x)) x)))))
- </pre>
-
- <p>But of course a sequence might contain #<eof>! So to be really safe, use iterator-at-end?
- instead of eof-object?.
- </p>
-
- <p>The argument to make-iterator can also be a function or macro.
- There should be a variable named 'iterator with a non-#f
- value in the closure's environment:
- </p>
- <pre class="indented">
- (define (make-circular-iterator obj)
- (let ((iter (make-iterator obj)))
- (make-iterator
- (let ((iterator? #t))
- (lambda ()
- (let ((result (iter)))
- (if (eof-object? result)
- ((set! iter (make-iterator obj)))
- result)))))))
- </pre>
- <p>The 'iterator? variable is similar to the 'documentation variable used by procedure-documentation.
- It gives make-iterator some hope of catching inadvertent bogus function arguments that would
- otherwise cause an infinite loop.
- </p>
-
-
- <div class="header" id="multidimensionalvectors"><h4>multidimensional vectors</h4></div>
-
-
- <p>
- s7 supports
- vectors with any number of dimensions. It is here, in particular, that generalized
- set! shines. make-vector's second argument can be a list of dimensions, rather than
- an integer as in the one dimensional case:
- </p>
-
- <pre class="indented">
- (make-vector (list 2 3 4))
- (make-vector '(2 3) 1.0)
- (vector-dimensions (make-vector '(2 3 4))) -> (2 3 4)
- </pre>
-
- <p>The second example includes the optional initial element.
- <code>(vect i ...)</code> or <code>(vector-ref vect i ...)</code> return the given
- element, and <code>(set! (vect i ...) value)</code> and <code>(vector-set! vect i ... value)</code>
- set it. vector-length (or just length) returns the total number of elements.
- vector-dimensions returns a list of the dimensions.
- </p>
-
- <pre class="indented">
- > (define v (make-vector '(2 3) 1.0))
- <em class="gray">#2D((1.0 1.0 1.0) (1.0 1.0 1.0))</em>
- > (set! (v 0 1) 2.0)
- <em class="gray">#2D((1.0 2.0 1.0) (1.0 1.0 1.0))</em>
- > (v 0 1)
- <em class="gray">2.0</em>
- > (vector-length v)
- <em class="gray">6</em>
- </pre>
-
- <p>This function initializes each element of a multidimensional vector:
- </p>
-
- <pre class="indented">
- (define (make-array dims . inits)
- (make-shared-vector (apply vector (flatten inits)) dims))
-
- > (make-array '(3 3) '(1 1 1) '(2 2 2) '(3 3 3))
- <em class="gray">#2D((1 1 1) (2 2 2) (3 3 3))</em>
- </pre>
-
- <p>make-int-vector and make-float-vector produce homogeneous vectors holding
- s7_ints or s7_doubles.
- These are mostly useful in conjunction with C code. These
- homogeneous vector functions are currently built-in:
- </p>
-
- <pre class="indented">
- (<em class=def>float-vector?</em> obj)
- (<em class=def>float-vector</em> . args)
- (<em class=def>make-float-vector</em> len (init 0.0))
- (<em class=def>float-vector-ref</em> obj . indices)
- (<em class=def>float-vector-set!</em> obj indices[...] value)
-
- (<em class=def id="intvectorp">int-vector?</em> obj)
- (<em class=def id="intvector">int-vector</em> . args)
- (<em class=def id="makeintvector">make-int-vector</em> len (init 0))
- (<em class=def id="intvectorref">int-vector-ref</em> obj . indices)
- (<em class=def id="intvectorset">int-vector-set!</em> obj indices[...] value)
- </pre>
-
- <div class="indented">
- <p>And also, for completeness:</p>
- <pre class="indented">
- (<em class=def id="bytevectorp">byte-vector?</em> obj)
- (<em class=def id="bytevector">byte-vector</em> . args)
- (<em class=def id="makebytevector">make-byte-vector</em> len (init 0))
- (<em class=def id="stringtobytevector">string->byte-vector</em> str)
- </pre>
- <p>but these are really just strings in disguise.</p>
- </div>
-
- <p>To access a vector's elements with different dimensions than the original had, use
- <code>(make-shared-vector original-vector new-dimensions (offset 0))</code>:
- </p>
-
- <pre class="indented">
- > (let ((v1 #2d((1 2 3) (4 5 6))))
- (let ((v2 (make-shared-vector v1 '(6)))) ; flatten the original
- v2))
- <em class="gray">#(1 2 3 4 5 6)</em>
- > (let ((v1 #(1 2 3 4 5 6)))
- (let ((v2 (make-shared-vector v1 '(3 2))))
- v2))
- <em class="gray">#2D((1 2) (3 4) (5 6))</em>
- </pre>
- <blockquote>
-
- <div class="indented">
-
- <p>matrix multiplication:
- </p>
-
- <pre>
- (define (matrix-multiply A B)
- ;; assume square matrices and so on for simplicity
- (let ((size (car (vector-dimensions A))))
- (do ((C (make-vector (list size size) 0))
- (i 0 (+ i 1)))
- ((= i size) C)
- (do ((j 0 (+ j 1)))
- ((= j size))
- (do ((sum 0)
- (k 0 (+ k 1)))
- ((= k size)
- (set! (C i j) sum))
- (set! sum (+ sum (* (A i k) (B k j)))))))))
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>Conway's game of Life:
- </p>
-
- <pre>
- (define* (life (width 40) (height 40))
- (let ((state0 (make-vector (list width height) 0))
- (state1 (make-vector (list width height) 0)))
-
- ;; initialize with some random pattern
- (do ((x 0 (+ x 1)))
- ((= x width))
- (do ((y 0 (+ y 1)))
- ((= y height))
- (set! (state0 x y) (if (< (random 100) 15) 1 0))))
-
- (do () ()
- ;; show current state (using terminal escape sequences, borrowed from the Rosetta C code)
- (format *stderr* "~C[H" #\escape) ; ESC H = tab set
- (do ((y 0 (+ y 1)))
- ((= y height))
- (do ((x 0 (+ x 1)))
- ((= x width))
- (format *stderr*
- (if (zero? (state0 x y))
- " " ; ESC 07m below = inverse
- (values "~C[07m ~C[m" #\escape #\escape))))
- (format *stderr* "~C[E" #\escape)) ; ESC E = next line
-
- ;; get the next state
- (do ((x 1 (+ x 1)))
- ((= x (- width 1)))
- (do ((y 1 (+ y 1)))
- ((= y (- height 1)))
- (let ((n (+ (state0 (- x 1) (- y 1))
- (state0 x (- y 1))
- (state0 (+ x 1) (- y 1))
- (state0 (- x 1) y)
- (state0 (+ x 1) y)
- (state0 (- x 1) (+ y 1))
- (state0 x (+ y 1))
- (state0 (+ x 1) (+ y 1)))))
- (set! (state1 x y)
- (if (or (= n 3)
- (and (= n 2)
- (not (zero? (state0 x y)))))
- 1 0)))))
- (do ((x 0 (+ x 1)))
- ((= x width))
- (do ((y 0 (+ y 1)))
- ((= y height))
- (set! (state0 x y) (state1 x y)))))))
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>Multidimensional vector constant syntax is modelled after CL: #nd(...) or #nD(...)
- signals that the lists specify the elements of an 'n' dimensional vector: <code>#2D((1 2 3) (4 5 6))</code>
- </p>
-
- <pre class="indented">
- > (vector-ref #2D((1 2 3) (4 5 6)) 1 2)
- <em class="gray">6</em>
- > (matrix-multiply #2d((-1 0) (0 -1)) #2d((2 0) (-2 2)))
- <em class="gray">#2D((-2 0) (2 -2))</em>
- </pre>
-
- <p>If any dimension has 0 length, you get an n-dimensional empty vector. It is not
- equal to a 1-dimensional empty vector.
- </p>
-
- <pre class="indented">
- > (make-vector '(10 0 3))
- <em class="gray">#3D()</em>
- > (equal? #() #3D())
- <em class="gray">#f</em>
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>To save on costly parentheses, and make it easier to write generic multidimensional sequence functions,
- you can use this same syntax with lists.
- </p>
-
- <pre class="indented">
- > (let ((L '((1 2 3) (4 5 6))))
- (L 1 0)) ; same as (list-ref (list-ref L 1) 0) or ((L 1) 0)
- <em class="gray">4</em>
- > (let ((L '(((1 2 3) (4 5 6)) ((7 8 9) (10 11 12)))))
- (set! (L 1 0 2) 32) ; same as (list-set! (list-ref (list-ref L 1) 0) 2 32) which is unreadable!
- L)
- <em class="gray">(((1 2 3) (4 5 6)) ((7 8 32) (10 11 12)))</em>
- </pre>
-
- <p>Or with vectors of vectors, of course:
- </p>
-
- <pre class="indented">
- > (let ((V #(#(1 2 3) #(4 5 6))))
- (V 1 2)) ; same as (vector-ref (vector-ref V 1) 2) or ((V 1) 2)
- <em class="gray">6</em>
- > (let ((V #2d((1 2 3) (4 5 6))))
- (V 0))
- <em class="gray">#(1 2 3)</em>
- </pre>
-
- <p>There's one difference between a vector-of-vectors and a multidimensional vector:
- in the latter case, you can't clobber one of the inner vectors.
- </p>
-
- <pre class="indented">
- > (let ((V #(#(1 2 3) #(4 5 6)))) (set! (V 1) 32) V)
- <em class="gray">#(#(1 2 3) 32)</em>
- > (let ((V #2d((1 2 3) (4 5 6)))) (set! (V 1) 32) V)
- <em class="gray">;not enough args for vector-set!: (#2D((1 2 3) (4 5 6)) 1 32)</em>
- </pre>
- </div>
-
-
-
- <div class="indented">
-
- <p>Using lists to display the inner vectors may not be optimal, especially when the elements are also lists:
- </p>
-
- <pre class="indented">
- #2D(((0) (0) ((0))) ((0) 0 ((0))))
- </pre>
-
- <p>The "#()" notation is no better (the elements can be vectors), and I'm not a fan of "[]" parentheses.
- Perhaps we could use different colors? Or different size parentheses?
- </p>
-
- <pre class="indented">
- #2D<em class=green>(</em><em class=red>(</em>(0) (0) ((0))<em class=red>)</em> <em class=red>(</em>(0) 0 ((0))<em class=red>)</em><em class=green>)</em>
- #2D<em class="bigger">(</em><em class="big">(</em>(0) (0) ((0))<em class="big">)</em> <em class="big">(</em>(0) 0 ((0))<em class="big">)</em><em class="bigger">)</em>
- </pre>
-
- <p>A similar problem afflicts homogeneous vectors. We need some reasonable way to express
- such a vector even when it has more than one dimension. My first thought was <code>#(...)#</code>,
- but that makes <code>(let ((b1 0)) (#(1 2)#b1))</code> ambiguous.
- </p>
-
- </div>
-
-
-
- <div class="indented">
-
- <p>I'm not sure how to handle vector->list and list->vector in the multidimensional case.
- Currently, vector->list flattens the vector, and list->vector always returns a
- one dimensional vector, so the two are not inverses.
- </p>
-
- <pre class="indented">
- > (vector->list #2d((1 2) (3 4)))
- <em class="gray">(1 2 3 4)</em> ; should this be '((1 2) (3 4)) or '(#(1 2) #(3 4))?
- > (list->vector '(#(1 2) #(3 4))) ; what about '((1 2) (3 4))?
- <em class="gray">#(#(1 2) #(3 4)) </em>
- </pre>
-
- <p>
- This also affects format and sort!:
- </p>
-
- <pre class="indented">
- > (format #f "~{~A~^ ~}" #2d((1 2) (3 4)))
- <em class="gray">"1 2 3 4"</em>
- > (sort! #2d((1 4) (3 2)) >)
- <em class="gray">#2D((4 3) (2 1))</em>
- </pre>
-
- <p>Perhaps make-shared-vector can help:
- </p>
-
- <pre class="indented">
- >(make-shared-vector (list->vector '(1 2 3 4)) '(2 2))
- <em class="gray">#2D((1 2) (3 4))</em>
- </pre>
-
- </div>
-
- <div class="indented">
-
- <p>Another question: should we accept the multi-index syntax in a case such as <code>
- (#("abc" "def") 0 2)</code>?
- My first thought was that the indices should all refer to the same
- type of object, so s7 would complain in a mixed case like that.
- If we can nest any applicable objects and apply the whole thing to
- an arbitrary list of indices, ambiguities arise:
- </p>
-
- <pre class="indented">
- ((lambda (x) x) "hi" 0)
- ((lambda (x) (lambda (y) (+ x y))) 1 2)
- </pre>
-
- <p>I think these should complain that the function got too many arguments,
- but from the implicit indexing point of view, they could be interpreted
- as:
- </p>
-
- <pre class="indented">
- (string-ref ((lambda (x) x) "hi") 0) ; i.e. (((lambda (x) x) "hi") 0)
- (((lambda (x) (lambda (y) (+ x y))) 1) 2)
- </pre>
-
- <p>Add optional and rest arguments, and you can't tell who is supposed to
- take which arguments.
- Currently, you can mix types with implicit indices,
- but a function grabs all remaining indices. Trickier than I expected!
- </p>
-
- </div>
-
- </blockquote>
-
-
-
-
-
-
- <div class="header" id="hashtables"><h4>hash-tables</h4></div>
-
-
- <ul>
- <li>(<em class=def id="makehashtable">make-hash-table</em> (size 8) eq-func)
- <li>(<em class=def id="hashtable">hash-table</em> ...)
- <li>(<em class=def id="hashtablestar">hash-table*</em> ...)
- <li>(<em class=def id="hashtablep">hash-table?</em> obj)
- <li>(<em class=def id="hashtableref">hash-table-ref</em> ht key)
- <li>(<em class=def id="hashtableset">hash-table-set!</em> ht key value)
- <li>(<em class=def id="hashtableentries">hash-table-entries</em> ht)
- </ul>
-
- <p>
- Each hash-table keeps track of the keys it contains, optimizing the search wherever possible.
- Any s7 object can be the key or the key's value.
- If you pass a table size that is not a power of 2, make-hash-table rounds it up to the next power of 2.
- The table grows as needed. length returns the current size.
- If a key is not in the table, hash-table-ref returns #f. To remove a key,
- set its value to #f; to remove all keys, <code>(fill! table #f)</code>.
- </p>
-
- <pre class="indented">
- > (let ((ht (make-hash-table)))
- (set! (ht "hi") 123)
- (ht "hi"))
- <em class="gray">123</em>
- </pre>
-
- <p>hash-table (the function) parallels the functions vector, list, and string. Its arguments are conses containing key/value pairs.
- The result is a new hash-table with those values preinstalled: <code>(hash-table '("hi" . 32) '("ho" . 1))</code>.
- After much use, I now think it is more convenient here, as in inlet, to use hash-table*; its arguments are
- simply the keys and values, without the consing: <code>(hash-table* 'a 1 'b 2)</code>.
- Implicit indexing gives multilevel hashes:
- </p>
-
- <pre class="indented">
- > (let ((h (hash-table* 'a (hash-table* 'b 2 'c 3)))) (h 'a 'b))
- <em class="gray">2</em>
- > (let ((h (hash-table* 'a (hash-table* 'b 2 'c 3)))) (set! (h 'a 'b) 4) (h 'a 'b))
- <em class="gray">4</em>
- </pre>
-
- <blockquote>
-
- <div class="indented">
-
- <p>Since hash-tables accept the same applicable-object syntax that vectors use, we can
- treat a hash-table as, for example, a sparse array:
- </p>
-
- <pre class="indented">
- > (define make-sparse-array make-hash-table)
- <em class="gray">make-sparse-array</em>
- > (let ((arr (make-sparse-array)))
- (set! (arr 1032) "1032")
- (set! (arr -23) "-23")
- (list (arr 1032) (arr -23)))
- <em class="gray">("1032" "-23")</em>
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>map and for-each accept hash-table arguments. On each iteration, the map or for-each function is passed
- an entry, <code>'(key . value)</code>, in whatever order the entries are encountered in the table.
- </p>
-
- <pre class="indented">
- (define (hash-table->alist table)
- (map values table))
-
- (define (merge-hash-tables . tables) ; probably faster: (define merge-hash-tables append)
- (apply hash-table (apply map values hash-tables)))
- </pre>
-
- <p>reverse of a hash-table returns a new table with the keys and values reversed.
- fill! sets all the values.
- Two hash-tables are equal if they have the same keys with the same values. This is independent
- of the table sizes, or the order in which the key/value pairs were added.
- </p>
- </div>
-
- <div class="indented">
- <p>The third argument to make-hash-table (eq-func) is slightly complicated. If it is omitted,
- s7 chooses the hashing equality and mapping functions based on the keys in the hash-table.
- There are times when you know
- in advance what equality function you want. If it's one of the built-in s7 equality
- functions, eq?, eqv?, equal?, morally-equal?, =, string=?, string-ci=?, char=?, or char-ci=?,
- you can pass that function as the third argument. In any other case, you need to
- give s7 both the equality function and the mapping function. The latter takes any object
- and returns the hash-table location for it (an integer). The problem here is that
- for the arbitrary equality function to work, objects that are equal according to that
- function have to be mapped to the same hash-table location. There's no way for s7 to intuit
- what this mapping should be except in the built-in cases. So to specify some arbitrary function, the third
- argument is a cons: '(equality-checker mapper).
- </p>
-
- <p>Here's a brief example. In CLM, we have c-objects of type mus-generator (from s7's point of view),
- and we want to hash them using equal? (which will call the generator-specific equality function).
- But s7 doesn't realize that the mus-generator type covers 40 or 50 internal types, so as the mapper we pass mus-type:
- <code>(make-hash-table 64 (cons equal? mus-type))</code>.
- </p>
- </div>
-
- <div class="indented">
- <p>If the hash key is a float (a non-rational number), hash-table-ref uses <a href="#morallyequalp">morally-equal?</a>.
- Otherwise, for example, you could use NaN as a key, but then never be able to access it!
- </p>
- </div>
-
-
- <div class="indented">
- <pre>
- (define-macro (define-memoized name&arg . body)
- (let ((arg (cadr name&arg))
- (memo (gensym "memo")))
- `(define ,(car name&arg)
- (let ((,memo (<em class=red>make-hash-table</em>)))
- (lambda (,arg)
- (or (,memo ,arg) ; check for saved value
- (set! (,memo ,arg) (begin ,@body)))))))) ; set! returns the new value
-
- > (define (fib n)
- (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
- <em class="gray">fib</em>
- > (define-memoized
- (memo-fib n)
- (if (< n 2) n (+ (memo-fib (- n 1)) (memo-fib (- n 2)))))
- <em class="gray">memo-fib</em>
- > (time (fib 34)) ; un-memoized time
- <em class="gray">1.168</em> ; 0.70 on ccrma's i7-3930 machines
- > (time (memo-fib 34)) ; memoized time
- <em class="gray">3.200e-05</em>
- > (outlet (funclet memo-fib))
- <em class="gray">(inlet '{memo}-18 (hash-table
- '(0 . 0) '(1 . 1) '(2 . 1) '(3 . 2) '(4 . 3) '(5 . 5)
- '(6 . 8) '(7 . 13) '(8 . 21) '(9 . 34) '(10 . 55) '(11 . 89)
- '(12 . 144) '(13 . 233) '(14 . 377) '(15 . 610) '(16 . 987)
- '(17 . 1597) '(18 . 2584) '(19 . 4181) '(20 . 6765) '(21 . 10946)
- '(22 . 17711) '(23 . 28657) '(24 . 46368) '(25 . 75025) '(26 . 121393)
- '(27 . 196418) '(28 . 317811) '(29 . 514229) '(30 . 832040) '(31 . 1346269)
- '(32 . 2178309) '(33 . 3524578) '(34 . 5702887)))</em>
- </pre>
- </div>
-
- </blockquote>
-
-
-
-
-
- <div class="header" id="environments"><h4>environments</h4></div>
-
-
- <p>An environment holds symbols and their values. The global environment, for example,
- holds all the variables that are defined at the top level.
- Environments are first class (and applicable) objects in s7.
- </p>
-
- <pre class="indented">
- (<em class=def id="rootlet">rootlet</em>) the top-level (global) environment
- (<em class=def id="curlet">curlet</em>) the current (innermost) environment
- (<em class=def id="funclet">funclet</em> proc) the environment at the time when proc was defined
- (owlet) the environment at the point of the last error
- (<em class=def id="unlet">unlet</em>) the current environment, but all built-in functions have their original values
-
- (<em class=def id="letref">let-ref</em> env sym) get value of sym in env, same as (env sym)
- (<em class=def id="letset">let-set!</em> env sym val)
-
- (<em class=def id="inlet">inlet</em> . bindings) make a new environment with the given bindings
- (<em class=def id="sublet">sublet</em> env . bindings) same as inlet, but the new environment is local to env
- (<em class=def id="varlet">varlet</em> env . bindings) add new bindings directly to env
- (<em class=def id="cutlet">cutlet</em> env . fields) remove bindings from env
-
- (<em class=def id="letp">let?</em> obj) #t if obj is an environment
- (<em class=def id="with-let">with-let</em> env . body) evaluate body in the environment env
- (<em class=def id="outlet">outlet</em> env) the environment that encloses the environment env (settable)
- (<em class=def id="lettolist">let->list</em> env) return the environment bindings as a list of (symbol . value) cons's
-
- (<em class=def id="openlet">openlet</em> env) mark env as open (see below)
- (<em class=def id="openletp">openlet?</em> env) #t is env is open
- (<em class=def id="coverlet">coverlet</em> env) mark env as closed (undo an earlier openlet)
-
- (<em class=def id="objecttolet">object->let</em> obj) return an environment containing information about obj
- (<em class=def id="lettemporarily">let-temporarily</em> vars . body)
- </pre>
-
- <br>
- <blockquote>
- <pre class="indented">
- > (inlet 'a 1 'b 2)
- <em class="gray">(inlet 'a 1 'b 2)</em>
- > (let ((a 1) (b 2)) (curlet))
- <em class="gray">(inlet 'a 1 'b 2)</em>
- > (let ((x (inlet :a 1 :b 2))) (x 'a))
- <em class="gray">1</em>
- > (with-let (inlet 'a 1 'b 2) (+ a b))
- <em class="gray">3</em>
- > (let ((x (inlet :a 1 :b 2))) (set! (x 'a) 4) x)
- <em class="gray">(inlet 'a 4 'b 2)</em>
- > (let ((x (inlet))) (varlet x 'a 1) x)
- <em class="gray">(inlet 'a 1)</em>
- > (let ((a 1)) (let ((b 2)) (outlet (curlet))))
- <em class="gray">(inlet 'a 1)</em>
- > (let ((e (inlet 'a (inlet 'b 1 'c 2)))) (e 'a 'b)) ; in C terms, e->a->b
- <em class="gray">1</em>
- > (let ((e (inlet 'a (inlet 'b 1 'c 2)))) (set! (e 'a 'b) 3) (e 'a 'b))
- <em class="gray">3</em>
- </pre>
- </blockquote>
-
-
- <p>As the names suggest, in s7 an environment is viewed as a disembodied let. Environments are equal if they
- contain the same symbols with the same values leaving aside shadowing, and taking into account the environment
- chain up to the rootlet. That is, two environments are equal if any local variable of either has the same value in both.
- </p>
-
- <p><b>with-let</b> evaluates its body in the given environment, so
- <code>(with-let e . body)</code> is equivalent to
- <code>(eval `(begin ,@body) e)</code>, but probably faster.
- Similarly, <code>(let bindings . body)</code> is equivalent to
- <code>(eval `(begin ,@body) (apply inlet (flatten bindings)))</code>,
- ignoring the outer (enclosing) environment (the default outer environment
- of inlet is rootlet).
- Or better,
- </p>
- <pre class="indented">
- (define-macro (with-environs e . body)
- `(apply let (map (lambda (a) (list (car a) (cdr a))) ,e) '(,@body)))
- </pre>
- <p>Or turning it around,</p>
- <pre>
- (define-macro (Let vars . body)
- `(with-let (sublet (curlet)
- ,@(map (lambda (var)
- (values (symbol->keyword (car var)) (cadr var)))
- vars))
- ,@body))
-
- (Let ((c 4))
- (Let ((a 2)
- (b (+ c 2)))
- (+ a b c)))
- </pre>
-
-
- <p><b>let-temporarily</b> (now built-into s7) is somewhat similar to fluid-let in other Schemes.
- Its syntax looks like
- let, but it first saves the current value, then sets the
- variable to the new value (via set!), calls the body, and finally restores the
- original value. It can handle anything settable:
- </p>
- <pre class="indented">
- (let-temporarily (((*s7* 'print-length) 8)) (display x))
- </pre>
- <p>This sets s7's print-length variable to 8 while displaying x, then
- puts it back to its original value.
- </p>
-
-
- <p>
- <b>sublet</b> adds bindings (symbols with associated values) to an environment.
- It does not change the environment passed to it, but
- just prepends the new bindings, shadowing any old ones,
- as if you had called "let".
- To add the bindings directly to the environment,
- use <b>varlet</b>. Both of these functions accept nil as the
- 'env' argument as shorthand for <code>(rootlet)</code>.
- Both also accept other environments as well as individual bindings,
- adding all the argument's bindings to the new environment.
- <b>inlet</b> is very similar, but normally omits the environment argument.
- The arguments to sublet and inlet can be passed as
- symbol/value pairs, as a cons, or using keywords as if in define*.
- inlet can also be used to copy an environment without accidentally invoking
- that environment's copy method.
- </p>
-
- <p>Here's an example: we want to define two functions that share a
- local variable:
- </p>
-
- <pre class="indented">
- (varlet (curlet) ; import f1 and f2 into the current environment
- (let ((x 1)) ; x is our local variable
- (define (f1 a) (+ a x))
- (define (f2 b) (* b x))
- (inlet 'f1 f1 'f2 f2))) ; export f1 and f2
- </pre>
-
- <p>One way to add reader and writer functions to an individual environment slot is:
- </p>
-
- <pre class="indented">
- (define e (inlet
- 'x (let ((local-x 3)) ; x's initial value
- (dilambda
- (lambda () local-x)
- (lambda (val) (set! local-x (max 0 (min val 100))))))))
- > ((e 'x))
- <em class="gray">3</em>
- > (set! ((e 'x)) 123)
- <em class="gray">100</em>
- </pre>
-
- <blockquote>
- <div class="indented">
- <p>I originally used a bunch of foolishly pompous names for the environment functions.
- Two are still available for backwards compatibility:
- </p>
-
- <pre class="indented">
- rootlet global-environment
- curlet current-environment
- </pre>
- </div>
- </blockquote>
-
-
- <p>It is possible in Scheme to redefine built-in functions such as car.
- To ensure that some code sees the original built-in function definitions,
- wrap it in <code>(with-let (unlet) ...)</code>:
- </p>
- <pre class="indented">
- > (let ((caar 123))
- (+ caar (with-let (unlet)
- (caar '((2) 3)))))
- <em class="gray">125</em>
- </pre>
-
- <p>
- with-let and unlet are constants, so you can
- use them in any context without worrying about whether they've been redefined.
- As mentioned in the macro section, #_<name> is a built-in reader macro
- for <code>(with-let (unlet) <name>)</code>,
- so for example, #_+ is the built-in + function, no matter what.
- <code>(unlet)</code> cleans up the current environment whenever it's called,
- so you can use it to revert the REPL. (The environment of built-in functions
- that unlet accesses is not accessible from scheme code, so there's no way
- that those values can be clobbered).
- </p>
-
- <blockquote>
-
- <div class="indented">
-
- <p>
- I think these functions can implement the notions of libraries,
- separate namespaces, or modules.
- Here's one way: first the library writer just writes his library.
- The normal user simply loads it. The abnormal user worries about everything,
- so first he loads the library in a local let to make sure no bindings escape
- to pollute his code, and then he
- uses unlet to
- make sure that none of his bindings pollute the library code:
- </p>
-
- <pre class="indented">
- (let ()
- (with-let (unlet)
- (load "any-library.scm" (curlet))
- ;; by default load puts stuff in the global environment
- ...))
- </pre>
-
- <p>Now Abnormal User can do what he wants with the library entities.
- Say he wants to use "lognor" under the name "bitwise-not-or", and
- all the other functions are of no interest:
- </p>
-
- <pre class="indented">
- (varlet (curlet)
- 'bitwise-not-or (with-let (unlet)
- (load "any-library.scm" (curlet))
- lognor)) ; lognor is presumably defined in "any-library.scm"
- </pre>
-
- <p>Say he wants to make sure the library is cleanly loaded, but all
- its top-level bindings are imported into the current environment:
- </p>
-
- <pre class="indented">
- (varlet (curlet)
- (with-let (unlet)
- (let ()
- (load "any-library.scm" (curlet))
- (curlet)))) ; these are the bindings introduced by loading the library
- </pre>
-
- <p>To do the same thing, but prepend "library:" to each name:
- </p>
-
- <pre class="indented">
- (apply varlet (curlet)
- (with-let (unlet)
- (let ()
- (load "any-library.scm" (curlet))
- (map (lambda (binding)
- (cons (symbol "library:" (symbol->string (car binding)))
- (cdr binding)))
- (curlet)))))
- </pre>
-
- <p>That's all there is to it! Here is the same idea as a macro:
- </p>
-
- <pre>
- (define-macro (let! init end . body)
- ;; syntax mimics 'do: (let! (vars&values) ((exported-names) result) body)
- ;; (let! ((a 1)) ((hiho)) (define (hiho x) (+ a x)))
- `(let ,init
- ,@body
- (varlet (outlet (curlet))
- ,@(map (lambda (export)
- `(cons ',export ,export))
- (car end)))
- ,@(cdr end)))
- </pre>
-
- <!--
- (define-macro (safe-let! init end . body)
- `(with-let (#_inlet (unlet)
- ,@(#_map (#_lambda (b)
- `(#_cons ',(#_car b) ,(#_cadr b)))
- init))
- ,@body
- (#_varlet (#_outlet (#_curlet))
- ,@(#_map (#_lambda (export)
- `(#_cons ',export ,export))
- (#_car end)))
- ,@(#_cdr end)))
- -->
-
- </div>
-
- <div class="indented">
- <p>Well, almost, darn it. If the loaded library file sets (via set!) a global value
- such as abs, we need to put it back to its original form:
- </p>
-
- <pre>
- (define (safe-load file)
- (let ((e (with-let (unlet) ; save the environment before loading
- (let->list (curlet)))))
- (<em class=red>load</em> file (curlet))
- (let ((new-e (with-let (unlet) ; get the environment after loading
- (let->list (curlet)))))
- (for-each ; see if any built-in functions were stepped on
- (lambda (sym)
- (unless (assoc (car sym) e)
- (format () "~S clobbered ~A~%" file (car sym))
- (apply set! (car sym) (list (cdr sym)))))
- new-e))))
-
- ;; say libtest.scm has the line (set! abs odd?)
-
- > (safe-load "libtest.scm")
- <em class="gray">"libtest.scm" clobbered abs</em>
- > (abs -2)
- <em class="gray">2</em>
- </pre>
-
- </div>
- </blockquote>
-
-
- <p><b>openlet</b> marks its argument, either an environment, a closure, or a c-object
- as open. I need better terminology here! An open object is one that the
- built-in s7 functions handle specially. If they encounter one in their
- argument list, they look in the object for their own name, and call that
- function if it exists. A bare-bones example:
- </p>
-
- <pre class="indented">
- > (abs (openlet (inlet 'abs (lambda (x) 47))))
- <em class="gray">47</em>
- > (define* (f1 (a 1)) (if (real? a) (abs a) ((a 'f1) a)))
- <em class="gray">f1</em>
- > (f1 :a (openlet (inlet 'f1 (lambda (e) 47))))
- <em class="gray">47</em>
- </pre>
-
- <p>In CLOS, we'd declare a class and a method, and call make-instance,
- and then discover that it wouldn't work anyway.
- Here we have, in effect, an anonymous instance of an anonymous class.
- I think this is called a "prototype system"; javascript is apparently similar.
- A slightly more complex example:
- </p>
-
- <pre class="indented">
- (let* ((e1 (openlet
- (inlet
- 'x 3
- '* (lambda args
- (apply * (if (number? (car args))
- (values (car args) ((cadr args) 'x) (cddr args))
- (values ((car args) 'x) (cdr args))))))))
- (e2 (copy e1)))
- (set! (e2 'x) 4)
- (* 2 e1 e2)) ; (* 2 3 4) => 24
- </pre>
-
- <p>Perhaps these names would be better: openlet -> with-methods, coverlet -> without-methods,
- and openlet? -> methods?.
- </p>
-
- <p><b>object->let</b> returns an environment (more of a dictionary really) that
- contains details about its argument. It
- is intended as a debugging aid, underlying a debugger's "inspect" for example.
- </p>
-
- <pre class="indented">
- > (let ((iter (make-iterator "1234")))
- (iter)
- (iter)
- (object->let iter))
- <em class="gray">(inlet 'value #<iterator: string> 'type iterator? 'at-end #f 'sequence "1234" 'length 4 'position 2)</em>
- </pre>
-
- <p>A c-object (in the sense of s7_new_type), can add its own info to this namespace via an object->let
- method in its local environment. snd-marks.c has a simple example using a class-wide environment (g_mark_methods),
- holding as the value of its 'object->let field the function s7_mark_to_let. The latter uses s7_varlet to
- add information to the namespace created by <code>(object->let mark)</code>.
- </p>
-
- <br>
-
-
- <blockquote>
-
- <details>
- <summary class="indented">define-class</summary>
- <div class="indented">
-
- <p>Here is yet another object system. Each class and each instance is an environment.
- A class might be thought of as a template for instances, but
- there's actually no difference between them. To make an instance of a class, copy it. To inherit from
- another class, concatenate the environments. To access (get or set) a field or a method, use the implicit
- indexing syntax with the field or method name. To evaluate a form in the context of an instance
- (CL's with-slots), use with-let. To run through all the fields, use map or for-each.
- </p>
-
- <pre>
- (define-macro* (define-class class-name inherited-classes (slots ()) (methods ()))
- `(let ((outer-env (outlet (curlet)))
- (new-methods ())
- (new-slots ()))
-
- (for-each
- (lambda (class)
- ;; each class is a set of nested environments, the innermost (first in the list)
- ;; holds the local slots which are copied each time an instance is created,
- ;; the next holds the class slots (global to all instances, not copied);
- ;; these hold the class name and other such info. The remaining environments
- ;; hold the methods, with the localmost method first. So in this loop, we
- ;; are gathering the local slots and all the methods of the inherited
- ;; classes, and will splice them together below as a new class.
-
- (set! new-slots (append (let->list class) new-slots))
- (do ((e (outlet (outlet class)) (outlet e)))
- ((or (not (let? e))
- (eq? e (rootlet))))
- (set! new-methods (append (let->list e) new-methods))))
- ,inherited-classes)
-
- (let ((remove-duplicates
- (lambda (lst) ; if multiple local slots with same name, take the localmost
- (letrec ((rem-dup
- (lambda (lst nlst)
- (cond ((null? lst) nlst)
- ((assq (caar lst) nlst) (rem-dup (cdr lst) nlst))
- (else (rem-dup (cdr lst) (cons (car lst) nlst)))))))
- (reverse (rem-dup lst ()))))))
- (set! new-slots
- (remove-duplicates
- (append (map (lambda (slot)
- (if (pair? slot)
- (cons (car slot) (cadr slot))
- (cons slot #f)))
- ,slots) ; the incoming new slots, #f is the default value
- new-slots)))) ; the inherited slots
-
- (set! new-methods
- (append (map (lambda (method)
- (if (pair? method)
- (cons (car method) (cadr method))
- (cons method #f)))
- ,methods) ; the incoming new methods
-
- ;; add an object->string method for this class (this is already a generic function).
- (list (cons 'object->string
- (lambda* (obj (use-write #t))
- (if (eq? use-write :readable) ; write readably
- (format #f "(make-~A~{ :~A ~W~^~})"
- ',class-name
- (map (lambda (slot)
- (values (car slot) (cdr slot)))
- obj))
- (format #f "#<~A: ~{~A~^ ~}>"
- ',class-name
- (map (lambda (slot)
- (list (car slot) (cdr slot)))
- obj))))))
- (reverse! new-methods))) ; the inherited methods, shadowed automatically
-
- (let ((new-class (openlet
- (apply sublet ; the local slots
- (sublet ; the global slots
- (apply inlet ; the methods
- (reverse new-methods))
- 'class-name ',class-name ; class-name slot
- 'inherited ,inherited-classes
- 'inheritors ()) ; classes that inherit from this class
- new-slots))))
-
- (varlet outer-env
- ',class-name new-class ; define the class as class-name in the calling environment
-
- ;; define class-name? type check
- (string->symbol (string-append (symbol->string ',class-name) "?"))
- (lambda (obj)
- (and (let? obj)
- (eq? (obj 'class-name) ',class-name))))
-
- (varlet outer-env
- ;; define the make-instance function for this class.
- ;; Each slot is a keyword argument to the make function.
- (string->symbol (string-append "make-" (symbol->string ',class-name)))
- (apply lambda* (map (lambda (slot)
- (if (pair? slot)
- (list (car slot) (cdr slot))
- (list slot #f)))
- new-slots)
- `((let ((new-obj (copy ,,class-name)))
- ,@(map (lambda (slot)
- `(set! (new-obj ',(car slot)) ,(car slot)))
- new-slots)
- new-obj))))
-
- ;; save inheritance info for this class for subsequent define-method
- (letrec ((add-inheritor (lambda (class)
- (for-each add-inheritor (class 'inherited))
- (if (not (memq new-class (class 'inheritors)))
- (set! (class 'inheritors) (cons new-class (class 'inheritors)))))))
- (for-each add-inheritor ,inherited-classes))
-
- ',class-name)))
-
- (define-macro (define-generic name) ; (define (genfun any) ((any 'genfun) any))
- `(define ,name
- (lambda args
- (let ((gf ((car args) ',name))) ; get local definition
- (if (not (eq? gf ,name)) ; avoid infinite recursion
- (apply gf args)
- (error "attempt to call generic function wrapper recursively"))))))
-
- (define-macro (define-slot-accessor name slot)
- `(define ,name (dilambda
- (lambda (obj) (obj ',slot))
- (lambda (obj val) (set! (obj ',slot) val)))))
-
- (define-macro (define-method name-and-args . body)
- `(let* ((outer-env (outlet (curlet)))
- (method-name (car ',name-and-args))
- (method-args (cdr ',name-and-args))
- (object (caar method-args))
- (class (symbol->value (cadar method-args)))
- (old-method (class method-name))
- (method (apply lambda* method-args ',body)))
-
- ;; define the method as a normal-looking function
- ;; s7test.scm has define-method-with-next-method that implements call-next-method here
- ;; it also has make-instance
- (varlet outer-env
- method-name (apply lambda* method-args
- `(((,object ',method-name)
- ,@(map (lambda (arg)
- (if (pair? arg) (car arg) arg))
- method-args)))))
-
- ;; add the method to the class
- (varlet (outlet (outlet class)) method-name method)
-
- ;; if there are inheritors, add it to them as well, but not if they have a shadowing version
- (for-each
- (lambda (inheritor)
- (if (not (eq? (inheritor method-name) #<undefined>)) ; defined? goes to the global env
- (if (eq? (inheritor method-name) old-method)
- (set! (inheritor method-name) method))
- (varlet (outlet (outlet inheritor)) method-name method)))
- (class 'inheritors))
-
- method-name))
-
- (define (all-methods obj method)
- ;; for arbitrary method combinations: this returns a list of all the methods of a given name
- ;; in obj's class and the classes it inherits from (see example below)
- (let* ((base-method (obj method))
- (methods (if (procedure? base-method) (list base-method) ())))
- (for-each
- (lambda (ancestor)
- (let ((next-method (ancestor method)))
- (if (and (procedure? next-method)
- (not (memq next-method methods)))
- (set! methods (cons next-method methods)))))
- (obj 'inherited))
- (reverse methods)))
-
-
- > (define-class class-1 ()
- '((a 1) (b 2))
- (list (list 'add (lambda (obj)
- (with-let obj
- (+ a b))))))
- <em class="gray">class-1</em>
- > (define v (make-class-1 :a 32))
- <em class="gray">v </em>
- > (v 'a) ; to set the 'a slot, (set! (v 'a) 0)
- <em class="gray">32</em>
- > (object->string v) ; built-in functions are all generic
- <em class="gray">"#<class-1: (a 32) (b 2)>"</em> ; so this uses the method defined in the class definition
- > ((v 'add) v)
- <em class="gray">34</em>
- > (define-generic add)
- <em class="gray">add</em>
- > (add v) ; syntactic sugar for ((v 'add) v)
- <em class="gray">34</em>
- > (define-slot-accessor slot-a a) ; more sugar!
- <em class="gray">slot-a</em>
- > (slot-a v) ; same as (v 'a), set via (set! (slot-a v) 123)
- <em class="gray">32</em>
- > (map car v) ; map and for-each work with environments
- <em class="gray">(a b)</em> ; map cdr would return '(32 2) in this case
- > (define-class class-2 (list class-1)
- '((c 3))
- (list (list 'multiply (lambda (obj)
- (with-let obj
- (* a b c))))))
- <em class="gray">class-2</em>
- > (define v2 (make-class-2 :a 32))
- <em class="gray">v2</em>
- > v2 ; will use object->string
- <em class="gray">"#<class-2: (c 3) (a 32) (b 2)>"</em>
- > ((v2 'multiply) v2)
- <em class="gray">192</em>
- > (add v2) ; inherited from class-1
- <em class="gray">34</em>
- > (define-method (subtract (obj class-1)) (with-let obj (- a b)))
- <em class="gray">subtract</em>
- > (subtract v2) ; class-2 inherits from class-1 so it knows about subtract
- <em class="gray">30</em>
- > (define v1 (make-class-1))
- <em class="gray">v1</em>
- > (varlet v1 ; change the add method just in this instance
- 'add (lambda (obj)
- (with-let obj
- (+ 1 a (* 2 b)))))
- <em class="gray">#<class-1: (a 1) (b 2) (add #<lambda (obj)>)></em>
- > (add v1)
- <em class="gray">6</em>
- > (add v) ; other class-1 instances are not affected
- <em class="gray">34</em>
- > (define-class class-3 (list class-2) ()
- (list (list 'multiply (lambda (obj num)
- (* num
- ((class-2 'multiply) obj) ; method combination
- (add obj))))))
- <em class="gray">class-3</em>
- > ((class-3 'multiply) class-3 10)
- <em class="gray">180 ; (* 10 (* 1 2 3) (+ 1 2))</em>
- > (define v3 (make-class-3))
- <em class="gray">v3</em>
- > (all-methods v3 'multiply)
- <em class="gray">(#<lambda (obj num)> #<lambda (obj)>)</em>
- > (for-each (lambda (p) (format () "~A~%" (procedure-source p))) (all-methods v3 'multiply))
- <em class="gray">(lambda (obj num) (* num ((class-2 'multiply) obj) (add obj)))</em>
- <em class="gray">(lambda (obj) (with-let obj (* a b c)))</em>
- </pre>
-
- <p>with-let (used briefly above) provides an even more striking simplification of syntax
- than implicit indexing or multidimensional vectors, and it is faster as well!
- See Snd's generators.scm for many examples.
- </p>
-
- <!--
- at class definition time:
- (list 'mac (symbol->value (define-macro (mac a) `(+ ,a 1))))
- -->
- </div>
- </details>
-
-
- <details>
- <summary class="indented">more examples</summary>
- <div class="indented">
- <p>Implicit indexing of a local environment
- does not search the global environment. Since unlet extends the
- current environment chain, it is considered a local environment:
- </p>
-
- <pre class="indented">
- > ((rootlet) 'abs)
- <em class="gray">abs</em>
- > (let () ((curlet) 'abs))
- <em class="gray">#<undefined></em>
- > ((unlet) 'abs)
- <em class="gray">#<undefined></em>
- </pre>
-
- </div>
-
-
- <div class="indented">
- <pre>
- (define-macro (value->symbol expr)
- `(let ((val ,expr)
- (e1 (curlet)))
- (call-with-exit
- (lambda (return)
- (do ((e e1 (outlet e))) ()
- (for-each
- (lambda (slot)
- (if (equal? val (cdr slot))
- (return (car slot))))
- e)
- (if (eq? e (rootlet))
- (return #f)))))))
-
- > (let ((a 1) (b "hi"))
- (value->symbol "hi"))
- <em class="gray">b</em>
- </pre>
- </div>
-
-
- <div class="indented">
- <p>openlet alerts the rest of s7 that the environment has methods.
- </p>
-
- <pre>
- (begin
- (define fvector? #f)
- (define make-fvector #f)
- (let ((type (gensym))
- (->float (lambda (x)
- (if (real? x)
- (* x 1.0)
- (error 'wrong-type-arg "fvector new value is not a real: ~A" x)))))
- (set! make-fvector
- (lambda* (len (init 0.0))
- (<em class=red>openlet</em>
- (inlet :v (make-vector len (->float init))
- :type type
- :length (lambda (f) len)
- :object->string (lambda (f . args) "#<fvector>")
- :let-set! (lambda (fv i val) (#_vector-set! (fv 'v) i (->float val)))
- :let-ref (lambda (fv i) (#_vector-ref (fv 'v) i))))))
- (set! fvector? (lambda (p)
- (and (let? p)
- (eq? (p 'type) type))))))
-
- > (define fv (make-fvector 32))
- <em class="gray">fv</em>
- > fv
- <em class="gray">#<fvector></em>
- > (length fv)
- <em class="gray">32</em>
- > (set! (fv 0) 123)
- <em class="gray">123.0</em>
- > (fv 0)
- <em class="gray">123.0</em>
- </pre>
-
- </div>
-
-
- <div class="indented">
-
- <p>I can't resist adding at least some of a quaternion implementation
- (finally "number?" is not the same
- as "complex?"!):
- </p>
-
- <pre>
- (define-class quaternion ()
- '((r 0) (i 0) (j 0) (k 0))
- (list (list 'number? (lambda (obj) #t))
- (list 'complex? (lambda (obj) #f))
- (list 'real? (lambda (obj) #f))
- (list 'integer? (lambda (obj) #f))
- (list 'rational? (lambda (obj) #f))
-
- (list '+ (lambda orig-args
- (let add ((r ()) (i ()) (j ()) (k ()) (args orig-args))
- (if (null? args)
- (<em class=red>make-quaternion</em>
- (apply + r) (apply + i) (apply + j) (apply + k))
- (let ((n (car args)))
- (cond
- ((real? n)
- (add (cons n r) i j k (cdr args)))
- ((complex? n)
- (add (cons (real-part n) r) (cons (imag-part n) i) j k (cdr args)))
- ((<em class=red>quaternion?</em> n)
- (add (cons (n 'r) r) (cons (n 'i) i)
- (cons (n 'j) j) (cons (n 'k) k)
- (cdr args)))
- ((not (<em class=red>openlet?</em> n)) ; maybe we'll add octonions later!
- (error 'wrong-type-arg "+ argument ~A is not a number" n))
- ((eq? n (car orig-args))
- (error 'missing-method "+ can't handle these arguments: ~A" args))
- (else
- (apply (n '+)
- (make-quaternion
- (apply + r) (apply + i) (apply + j) (apply + k))
- (cdr args)))))))))))
-
- > (let ((q1 (make-quaternion 1.0 1.0 0.0 0.0)))
- (+ 1 q1 2.5+i))
- <em class="gray">#<quaternion: (r 4.5) (i 2.0) (j 0.0) (k 0.0)></em>
- </pre>
-
- </div>
- </details>
-
- <div class="indented">
- <p>If an s7 function ignores the type of an argument, as in cons or vector for example,
- then that argument won't be treated as having any methods.
- </p>
-
- <p>
- Since outlet is settable, there are two ways an environment can
- become circular. One is to include the current environment as the value of one of its variables.
- The other is: <code>(let () (set! (outlet (curlet)) (curlet)))</code>.
- </p>
-
- <p>If you want to hide an environment's fields from any part of s7 that does not
- know the field names in advance,
- </p>
- <pre class="indented">
- (openlet ; make it appear to be empty to the rest of s7
- (inlet 'object->string (lambda args "#<let>")
- 'map (lambda args ())
- 'for-each (lambda args #<unspecified>)
- 'let->list (lambda args ())
- 'length (lambda args 0)
- 'copy (lambda args (inlet))
- 'open #t
- 'coverlet (lambda (e) (set! (e 'open) #f) e)
- 'openlet (lambda (e) (set! (e 'open) #t) e)
- 'openlet? (lambda (e) (e 'open))
- ;; your secret data here
- ))
- </pre>
- <p>(There are still at least two ways to tell that something is fishy).
- </p>
- <!-- add a field and it disappears, or sublet and read back -->
- </div>
- </blockquote>
-
-
-
-
-
- <div class="header" id="multiplevalues"><h4>multiple-values</h4></div>
-
- <p>
- In s7, multiple values are spliced directly into the caller's argument list.
- </p>
-
- <pre class="indented">
- > (+ (values 1 2 3) 4)
- <em class="gray">10</em>
- > (string-ref ((lambda () (values "abcd" 2))))
- <em class="gray">#\c</em>
- > ((lambda (a b) (+ a b)) ((lambda () (values 1 2))))
- <em class="gray">3</em>
- > (+ (call/cc (lambda (ret) (ret 1 2 3))) 4) ; call/cc has an implicit "values"
- <em class="gray">10</em>
- > ((lambda* ((a 1) (b 2)) (list a b)) (values :a 3))
- <em class="gray">(3 2)</em>
-
- (define-macro (call-with-values producer consumer)
- `(,consumer (,producer)))
-
- (define-macro (multiple-value-bind vars expr . body)
- `((lambda ,vars ,@body) ,expr))
-
- (define-macro (define-values vars expression)
- `(if (not (null? ',vars))
- (varlet (curlet) ((lambda ,vars (curlet)) ,expression))))
-
- (define (curry function . args)
- (if (null? args)
- function
- (lambda more-args
- (if (null? more-args)
- (apply function args)
- (function (apply values args) (apply values more-args))))))
- </pre>
-
-
-
- <blockquote>
-
- <div class="indented">
-
- <p>multiple-values are useful in a several situations. For example,
- <code>(if test (+ a b c) (+ a b d e))</code> can be written
- <code>(+ a b (if test c (values d e)))</code>.
- There are a few special uses of multiple-values.
- First, you can use the values function to return any number of values, including 0,
- from map's function application:
- </p>
-
- <pre class="indented">
- > (map (lambda (x) (if (odd? x) (values x (* x 20)) (values))) (list 1 2 3))
- <em class="gray">(1 20 3 60)</em>
- > (map values (list 1 2 3) (list 4 5 6))
- <em class="gray">(1 4 2 5 3 6)</em>
-
- (define (remove-if func lst)
- (map (lambda (x) (if (func x) (values) x)) lst))
-
- (define (pick-mappings func lst)
- (map (lambda (x) (or (func x) (values))) lst))
-
- (define (shuffle . args)
- (apply map values args))
-
- > (shuffle '(1 2 3) #(4 5 6) '(7 8 9))
- <em class="gray">(1 4 7 2 5 8 3 6 9)</em>
-
- (define (concatenate . args)
- (apply append (map (lambda (arg) (map values arg)) args)))
- </pre>
-
- <p>Second, a macro can return multiple values; these are evaluated and spliced,
- exactly like a normal macro,
- so you can use <code>(values '(define a 1) '(define b 2))</code> to
- splice multiple definitions at the macro invocation point.
- If an expansion returns (values), nothing is spliced in. This is
- mostly useful in <a href="#readercond">reader-cond</a> and the #; reader.
- </p>
-
- <pre class="indented">
- > (define-expansion (comment str) (values))
- <em class="gray">comment</em>
- > (+ 1 (comment "one") 2 (comment "two"))
- <em class="gray">3</em>
- </pre>
-
- <p>At the top-level (in the REPL), since there's nothing to splice into, you simply get your values back:
- </p>
-
- <pre class="indented">
- > (values 1 (list 1 2) (+ 3 4 5))
- <em class="gray">(values 1 (1 2) 12)</em>
- </pre>
-
- <p>But this printout is just trying to be informative. There is no multiple-values object
- in s7. You can't <code>(set! x (values 1 2))</code>, for example. The values function
- tells s7 that its arguments should be handled in a special way, and the multiple-value indication goes away
- as soon as the arguments are spliced into some caller's arguments.
- </p>
-
- <p>Internally, s7 uses <code>(apply values ...)</code> to implement unquote splicing (",@") in quasiquote.
- <!--
- Is <code>(apply apply func arglist)</code> the same as <code>(apply func (apply values arglist))</code>,
- or (leaving aside <code>'(()))</code>, <code>(func (apply values (apply values arglist)))</code>?
- -->
- </p>
- </div>
-
-
- <div class="indented">
-
- <p>Since set! does not evaluate its first argument, and
- there is no setter for "values", <code>(set! (values x) ...)</code> is not
- the same as <code>(set! x ...)</code>. <code>(string-set! (values string) ...)</code>
- works because string-set! does evaluate its first argument. <code>((values + 1 2) (values 3 4) 5)</code>
- is 15, as anyone would expect.
- </p>
-
- </div>
-
- <!--
- <details>
- <summary class="indented">more examples</summary>
-
- <pre>
- (define (flatten lst)
- (map values (list (let flatten-1 ((lst lst))
- (cond ((null? lst) (values))
- ((not (pair? lst)) lst)
- (else (values (flatten-1 (car lst))
- (flatten-1 (cdr lst)))))))))
- </pre>
-
- <div class="indented">
- </details>
- -->
-
-
- <details>
- <summary class="indented">more on values</summary>
- <div class="indented">
-
- <p>In some Schemes, values behaves like CL's prog1:
- </p>
-
- <pre class="indented">
- (not s7)> (let ((x 1)) (cond ((values #f (set! x 2) #t) 3) (#t x)))
- <em class="gray">2</em>
- (not s7)> (if (values #f #t) 1 2)
- <em class="gray">2</em>
- </pre>
-
- <p>But in s7 we're trying to implement real multiple values (else why have them at all?).
- There are many ways we could interpret <code>(cond ((values ...))...)</code> and
- <code>(cond ((values...) => func))</code>, but surely
- equivalent uses of "cond" and "if" should give the same result.
- Currently in s7, where a test is in progress, only <code>(values #f)</code> is the same as #f.
- </p>
-
- <pre class="indented">
- > (if (values #f #f) 1 2) ; (values #f #f) is not #f
- <em class="gray">1</em>
- > (cond ((values #f #f) 1) (#t 2))
- <em class="gray">1</em>
- ;;; but if we interpreted this as splicing in the values, we get an inconsistency:
- > (cond (#f #f 1) (#t 2))
- <em class="gray">2</em>
- > (if (values #f) 1 2)
- <em class="gray">2</em>
- > (cond ((values #f) 1) (#t 2))
- <em class="gray">2</em>
- > (if (values) 1 2)
- <em class="gray">1</em>
- > (cond ((values) 1) (#t 2))
- <em class="gray">1</em>
- ;;; this is consistent with (cond (1) (#t 2))
- </pre>
-
- <p>
- So "if" and "cond" agree, but it requires that in one case the "values"
- behavior is slightly weird. <code>(or (values #f #f))</code> is #f, but that isn't inconsistent because
- "or" is not testing anything.
- We might choose to say that <code>(if (values #f #f)...)</code>
- is an error, but that would be hasty —
- our troubles have only begun. First, "cond" can omit the expressions that follow the test, unlike "if":
- </p>
-
- <pre class="indented">
- > (cond (3))
- <em class="gray">3</em>
- </pre>
-
- <p>and even trickier, "cond" can pass the test value to a function:
- </p>
-
- <pre class="indented">
- > (cond (3 => +))
- <em class="gray">3</em>
- </pre>
-
- <p>The various standards agree that in the "=>" case, the "fed to" function
- receives one argument, so
- </p>
-
- <pre class="indented">
- (not s7)> (cond ((values 1 2) => +))
- <em class="gray">1</em>
- </pre>
-
- <p>If we were following the "splice immediately" model, this would be <code>(cond (1 2 => +))</code>
- which is an error in some Schemes.
- So something has to give. My druthers is to make "values" work as consistently as possible, and hope
- that the one odd corner will not trip anyone. From that point of view, the "one arg" standard
- looks like a wasted opportunity.
- s7 handles it this way:
- </p>
-
- <pre class="indented">
- > (+ 1 (cond ((values 2 3))) 4) ; trailing values are not ignored
- <em class="gray">10</em>
- > (cond ((values 1 2 3) => +))
- <em class="gray">6</em>
- </pre>
-
- <p>Of course, it is unproblematic that the expression can itself involve multiple values:
- </p>
-
- <pre class="indented">
- > (+ (cond (#t (values 1 2))))
- <em class="gray">3</em>
- </pre>
-
- <p>Now, what have I missed?
- </p>
- </div>
- </details>
-
- </blockquote>
-
- <!-- one place where multiple values might cause surprise:
- :(define* (a1 (a2 (values 1 2))) a2)
- :(a1)
- (values 1 2)
- :(define (a1 a2) a2)
- :(a1 (values 1 2))
- ;(values 1 2): too many arguments: (values 1 2)
- -->
-
-
-
-
-
- <div class="header" id="callwithexit1"><h4>call-with-exit, with-baffle and continuation?</h4></div>
-
-
- <p><b><em class=def id="callwithexit">call-with-exit</em></b> is call/cc without the ability to jump back into the original context,
- similar to "return" in C. This
- is cleaner than call/cc, and much faster.
- </p>
-
- <pre class="indented">
- (define-macro (block . body)
- ;; borrowed loosely from CL — predefine "return" as an escape
- `(<em class=red>call-with-exit</em> (lambda (return) ,@body)))
-
- (define-macro (while test . body) ; while loop with predefined break and continue
- `(<em class=red>call-with-exit</em>
- (lambda (break)
- (let continue ()
- (if (let () ,test)
- (begin
- (let () ,@body)
- (continue))
- (break))))))
-
- (define-macro (switch selector . clauses) ; C-style case (branches fall through unless break called)
- `(<em class=red>call-with-exit</em>
- (lambda (break)
- (case ,selector
- ,@(do ((clause clauses (cdr clause))
- (new-clauses ()))
- ((null? clause) (reverse new-clauses))
- (set! new-clauses (cons `(,(caar clause)
- ,@(cdar clause)
- ,@(map (lambda (nc)
- (apply values (cdr nc))) ; doubly spliced!
- (if (pair? clause) (cdr clause) ())))
- new-clauses)))))))
-
- (define (and-for-each func . args)
- ;; apply func to the first member of each arg, stopping if it returns #f
- (<em class=red>call-with-exit</em>
- (lambda (quit)
- (apply for-each (lambda arglist
- (if (not (apply func arglist))
- (quit #<unspecified>)))
- args))))
-
- (define (find-if f . args) ; generic position-if is very similar
- (<em class=red>call-with-exit</em>
- (lambda (return)
- (apply for-each (lambda main-args
- (if (apply f main-args)
- (apply return main-args)))
- args))))
-
- > (find-if even? #(1 3 5 2))
- <em class="gray">2</em>
- > (* (find-if > #(1 3 5 2) '(2 2 2 3)))
- <em class="gray">6</em>
- </pre>
-
- <p>
- The call-with-exit function's argument (the "continuation") is only valid
- within the call-with-exit function. In call/cc, you can save it, then call it later
- to jump back, but if you try that with call-with-exit (from outside the call-with-exit function's body), you'll get an error.
- This is similar to trying to read from a closed input port.
- </p>
-
-
- <p>The other side, so to speak, of call-with-exit, is <em class=def id="withbaffle">with-baffle</em>.
- Both limit the scope of a continuation.
- Sometimes we need a normal call/cc, but want to make sure it is active
- only within a given block of code.
- Normally, if a continuation gets away, there's no telling when it might wreak havoc on us.
- Scheme code with call/cc becomes unpredictable, undebuggable, and completely
- unmaintainable.
- with-baffle blocks all that — no continuation can jump into its body:
- </p>
-
- <pre class="indented">
- (let ((what's-for-breakfast ())
- (bad-dog 'fido)) ; bad-dog wonders what's for breakfast?
- (<em class=red>with-baffle</em> ; the syntax is (with-baffle . body)
- (set! what's-for-breakfast
- (call/cc
- (lambda (biscuit?)
- (set! bad-dog biscuit?) ; bad-dog smells a biscuit!
- 'biscuit!))))
- (if (eq? what's-for-breakfast 'biscuit!)
- (bad-dog 'biscuit!)) ; now, outside the baffled block, bad-dog wants that biscuit!
- what's-for-breakfast) ; but s7 says "No!": baffled! ("continuation can't jump into with-baffle")
- </pre>
-
- <br>
- <p><em class=def id="continuationp">continuation?</em> returns #t if its argument is a continuation,
- as opposed to a normal procedure. I don't know why Scheme hasn't had this function from
- the very beginning, but it's needed if you want to write a continuable error
- handler. Here is a sketch of the situation:
- </p>
-
- <pre class="indented">
- (let ()
- (catch #t
- (lambda ()
- (let ((res (call/cc
- (lambda (ok)
- (error 'cerror "an error" ok)))))
- (display res) (newline)))
- (lambda args
- (if (and (eq? (car args) 'cerror)
- (<em class=red>continuation?</em> (cadadr args)))
- (begin
- (display "continuing...")
- ((cadadr args) 2)))
- (display "oops"))))
-
- continuing...2
- </pre>
-
- <p>In a more general case, the error handler is separate from the
- catch body, and needs a way to distinguish a real continuation
- from a simple procedure.
- </p>
-
- <pre class="indented">
- (define (continuable-error . args)
- (call/cc
- (lambda (continue)
- (apply error args))))
-
- (define (continue-from-error)
- (if (<em class=red>continuation?</em> ((owlet) 'continue)) ; might be #<undefined> or a function as in the while macro
- (((owlet) 'continue))
- 'bummer))
- </pre>
-
- <!--
- (define-macro (call-with-exit func)
- (let ((tag (caadr func)))
- `(catch ',tag
- (lambda ()
- (define-macro (,tag . body)
- `(throw ',',tag ,@body))
- ,@(cddr func))
- (lambda (type info)
- (car info)))))
- -->
-
-
-
-
- <div class="header" id="format1"><h4>format, object->string</h4></div>
-
- <p>object->string returns the string representation of its argument. Its optional second argument
- can be #f (use display), #t (the default, use write), or :readable. In the latter case, object->string
- tries to produce a string that can be evaluated via eval-string to return an object equal to the
- original.
- </p>
-
- <pre class="indented">
- > (object->string "hiho")
- <em class="gray">"\"hiho\""</em>
- > (format #f "~S" "hiho")
- <em class="gray">"\"hiho\""</em>
- </pre>
-
- <br>
- <p>s7's <em class=def id="format">format</em> function is very close to that in srfi-48.
- </p>
-
- <pre class="indented">
- > (format #f "~A ~D ~F" 'hi 123 3.14)
- <em class="gray">"hi 123 3.140000"</em>
- </pre>
-
-
- <p>The format directives (tilde chars) are:</p>
- <pre class="indented">
- ~% insert newline
- ~& insert newline if preceding char was not newline
- ~~ insert tilde
- ~\n (tilde followed by newline): trim white space
- ~{ begin iteration (take arguments from a list, string, vector, or any other applicable object)
- ~} end iteration
- ~^ ~| jump out of iteration
- ~* ignore the current argument
- ~C print character (numeric argument = how many times to print it)
- ~P insert 's' if current argument is not 1 or 1.0 (use ~@P for "ies" or "y")
- ~A object->string as in display
- ~S object->string as in write
- ~B number->string in base 2
- ~O number->string in base 8
- ~D number->string in base 10
- ~X number->string in base 16
- ~E float to string, (format #f "~E" 100.1) -> "1.001000e+02", (%e in C)
- ~F float to string, (format #f "~F" 100.1) -> "100.100000", (%f in C)
- ~G float to string, (format #f "~G" 100.1) -> "100.1", (%g in C)
- ~T insert spaces (padding)
- ~N get numeric argument from argument list (similar to ~V in CL)
- ~W object->string with :readable (write readably; s7 is the intended reader)
- </pre>
-
- <p>The eight directives before ~W take the usual numeric arguments to specify field width and precision.
- These can also be ~N or ~n in which case the numeric argument is read from the list of arguments:
- </p>
- <pre class="indented">
- (format #f "~ND" 20 1234) ; => (format "~20D" 1234)
- <em class="gray">" 1234"</em>
- </pre>
-
- <p>
- <code>(format #f ...)</code> simply returns the formatted string; <code>(format #t ...)</code>
- also sends the string to the current-output-port. <code>(format () ...)</code> sends the output to
- the current-output-port without returning the string (this mimics the other IO routines
- such as display and newline). Other built-in port choices are *stdout* and *stderr*.
- </p>
-
-
- <blockquote>
-
- <div class="indented">
-
- <p>Floats can occur in any base, so:
- </p>
-
- <pre class="indented">
- > #xf.c
- <em class="gray">15.75</em>
- </pre>
-
- <p>This also affects format. In most Schemes, <code>(format #f "~X" 1.25)</code> is
- an error. In CL, it is equivalent to using ~A which is perverse. But
- </p>
-
- <pre class="indented">
- > (number->string 1.25 16)
- <em class="gray">"1.4"</em>
- </pre>
-
- <p>and there's no obvious way to get the same effect from format unless we accept
- floats in the "~X" case. So in s7,
- </p>
-
- <pre class="indented">
- > (format #f "~X" 21)
- <em class="gray">"15"</em>
- > (format #f "~X" 1.25)
- <em class="gray">"1.4"</em>
- > (format #f "~X" 1.25+i)
- <em class="gray">"1.4+1.0i"</em>
- > (format #f "~X" 21/4)
- <em class="gray">"15/4"</em>
- </pre>
-
- <p>That is, the output choice matches the argument. A case that came up in the Guile mailing lists is:
- <code>(format #f "~F" 1/3)</code>. s7 currently returns "1/3", but Clisp returns "0.33333334".
- </p>
- <br>
- <p>The curly bracket directive applies to anything you can map over, not just lists:
- </p>
-
- <pre class="indented">
- > (format #f "~{~C~^ ~}" "hiho")
- <em class="gray">"h i h o"</em>
- > (format #f "~{~{~C~^ ~}~^...~}" (list "hiho" "test"))
- <em class="gray">"h i h o...t e s t"</em>
- > (with-input-from-string (format #f "(~{~C~^ ~})" (format #f "~B" 1367)) read) ; integer->list
- <em class="gray">(1 0 1 0 1 0 1 0 1 1 1)</em>
- </pre>
- <br>
-
- <p>Since any sequence can be passed to ~{~}, we need a way to truncate output and represent
- the rest of the sequence with "...", but ~^ only stops at the end of the sequence. ~|
- is like ~^ but it also stops after it handles (*s7* 'print-length) elements and prints
- "...". So, <code>(format #f "~{~A~| ~}" #(0 1 2 3 4))</code> returns "0 1 2 ..."
- if (*s7* 'print-length) is 3.
- </p>
-
- </div>
- </blockquote>
-
-
- <blockquote>
-
- <div class="indented">
-
- <p>I added object->string to s7 before deciding to include format. format excites a
- vague disquiet — why do we need this ancient unlispy thing?
- We can almost replace it with:
- </p>
-
- <pre class="indented">
- (define (objects->string . objects)
- (apply string-append (map (lambda (obj) (object->string obj #f)) objects)))
- </pre>
-
- <p>But how to handle lists (~{...~} in format), or columnized output (~T)?
- I wonder whether formatted string output still matters outside a REPL. Even in that context,
- a modern GUI leaves formatting decisions to a text or table widget.
- </p>
-
- <pre class="indented">
- (define-macro (string->objects str . objs)
- `(with-input-from-string ,str
- (lambda ()
- ,@(map (lambda (obj)
- `(set! ,obj (eval (read))))
- objs))))
- </pre>
-
-
- <!--
-
- :(objects->string "int: " 32 ", string: " "hi")
- "int: 32, string: hi"
-
- (define (cycle->string . objs)
- (call-with-exit
- (lambda (return)
- (for-each
- (lambda (obj)
- (if (pair? obj)
- (return
- (string-append
- (apply objects->string
- (map (lambda (obj)
- (if (pair? obj)
- (car obj)
- obj))
- objs))
- (apply cycle->string
- (map (lambda (obj)
- (if (pair? obj)
- (cdr obj)
- obj))
- objs))))))
- objs)
- "")))
-
- ;;; (cycle->string ": " (list 1 2 3) " |")
- :(objects->string "int: " 32 ", list with spaces: (" (cycle->string (list 1 2 3) " ") "), string: " "hi")
- "int: 32, list with spaces: (1 2 3 ), string: hi"
-
- :(let ((x 0) (y 0)) (string->objects "1 2" x y) (list x y))
- (1 2)
-
- -->
-
- </div>
-
- </blockquote>
-
-
-
- <div class="header" id="hooks"><h4>hooks</h4></div>
-
- <pre class="indented">
- (<em class=def id="makehook">make-hook</em> . fields) ; make a new hook
- (<em class=def id="hookfunctions">hook-functions</em> hook) ; the hook's list of 'body' functions
- </pre>
-
- <p>A hook is a function created by make-hook, and called (normally from C) when something interesting happens.
- In GUI toolkits hooks are called callback-lists, in CL conditions,
- in other contexts watchpoints or signals. s7 itself has several
- hooks: <a href="#errorhook">*error-hook*</a>, <a href="#readerrorhook">*read-error-hook*</a>,
- <a href="#unboundvariablehook">*unbound-variable-hook*</a>, *missing-close-paren-hook*,
- and <a href="#loadhook">*load-hook*</a>.
- make-hook is:
- </p>
-
- <pre class="indented">
- (define (make-hook . args)
- (let ((body ()))
- (apply lambda* args
- '(let ((result #<unspecified>))
- (let ((e (curlet)))
- (for-each (lambda (f) (f e)) body)
- result))
- ())))
- </pre>
-
- <p>So the result of calling make-hook is a function (the lambda* that is applied to args above) that
- contains a list of functions, 'body.
- Each function in that list takes one argument, the hook.
- Each time the hook itself is called, each of the body functions is called, and the value of 'result is returned.
- That variable, and each of the hook's arguments are accessible to the hook's internal
- functions by going through the environment passed to the internal functions. This is a bit circuitous;
- here's a sketch:
- </p>
-
- <pre class="indented">
- > (define h (make-hook '(a 32) 'b)) ; h is a function: (lambda* ((a 32) b) ...)
- <em class="gray">h</em>
- > (set! (hook-functions h) ; this sets ((funclet h) 'body)
- (list (lambda (hook) ; each hook internal function takes one argument, the environment
- (set! (hook 'result) ; this is the "result" variable above
- (format #f "a: ~S, b: ~S" (hook 'a) (hook 'b))))))
- <em class="gray">(#<lambda (hook)>)</em>
- > (h 1 2) ; this calls the hook's internal functions (just one in this case)
- <em class="gray">"a: 1, b: 2" ; we set "result" to this string, so it is returned as the hook application result</em>
- > (h)
- <em class="gray">"a: 32, b: #f"</em>
- </pre>
-
- <p>In C, to make a hook:
- </p>
-
- <pre class="indented">
- hook = s7_eval_c_string("(make-hook '(a 32) 'b)");
- s7_gc_protect(s7, hook);
- </pre>
-
- <p>And call it:
- </p>
-
- <pre class="indented">
- result = s7_call(s7, hook, s7_list(s7, 2, s7_make_integer(s7, 1), s7_make_integer(s7, 2)));
- </pre>
-
- <div class="indented">
- <pre>
- (define-macro (hook . body) ; return a new hook with "body" as its body, setting "result"
- `(let ((h (make-hook)))
- (set! (hook-functions h) (list (lambda (h) (set! (h 'result) (begin ,@body)))))
- h))
- </pre>
- </div>
-
-
-
-
-
- <div class="header" id="procedureinfo"><h4>procedure info</h4></div>
-
-
- <pre class="indented">
- (<em class=def id="proceduresource">procedure-source</em> proc)
- (<em class=def id="proceduredocumentation">procedure-documentation</em> proc)
- (<em class=def id="proceduresignature">procedure-signature</em> proc)
- (<em class=def id="proceduresetter">procedure-setter</em> proc)
- (funclet proc)
-
- (<em class=def id="arity">arity</em> obj)
- (<em class=def id="aritablep">aritable?</em> obj num-args)
- </pre>
-
- <p>
- procedure-setter returns or sets the set function associated with a procedure (set-car! with car, for example).
- funclet returns
- a procedure's environment.
- procedure-source returns the procedure source (a list).
- </p>
-
- <pre class="indented">
- (define (procedure-arglist f) (cadr (procedure-source f)))
- </pre>
-
- <p>
- procedure-documentation returns the documentation string associated with a procedure. This used to be
- the initial string in the function's body (as in CL), but now it is the value of the 'documentation variable, if any,
- in the procedure's local environment. That is, <code>(define (f a) "doc string" a)</code> is now
- <code>(define f (let ((documentation "doc string")) (lambda (a) a)))</code>. This change gets the
- string out of the body (where it can slow down evaluation of the function to no purpose), and
- makes it possible to extend the function information arbitrarily.
- </p>
-
- <p>
- arity takes any object and returns either #f if it is not applicable,
- or a cons containing the minimum and maximum number of arguments acceptable. If the maximum reported
- is a really big number, that means any number of arguments is ok.
- aritable? takes two arguments, an object and an integer, and returns #t if the object can be
- applied to that many arguments.
- </p>
-
- <pre class="indented">
- > (define add-2 (let ((documentation "add-2 adds its 2 args")) (lambda* (a (b 32)) (+ a b))))
- <em class="gray">add-2</em>
- > (procedure-documentation add-2)
- <em class="gray">"add-2 adds its 2 args"</em>
- > (procedure-source add-2)
- <em class="gray">(lambda* (a (b 32)) (+ a b))</em>
- > (arity add-2)
- <em class="gray">(0 . 2)</em>
- </pre>
-
- <p>
- procedure-signature is a list describing the argument types and returned value type
- of the function. The first entry in the list is the return type, and the rest are
- argument types. #t means any type is possible, and 'values means the function returns multiple values.
- </p>
-
- <pre class="indented">
- > (procedure-signature round)
- <em class="gray">(integer? real?)</em> ; round takes a real argument, returns an integer
- > (procedure-signature vector-ref)
- <em class="gray">(#t vector? . #1=(integer? . #1#))</em> ; trailing args are all integers (indices)
- </pre>
-
- <p>If an entry is a list, any of the listed types can occur:
- </p>
-
- <pre class="indented">
- > (procedure-signature char-position)
- <em class="gray">((boolean? integer?) (char? string?) string? integer?)</em>
- </pre>
-
- <p>which says that the first argument to char-position can be a string or a character,
- and the return type can be either boolean or an integer.
- If the function is defined in scheme, its signature is the value of the 'signature variable
- in its closure:
- </p>
-
- <pre class="indented">
- > (define f1 (let ((documentation "helpful info")
- (signature '(boolean? real?)))
- (lambda (x)
- (positive? x))))
- <em class="gray">f1</em>
- > (procedure-documentation f1)
- <em class="gray">"helpful info"</em>
- > (procedure-signature f1)
- <em class="gray">(boolean? real?)</em>
- </pre>
-
- <p>We could do the same thing using methods:
- </p>
-
- <pre class="indented">
- > (define f1 (let ((procedure-documentation (lambda (f) "helpful info"))
- (procedure-signature (lambda (f) '(boolean? real?))))
- (<em class=red>openlet</em>
- (lambda (x)
- (positive? x)))))
- > (procedure-documentation f1)
- <em class="gray">"helpful info"</em>
- > (procedure-signature f1)
- <em class="gray">(boolean? real?)</em>
- </pre>
-
- <p>openlet alerts s7 that f1 has methods.
- </p>
- <blockquote>
- <br>
-
- <details>
- <summary class="indented">examples</summary>
- <div class="indented">
-
- <p>procedure-source returns the actual function source. If you
- call the function, the optimizer changes the source to suit itself,
- so if you want to walk over the function body or change it in some way,
- make a clean copy of
- the procedure-source first (using, for example, copy-tree in stuff.scm).
- </p>
-
- <pre>
- (define-bacro (procedure-annotate proc) ; use "bacro" so we can annotate local functions
- (let ((orig (<em class=red>procedure-source</em> proc))) ; this assumes we haven't called "proc" yet
-
- (define (proc-walk source)
- (cond ((not (pair? source))
- source)
-
- ((not (memq (car source) '(let let*))) ; if let or let*, show local variables
- (cons (proc-walk (car source))
- (proc-walk (cdr source))))
-
- ((symbol? (cadr source)) ; named let?
- (append ; (let name vars . body) -> (let name vars print-vars . body)
- (list (car source)
- (cadr source)
- (caddr source)
- `(format () " (let ~A (~{~A~^ ~}) ...)~%" ,(cadr source) (curlet)))
- (cdddr source)))
-
- (else ; (let(*) vars . body) -> (let vars print-vars . body)
- (append
- (list (car source)
- (cadr source)
- `(format () " (~A (~{~A~^ ~}) ...)~%" ,(car source) (curlet)))
- (cddr source)))))
-
- (let* ((new-body (proc-walk orig))
- (result (gensym))
- (new-source
- `(lambda ,(cadr orig)
- (let ((,result #<undefined>))
- (dynamic-wind
- (lambda () ; upon entry, show procedure name and args
- (format () "(~A~{ ~A~})~%"
- ',proc
- (outlet (outlet (curlet)))))
- (lambda ()
- (set! ,result (,new-body ,@(cadr orig))))
- (lambda () ; at exit, show result
- (if (eq? ,result #<undefined>)
- (format () " ~A returns early~%" ',proc)
- (format () " ~A returns ~A~%" ',proc ,result))))))))
-
- `(define ,proc ,new-source))))
-
-
- > (define (hi a) (let ((b 12)) (+ b a)))
- <em class="gray">hi</em>
- > (procedure-annotate hi)
- <em class="gray">#<lambda (a)></em>
- > (let ((x 32)) (+ 1 (hi x)))
- <em class="gray">45</em>
- ;; printing:
- (hi (a . 32))
- (let ((b . 12)) ...)
- hi returns 44
- </pre>
-
- <p>But maybe something less invasive is better. Here's a version of let that prints
- its bindings (this is borrowed from "nuntius" at reddit lisp):
- </p>
-
- <pre>
- (define-macro (print-let bindings . body)
- (let ((temp-symbol (gensym)))
- `(let ,(map (lambda (var/val)
- `(,(car var/val)
- (let ((,temp-symbol ,(cadr var/val)))
- (format () ";~S: ~S -> ~S~%"
- ',(car var/val)
- ',(cadr var/val)
- ,temp-symbol)
- ,temp-symbol)))
- bindings)
- ,@body)))
-
- ;; or simpler:
-
- (define-macro (print-let bindings . body)
- `(let ,bindings
- (format () "~{;~A~%~}" (curlet))
- ,@body))
- </pre>
-
- </div>
-
-
- <div class="indented">
-
- <p>A minor footnote: there are cases in s7 where aritable? can't tell whether
- a given number of arguments can be applied to an object. For example,
- <code>((list 1 (list 2 3)) 0 1)</code> is an error, but
- <code>((list 1 (list 2 3)) 1 1)</code> is 3. So
- <code>(aritable? (list 1 (list 2 3)) 2)</code> depdends on
- what actual arguments you pass. In these cases, aritable? returns <code>#f</code>.
- </p>
-
- </div>
- </details>
-
- <div class="indented">
-
- <pre>
- (define (for-each-subset func args)
- ;; form each subset of args, apply func to the subsets that fit its arity
- (let subset ((source args)
- (dest ())
- (len 0))
- (if (null? source)
- (if (<em class=red>aritable?</em> func len) ; does this subset fit?
- (apply func dest))
- (begin
- (subset (cdr source) (cons (car source) dest) (+ len 1))
- (subset (cdr source) dest len)))))
- </pre>
- </div>
- </blockquote>
-
-
- <div class="header" id="evalstring"><h4>eval</h4></div>
-
- <p>
- <b>eval</b> evaluates its argument, a list representing a piece of code. It takes an optional
- second argument, the environment in which the evaluation should take place. <b>eval-string</b>
- is similar, but its argument is a string.
- </p>
-
- <pre class="indented">
- > (eval '(+ 1 2))
- <em class="gray">3</em>
- > (eval-string "(+ 1 2)")
- <em class="gray">3</em>
- </pre>
-
- <p>Leaving aside a few special cases, eval-string could be defined:
- </p>
- <pre class="indented">
- (define-macro* (eval-string x e)
- `(eval (with-input-from-string ,x read) (or ,e (curlet))))
- </pre>
-
-
- <blockquote>
-
-
- <div class="indented">
-
- <p>eval's environment argument is handy when implementing break and trace:
- </p>
- <pre>
- (define *breaklet* #f)
- (define *step-hook* (make-hook 'code 'e))
-
- (define-macro* (trace/break code . break-points)
- (define (caller tree)
- (if (pair? tree)
- (cons
- (if (pair? (car tree))
- (if (and (symbol? (caar tree))
- (procedure? (symbol->value (caar tree))))
- (if (member (car tree) break-points)
- `(__break__ ,(caller (car tree)))
- `(__call__ ,(caller (car tree))))
- (caller (car tree)))
- (car tree))
- (caller (cdr tree)))
- tree))
- `(call-with-exit (lambda (__top__) ,(caller code))))
-
- (define (go . args)
- (and (let? *breaklet*)
- (apply (*breaklet* 'go) args)))
-
- (define (clear-break)
- (set! *breaklet* #f))
-
- (define-macro (__call__ code)
- `(*step-hook* ',code (curlet)))
-
- (define-macro (__break__ code)
- `(begin
- (call/cc
- (lambda (go)
- (set! *breaklet* (curlet))
- (__top__ (format #f "break at: ~A~%" ',code))))
- ,code))
-
- (set! (hook-functions *step-hook*)
- (list (lambda (hook)
- (set! (hook 'result) (<em class=red>eval (hook 'code) (hook 'e)</em>)))))
-
- (set! ((funclet *step-hook*) 'end)
- (list (lambda (hook)
- (define (uncaller tree)
- (if (pair? tree)
- (cons
- (if (and (pair? (car tree))
- (memq (caar tree) '(__call__ __break__)))
- (uncaller (cadar tree))
- (uncaller (car tree)))
- (uncaller (cdr tree)))
- tree))
- (format (current-output-port) ": ~A -> ~A~40T~A~%"
- (uncaller (hook 'code))
- (hook 'result)
- (if (and (not (eq? (hook 'e) (rootlet)))
- (not (defined? '__top__ (hook 'e))))
- (map values (hook 'e))
- "")))))
-
- ;;; (trace/break (let ((a (+ 3 1)) (b 2)) (if (> (* 2 a) b) 2 3)))
- ;;; (trace/break (let ((a (+ 3 1)) (b 2)) (if (> (* 2 a) b) 2 3)) (* 2 a))
- </pre>
- </div>
-
- </blockquote>
-
-
-
-
-
- <div class="header" id="IO"><h4>IO and other OS functions</h4></div>
-
-
-
- <p>Besides files, ports can also represent strings and functions. The string port functions
- are:
- </p>
-
- <pre class="indented">
- (with-output-to-string thunk) ; open a string port as current-output-port, call thunk, return string
- (with-input-from-string string thunk) ; open string as current-input-port, call thunk
- (call-with-output-string proc) ; open a string port, apply proc to it, return string
- (call-with-input-string string proc) ; open string as current-input-port, apply proc to it
- (open-output-string) ; open a string output port
- (get-output-string port clear) ; return output accumulated in the string output port
- (open-input-string string) ; open a string input port reading string
- </pre>
-
- <pre class="indented">
- > (let ((result #f)
- (p (<em class=red>open-output-string</em>)))
- (format p "this ~A ~C test ~D" "is" #\a 3)
- (set! result (<em class=red>get-output-string</em> p))
- (close-output-port p)
- result)
- <em class="gray">"this is a test 3"</em>
- </pre>
-
- <p>In get-output-string, if the optional 'clear' argument is #t, the port is cleared (the default in r7rs I think).
- Other functions:
- </p>
-
- <ul>
- <li>read-byte and write-byte: binary IO.
- <li>read-line: line-at-a-time reads, optional third argument #t to include the newline.
- <li>current-error-port, set-current-error-port
- </ul>
-
- <p>The variable (*s7* 'print-length) sets
- the upper limit on how many elements of a sequence are printed by object->string and format.
- The end-of-file object is #<eof>.
- When running s7 behind a GUI, you often want input to come from and output to go to
- arbitrary widgets. The function ports provide a way to redirect IO in C. See <a href="#functionportexample">below</a>
- for an example.
- </p>
-
-
- <blockquote>
-
- <div class="indented">
-
- <p>
- The default IO ports are *stdin*, *stdout*, and *stderr*.
- *stderr* is useful if you want to make sure output is flushed out immediately.
- The default output port is *stdout* which buffers output until a newline is seen.
- In most REPLs, the input port is set up by the REPL, so you need to use
- *stdin* if you want to read from the terminal instead:
- </p>
-
- <pre class="indented">
- > (read-char)
- <em class="gray">#<eof></em>
- > (read-char *stdin*)
- a ; here s7 waited for me to type "a" in the terminal
- <em class="gray">#\a</em> ; this is the REPL reporting what read-char returned
- </pre>
-
- </div>
-
-
- <div class="indented">
- <p>An environment can be treated as an IO port, providing what Guile calls a "soft port":
- </p>
-
- <pre class="indented">
- (define (call-with-input-vector v proc)
- (let ((i -1))
- (proc (openlet (inlet 'read (lambda (p) (v (set! i (+ i 1)))))))))
- </pre>
-
- <p>Here the IO port is an open environment that redefines the "read" function so that it
- returns the next element of a vector. See stuff.scm for call-with-output-vector.
- The "proc" argument above can also be a macro, giving you a kludgey way to get around
- the dumb "lambda". Here are more useful examples:
- </p>
- <pre class="indented">
- (openlet ; a soft port for format that sends its output to *stderr* and returns the string
- (inlet 'format (lambda (port str . args)
- (display (apply format #f str args) *stderr*))))
-
- (define (open-output-log name)
- ;; return a soft output port that does not hold its output file open
- (define (logit name str)
- (let ((p (open-output-file name "a")))
- (display str p)
- (close-output-port p)))
- (openlet
- (inlet :name name
- :format (lambda (p str . args) (logit (p 'name) (apply format #f str args)))
- :write (lambda (obj p) (logit (p 'name) (object->string obj #t)))
- :display (lambda (obj p) (logit (p 'name) (object->string obj #f)))
- :write-string (lambda (str p) (logit (p 'name) str))
- :write-char (lambda (ch p) (logit (p 'name) (string ch)))
- :newline (lambda (p) (logit (p 'name) (string #\newline)))
- :close-output-port (lambda (p) #f)
- :flush-output-port (lambda (p) #f))))
- </pre>
- </div>
-
-
- <div class="indented">
-
- <p>binary-io.scm in the Snd package has functions that read and write integers and floats in
- both endian choices in a variety of sizes.
- </p>
-
- </div>
- </blockquote>
-
- <p>If the compile time switch WITH_SYSTEM_EXTRAS is 1, several additional OS-related and
- file-related functions are built-in. This is work in progress; currently this switch
- adds:
- </p>
-
- <pre class="indented">
- (directory? str) ; return #t if str is the name of a directory
- (file-exists? str) ; return #t if str names an existing file
- (delete-file str) ; try to delete the file, return 0 is successful, else -1
- (getenv var) ; return the value of an environment variable: (getenv "HOME")
- (directory->list dir) ; return contents of directory as a list of strings (if HAVE_DIRENT_H)
- (system command) ; execute command
- </pre>
-
- <p>But maybe this is not needed; see <a href="#cload">cload.scm</a> below for
- a more direct approach.
- </p>
-
-
-
-
-
- <div class="header" id="errors"><h4>error handling</h4></div>
-
- <pre class="indented">
- (error tag . info) ; signal an error of type tag with addition information
- (catch tag body err) ; if error of type tag signalled in body (a thunk), call err with tag and info
- (throw tag . info) ; jump to corresponding catch
- </pre>
-
- <p>s7's error handling mimics that of Guile. An error is signalled
- via the error function, and can be trapped and dealt with via <em class=def id="catch">catch</em>.
- </p>
-
- <pre class="indented">
- > (<em class=red>catch</em> 'wrong-number-of-args
- (lambda () ; code protected by the catch
- (abs 1 2))
- (lambda args ; the error handler
- (apply format #t (cadr args))))
- <em class="gray">"abs: too many arguments: (1 2)"</em>
- > (<em class=red>catch</em> 'division-by-zero
- (lambda () (/ 1.0 0.0))
- (lambda args (string->number "inf.0")))
- <em class="gray">inf.0</em>
-
- (define-macro (catch-all . body)
- `(<em class=red>catch</em> #t (lambda () ,@body) (lambda args args)))
- </pre>
-
- <p>
- catch has 3 arguments: a tag indicating what error to catch (#t = anything),
- the code, a thunk, that the catch is protecting, and the function to call
- if a matching error occurs during the evaluation of the thunk. The error handler
- takes a rest argument which will hold whatever the error function chooses to pass it.
- The error function itself takes at least 2 arguments, the error type, a symbol,
- and the error message. There may also be other arguments describing the error.
- The default action, in the absence of any catch, is to treat the message as
- a format control string, apply format to it and the other arguments, and
- send that info to the current-error-port.
- </p>
-
-
- <blockquote>
- <div class="indented">
- <p>Normally when reading a file, we have to check for #<eof>, but we can let s7
- do that:
- </p>
-
- <pre>
- (define (copy-file infile outfile)
- (call-with-input-file infile
- (lambda (in)
- (call-with-output-file outfile
- (lambda (out)
- (<em class=red>catch</em> 'wrong-type-arg ; s7 raises this error if write-char gets #<eof>
- (lambda ()
- (do () () ; read/write until #<eof>
- (write-char (read-char in) out)))
- (lambda err
- outfile)))))))
- </pre>
-
- <p>catch is not limited to error handling:
- </p>
-
- <pre class="indented">
- (define (map-with-exit func . args)
- ;; map, but if early exit taken, return the accumulated partial result
- ;; func takes escape thunk, then args
- (let* ((result ())
- (escape-tag (gensym))
- (escape (lambda () (throw escape-tag))))
- (<em class=red>catch</em> escape-tag
- (lambda ()
- (let ((len (apply max (map length args))))
- (do ((ctr 0 (+ ctr 1)))
- ((= ctr len) (reverse result)) ; return the full result if no throw
- (let ((val (apply func escape (map (lambda (x) (x ctr)) args))))
- (set! result (cons val result))))))
- (lambda args
- (reverse result))))) ; if we catch escape-tag, return the partial result
-
- (define (truncate-if func lst)
- (map-with-exit (lambda (escape x) (if (func x) (escape) x)) lst))
-
- > (truncate-if even? #(1 3 5 -1 4 6 7 8))
- <em class="gray">(1 3 5 -1)</em>
- </pre>
-
- <p>But this is less useful than map (it can't map over a hash-table for example),
- and is mostly reimplementing built-in code. Perhaps s7 should have an extension
- of map (and more usefully, for-each) that is patterned after dynamic-wind:
- <code>(dynamic-for-each init-func main-func end-func . args)</code> where init-func
- is called with one argument, the length of the shortest sequence argument (for-each
- and map know this in advance); main-func takes n arguments where n matches
- the number of sequences passed; and end-func is called even if a jump out of main-func
- occurs (like dynamic-wind in this regard). In the dynamic-map case, the end-func
- takes one argument, the current, possibly partial, result list. dynamic-for-each
- then could easily (but maybe not efficiently) implement generic functions such as ->list, ->vector, and
- ->string (converting any sequence into a sequence of some other type).
- map-with-exit would be
- </p>
- <pre class="indented">
- (define (map-with-exit func . args)
- (let ((result ()))
- (call-with-exit
- (lambda (quit)
- (apply dynamic-map #f ; no init-func in this case
- (lambda main-args
- (apply func quit main-args))
- (lambda (res)
- (set! result res))
- args)))
- result))
- </pre>
- </div>
-
- <div class="indented">
- <p>With all the lambda boilerplate, nested catches are hard to read:
- </p>
- <pre class="indented">
- (catch #t
- (lambda ()
- (catch 'division-by-zero
- (lambda ()
- (catch 'wrong-type-arg
- (lambda ()
- (abs -1))
- (lambda args (format () "got a bad arg~%") -1)))
- (lambda args 0)))
- (lambda args 123))
- </pre>
-
- <p>Perhaps we need a macro:
- </p>
-
- <pre class="indented">
- (define-macro (catch-case clauses . body)
- (let ((base `(lambda () ,@body)))
- (for-each (lambda (clause)
- (let ((tag (car clause)))
- (set! base `(lambda ()
- (catch ',(or (eq? tag 'else) tag)
- ,base
- ,@(cdr clause))))))
- clauses)
- (caddr base)))
-
- ;;; the code above becomes:
- (catch-case ((wrong-type-arg (lambda args (format () "got a bad arg~%") -1))
- (division-by-zero (lambda args 0))
- (else (lambda args 123)))
- (abs -1))
- </pre>
-
- <p>This is similar to r7rs scheme's "guard", but I don't want a pointless thunk for the body of the catch.
- Along the same lines:
- </p>
- <pre class="indented">
- (define (catch-if test func err)
- (catch #t
- func
- (lambda args
- (apply (if (test (car args)) err throw) args)))) ; if not caught, re-raise the error via throw
-
- (define (catch-member lst func err)
- (catch-if (lambda (tag) (member tag lst)) func err))
-
- (define-macro (catch* clauses . error)
- ;; try each clause until one evaluates without error, else error:
- ;; (macroexpand (catch* ((+ 1 2) (- 3 4)) 'error))
- ;; (catch #t (lambda () (+ 1 2)) (lambda args (catch #t (lambda () (- 3 4)) (lambda args 'error))))
- (define (builder lst)
- (if (null? lst)
- (apply values error)
- `(catch #t (lambda () ,(car lst)) (lambda args ,(builder (cdr lst))))))
- (builder clauses))
- </pre>
- </div>
-
- <!--
- (define (or-catch . funks)
- (call-with-exit
- (lambda (return)
- (for-each
- (lambda (f)
- (catch #t
- (lambda ()
- (return (f)))
- (lambda args
- (case (car args)
- ((wrong-type-arg) ...)
- (...)
- (else (apply throw args))))))
- funks))))
- -->
-
- </blockquote>
-
- <p>When an error is encountered, and when s7 is interrupted via <a href="#beginhook">begin_hook</a>,
- (<em class=def id="owlet">owlet</em>) returns an environment that contains
- additional info about that error:
- </p>
-
- <ul>
- <li>error-type: the error type or tag, e.g. 'division-by-zero
- <li>error-data: the message or information passed by the error function
- <li>error-code: the code that s7 thinks triggered the error
- <li>error-line: the line number of that code
- <li>error-file: the file name of that code
- <li>error-history: previous evaluations leading to the error (a circular list)
- </ul>
-
- <p>The error-history field depends on the compiler flag WITH_HISTORY. See ow! in
- stuff.scm for one way to display this data. The *s7* field 'history-size sets the size of the buffer.
- </p>
-
- <blockquote>
-
-
- <div class="indented">
-
- <p>To find a variable's value at the point of the error: <code>((owlet) var)</code>.
- To list all the local bindings from the error outward:
- </p>
-
- <pre class="indented">
- (do ((e (outlet (owlet)) (outlet e)))
- ((eq? e (rootlet)))
- (format () "~{~A ~}~%" e))
- </pre>
-
- <p>To see the current s7 stack, <code>(stacktrace)</code>. You'll probably
- want to use this in conjunction with *error-hook*.
- To evaluate the error handler in the environment of the error:
- </p>
-
- <pre class="indented">
- (let ((x 1))
- (catch #t
- (lambda ()
- (let ((y 2))
- (error 'oops)))
- (lambda args
- (with-let (sublet (owlet) :args args) ; add the error handler args
- (list args x y))))) ; we have access to 'y'
- </pre>
-
- <p>To limit the maximum size of the stack, set (*s7* 'max-stack-size).
- </p>
- </div>
-
-
- </blockquote>
-
-
- <p>The hook <em class=def id="errorhook">*error-hook*</em> provides a way to specialize error reporting.
- Its arguments are named 'type and 'data.
- </p>
-
- <pre class="indented">
- (set! (hook-functions *error-hook*)
- (list (lambda (hook)
- (apply format *stderr* (hook 'data))
- (newline *stderr*))))
- </pre>
-
- <p><em class=def id="readerrorhook">*read-error-hook*</em> provides two hooks into the reader.
- A major problem when reading code written for other Schemes is that each Scheme provides
- a plethora of idiosyncratic #-names (even special character names), and \ escapes in string
- constants. *read-error-hook* provides a way to handle these weird cases. If a #-name
- is encountered that s7 can't deal with, *read-error-hook* is called with two arguments,
- #t and the string representing the constant. If you set (hook 'result), that result is
- returned to the reader. Otherwise a 'read-error is raised and you drop into the error handler.
- Similarly, if some bizaare \ use occurs, *read-error-hook* is called with two arguments,
- #f and the offending character. If you return a character, it is passed to the reader;
- otherwise you get an error. lint.scm has an example.
- </p>
-
- <p>
- There is a break macro defined in Snd (snd-xen.c)
- which allows you to stop at some point, then evaluate arbitrary expressions in that context.
- </p>
-
-
- <div class="indented">
- <p>The s7-built-in catch tags are 'wrong-type-arg, 'syntax-error, 'read-error,
- 'out-of-memory, 'wrong-number-of-args, 'format-error, 'out-of-range, 'division-by-zero, 'io-error, and 'bignum-error.
- </p>
- </div>
-
-
-
- <div class="header" id="autoload"><h4>autoload</h4></div>
-
- <!-- INDEX autoload:autoload -->
- <p>
- If s7 encounters an unbound variable, it first looks to see if it has any autoload information about it.
- This info can be declared via <em class=def>autoload</em>, a function of two arguments, the
- symbol that triggers the autoload, and either a filename or a function. If a filename, s7
- loads that file; if a function, it is called with one argument, the current (calling) environment.
- </p>
-
- <pre class="indented">
- (autoload 'channel-distance "dsp.scm")
- ;; now if we subsequently call channel-distance but forget to load "dsp.scm" first,
- ;; s7 loads "dsp.scm" itself, and uses its definition of channel-distance.
- ;; The C-side equivalent is s7_autoload.
-
- ;; here is the cload.scm case, loading j0 from the math library if it is called:
- (autoload 'j0
- (lambda (e)
- (unless (provided? 'cload.scm)
- (load "cload.scm"))
- (c-define '(double j0 (double)) "" "math.h")
- (varlet e 'j0 j0)))
- </pre>
-
- <p>The entity (hash-table or environment probably) that holds the autoload info is named *autoload*.
- If after checking autoload, the symbol is still unbound, s7 calls
- <em class=def id="unboundvariablehook">*unbound-variable-hook*</em>.
- The offending symbol is named 'variable in the hook environment.
- If after running *unbound-variable-hook*, the symbol is still unbound,
- s7 calls the error handler.
- </p>
-
- <p>The autoloader knows about s7 environments used as libraries, so, for example,
- you can <code>(autoload 'j0 "libm.scm")</code>, then use j0 in scheme code. The first
- time s7 encounters j0, j0 is undefined, so
- s7 loads libm.scm. That load returns the C math library as the environment *libm*.
- s7 then automatically looks for j0 in *libm*, and defines it for you.
- So the result is the same as if you had defined j0 yourself in C code.
- You can use the r7rs library mechanism here, or with-let, or
- whatever you want! (In Snd, libc, libm, libdl, and libgdbm are automatically
- tied into s7 via autoload, so if you call, for example, frexp, libm.scm
- is loaded, and frexp is exported from the *libm* environment, then the
- evaluator soldiers on, as if frexp had always been defined in s7).
- You can also import all of (say) gsl into the current environment
- via <code>(varlet (curlet) *libgsl*)</code>.
- </p>
-
-
- <div class="header" id="constants"><h4>define-constant, constant?, symbol-access</h4></div>
-
- <p><em class=def id="defineconstant">define-constant</em> defines a constant and
- <em class=def id="constantp">constant?</em> returns #t if its argument
- is a constant. A constant in s7 is really constant: it can't be set or rebound.
- </p>
-
- <pre class="indented">
- > (define-constant var 32)
- <em class="gray">var</em>
- > (set! var 1)
- <em class="gray">;set!: can't alter immutable object: var</em>
- > (let ((var 1)) var)
- <em class="gray">;can't bind or set an immutable object: var, line 1</em>
- </pre>
-
- <p>This has the possibly surprising side effect that previous uses of the constant name
- become constants:
- </p>
-
- <pre class="indented">
- (define (func a) (let ((cvar (+ a 1))) cvar))
- (define-constant cvar 23)
- (func 1)
- ;can't bind or set an immutable object: cvar
- </pre>
-
- <p>So, obviously, choose unique names for your constants, or don't use define-constant.
- A function can also be a constant. In some cases, the optimizer can take advantage
- of this information to speed up function calls.
- </p>
-
- <p>Constants are very similar to things such as keywords (no set, always return itself as its value),
- variable trace (informative function upon set or keeping a history of past values), typed variables (restricting a
- variable's values or doing automatic conversions upon set), and notification upon set (either in Scheme
- or in C; I wanted this many years ago in Snd). The notification function is especially useful if
- you have a Scheme variable and want to reflect any change in its value immediately in C (see <a href="#notify">below</a>).
- In s7, <em class=def id="symbolaccess">symbol-access</em> sets this function.
- </p>
-
- <p>Each environment is a set of symbols and their associated values. symbol-access places a function (or macro) between a symbol
- and its value in a given environment. The accessor function takes two arguments, the symbol and the new value, and
- returns the value that is actually set. For example, the function can ensure that a variable is always an integer:
- </p>
-
- <pre class="indented">
- (define e ; save environment for use below
- (let ((x 3) ; will always be an integer
- (y 3) ; will always keep its initial value
- (z 3)) ; will report set!
-
- (set! (symbol-access 'x) (lambda (s v) (if (integer? v) v x)))
- (set! (symbol-access 'y) (lambda (s v) y))
- (set! (symbol-access 'z) (lambda (s v) (format *stderr* "z ~A -> ~A~%" z v) v))
-
- (set! x 3.3) ; x does not change because 3.3 is not an integer
- (set! y 3.3) ; y does not change
- (set! z 3.3) ; prints "z 3 -> 3.3"
- (curlet)))
-
- > e
- <em class="gray">(inlet 'x 3 'y 3 'z 3.3)</em>
- >(begin (set! (e 'x) 123) (set! (e 'y) #()) (set! (e 'z) #f))
- ;; prints "z 3.3 -> #f"
- > e
- <em class="gray">(inlet 'x 123 'y 3 'z #f)</em>
- > (define-macro (reflective-let vars . body)
- `(let ,vars
- ,@(map (lambda (vr)
- `(set! (symbol-access ',(car vr))
- (lambda (s v)
- (format *stderr* "~S -> ~S~%" s v)
- v)))
- vars)
- ,@body))
- <em class="gray">reflective-let</em>
- > (reflective-let ((a 1)) (set! a 2))
- <em class="gray">2</em> ; prints "a -> 2"
- >(let ((a 0))
- (set! (symbol-access 'a)
- (let ((history (make-vector 3 0))
- (position 0))
- (lambda (s v)
- (set! (history position) v)
- (set! position (+ position 1))
- (if (= position 3) (set! position 0))
- v)))
- (set! a 1)
- (set! a 2)
- ((funclet (symbol-access 'a)) 'history))
- <em class="gray">#(1 2 0)</em>
- </pre>
-
- <p>Or implement reactive programming:
- </p>
- <pre class="indented">
- (let ((a 1)
- (b 2)
- (c 3))
- (set! (symbol-access 'b) (lambda (s v) (set! a (+ v c)) v))
- (set! (symbol-access 'c) (lambda (s v) (set! a (+ b v)) v))
- (set! a (+ b c)) ; a will be updated if b or c is set
- (set! b 4)
- (set! c 5)
- a) ; a is 9 = (+ 4 5)
- </pre>
-
-
- <details>
- <summary class="indented">reactive-let</summary>
- <div class="indented">
-
- <p>stuff.scm has reactive-set!, reactive-vector, reactive-let, reactive-let*, and reactive-lambda*:
- </p>
- <pre class="indented">
- > (let ((-a- 1)
- (b 2))
- (<em class=red>reactive-set!</em> -a- (* b 2))
- (set! b 3)
- -a-)
- <em class="gray">6</em>
- > (let ((a 1))
- (let ((v (<em class=red>reactive-vector</em> a (+ a 1) 2)))
- (set! a 4)
- v))
- <em class="gray">#(4 5 2)</em>
- > (let ((a 1))
- (<em class=red>reactive-let</em> ((-b- (+ a 1))) ; if 'a changes, '-b- does too
- (set! a 3) ; so '-b- is now 4
- -b-))
- <em class="gray">4</em>
- > (let ((a 1))
- (<em class=red>reactive-lambda*</em> (s v)
- (format *stderr* "~S -> ~S~%" s v))
- (set! a 3))
- <em class="gray">"a -> 3"</em>
- </pre>
-
- <p>In the reactive-let example, the macro notices that '-b- depends on 'a, so it sets up a symbol-access on 'a
- so that <code>(set! a 3)</code> triggers <code>(set! -b- (+ a 1))</code>. I'm using -name- to distinguish
- the variables that can change value at any time; in the Lisp community, +name+ is often used to mark a constant,
- so this seems like a natural convention.
- </p>
-
- <p>Here's the standard example of following the mouse (assuming you're using Snd and glistener):
- </p>
- <pre class="indented">
- (let ((*mouse-x* 0) (*mouse-y* 0)
- (x 0) (y 0))
-
- (reactive-set! x (let ((val (round *mouse-x*)))
- (format *stderr* "mouse: ~A ~A~%" x y)
- val))
- (reactive-set! y (round *mouse-y*))
-
- (g_signal_connect (G_OBJECT (listener-text-widget *listener*)) "motion_notify_event"
- (lambda (w e d)
- (let ((mxy (cdr (gdk_event_get_coords (GDK_EVENT e)))))
- (set! *mouse-x* (car mxy))
- (set! *mouse-y* (cadr mxy))))))
- </pre>
-
- <!--
- (let ((*mouse-x* 0) (*mouse-y* 0))
-
- (g_signal_connect (G_OBJECT (listener-text-widget *listener*)) "motion_notify_event"
- (lambda (w e d)
- (let ((mxy (gdk_event_get_coords (GDK_EVENT e))))
- (set! *mouse-x* (cadr mxy))
- (set! *mouse-y* (caddr mxy)))))
-
- (with-accessors (*mouse-x* *mouse-y*)
- (let ((x 0) (y 0))
- (reactive-set! x (let ((val (round *mouse-x*)))
- (format *stderr* "mouse: ~A ~A~%" x y)
- val))
- (reactive-set! y (round *mouse-y*))
- (gtk_main))))
- -->
-
- <p>reactive-lambda* is aimed at library consistency. Say we have the following library
- that wants A to always be half B:
- </p>
-
- <pre class="indented">
- (define (make-library)
- (let ((A 1.0)
- (B 2.0))
- (reactive-lambda* (s v)
- (case s
- ((A) (set! B (* 2 v)))
- ((B) (set! A (/ v 2)))))
- (define (f1 x)
- (+ A (* B x)))
- (curlet)))
-
- (with-let (make-library)
- (format *stderr* "(f1 3): ~A~%" (f1 3))
- (set! A 3.0)
- (format *stderr* "A: ~A, B: ~A, (f1 3): ~A~%" A B (f1 3))
- (set! B 4.0)
- (format *stderr* "A: ~A, B: ~A, (f1 3): ~A~%" A B (f1 3)))
- </pre>
-
- <p>reactive-lambda* sets up accessors on the library's top-level variables
- that call the lambda body if one of the variables is set.
- </p>
-
- <p>None of these macros does the right thing yet; I'm sort of following my nose.
- </p>
-
- </div>
- </details>
-
-
-
- <div class="header" id="miscellanea"><h4>marvels and curiousities</h4></div>
-
- <p>
- <b><em class=def id="loadpath">*load-path*</em></b> is a list of directories to search when loading a file.
- <b><em class=def id="loadhook">*load-hook*</em></b> is a hook whose functions are called just before a file is loaded.
- The hook function argument, named 'name, is the filename.
- While loading, the <em class=def id="portfilename">port-filename</em> and
- <em class=def id="portlinenumber">port-line-number</em> of the current-input-port can tell you
- where you are in the file. This data is available after loading via <em class=def id="pairlinenumber">pair-line-number</em>
- and <em class=def id="pairfilename">pair-filename</em>. port-line-number is settable (for fancy *#readers*).
- </p>
-
- <pre class="indented">
- (set! (hook-functions *load-hook*)
- (list (lambda (hook)
- (format () "loading ~S...~%" (hook 'name)))))
-
- (set! (hook-functions *load-hook*)
- (cons (lambda (hook)
- (format *stderr* "~A~%"
- (system (string-append "./snd lint.scm -e '(begin (lint \"" (hook 'name) "\") (exit))'") #t)))
- (hook-functions *load-hook*)))
- </pre>
-
- <p>Here's a *load-hook* function that adds the loaded file's directory
- to the *load-path* variable so that subsequent loads don't need to specify
- the directory:
- </p>
-
- <pre class="indented">
- (set! (hook-functions <em class=red>*load-hook*</em>)
- (list (lambda (hook)
- (let ((pos -1)
- (filename (hook 'name)))
- (do ((len (length filename))
- (i 0 (+ i 1)))
- ((= i len))
- (if (char=? (filename i) #\/)
- (set! pos i)))
- (if (positive? pos)
- (let ((directory-name (substring filename 0 pos)))
- (if (not (member directory-name <em class=red>*load-path*</em>))
- (set! <em class=red>*load-path*</em> (cons directory-name *load-path*)))))))))
- </pre>
-
-
- <div class="separator"></div>
-
- <p>As in Common Lisp, <b><em class=def id="featureslist">*features*</em></b> is a list describing what is currently loaded into s7. You can
- check it with the <b>provided?</b> function, or add something to it with <b>provide</b>. In my version of Snd,
- at startup *features* is:
- </p>
-
- <pre class="indented">
- > *features*
- <em class="gray">(snd-16.7 snd15 snd audio snd-s7 snd-gtk gsl alsa gtk2 xg clm6 clm sndlib linux
- dlopen complex-numbers system-extras ratio s7-3.26 s7) </em>
- > (provided? 'gsl)
- <em class="gray">#t</em>
- </pre>
-
- <p>The other side of <code>provide</code> is <em class=def id="requires7">require</em>.
- <code>(require . things)</code> finds each thing
- (via <a href="#autoload">autoload</a>), and if that thing has not already been loaded,
- loads the associated file. <code>(require integrate-envelope)</code>
- loads "env.scm", for example; in this case it is equivalent to
- simply using integrate-envelope, but if placed at the start of
- a file, it documents that you're using that function.
- In the more normal use, <code>(require snd-ws.scm)</code>
- looks for the file that has <code>(provide 'snd-ws.scm)</code>
- and if it hasn't already been loaded, loads it ("ws.scm" in this case).
- To add your own files to this mechanism, add the provided symbol via <a href="#autoload">autoload</a>.
- Since load can take an environment argument, *features* and its friends follow block structure.
- So, for example, (let () (require stuff.scm) ...) loads "stuff.scm" into the local environment,
- not globally.
- </p>
-
- <div class="indented">
- <p>*features* is an odd variable: it is spread out across the chain of environments, and
- can hold features in an intermediate environment that aren't in subsequent (nested) values.
- One simple way this can happen is to load a file in a let, but cause the load to happen
- at the top level. The provided entities get added to the top-level *features* value,
- not the current let's value, but they are actually accessible locally. So *features*
- is a merge of all its currently accessible values, vaguely like call-next-method in
- CLOS. We can mimic this behavior:
- </p>
- <pre class="indented">
- (let ((x '(a)))
- (let ((x '(b)))
- (define (transparent-memq sym var e)
- (let ((val (symbol->value var e)))
- (or (and (pair? val)
- (memq sym val))
- (and (not (eq? e (rootlet)))
- (transparent-memq sym var (outlet e))))))
- (let ((ce (curlet)))
- (list (transparent-memq 'a 'x ce)
- (transparent-memq 'b 'x ce)
- (transparent-memq 'c 'x ce)))))
-
- '((a) (b) #f)
- </pre>
- </div>
-
- <!--
- (let ((spread-function (lambda (x e) (+ x 1))))
- (let ((spread-function (lambda (x e) (+ x 2))))
- (let ((x 3))
- (define (spread-function x e)
- (let ((val x))
- (do ((e1 e (outlet e1)))
- ((eq? e1 (rootlet)) val)
- (let ((f (symbol->value 'spread-function e1)))
- (if (procedure? f)
- (set! val (f val (rootlet))))))))
- (spread-function x (curlet)))))
- 6
- -->
-
-
- <div class="separator"></div>
-
- <p>Multi-line and in-line comments can be enclosed in #| and |#.
- <code>(+ #| add |# 1 2)</code>.
- </p>
-
- <div class="indented">
- <p>Leaving aside this case and the booleans, #f and #t, you can specify your own handlers for
- tokens that start with "#". <b><em class=def id="sharpreaders">*#readers*</em></b> is a list of pairs: <code>(char . func)</code>.
- "char" refers to the first character after the sharp sign (#). "func" is a function of
- one argument, the string that follows the #-sign up to the next delimiter. "func" is called
- when #<char> is encountered. If it returns something other than #f, the #-expression
- is replaced with that value. Scheme has several predefined #-readers for cases such
- as #b1, #\a, #i123, and so on, but you can override these if you like. If the string
- passed in is not the complete #-expression, the function can use read-char or read to get the
- rest. Say we'd like #t<number> to interpret the number in base 12:
- </p>
-
- <pre class="indented">
- (set! *#readers* (cons (cons #\t (lambda (str) (string->number (substring str 1) 12))) *#readers*))
-
- > #tb
- <em class="gray">11</em>
- > #t11.3
- <em class="gray">13.25</em>
- </pre>
-
- <p>Or have #c(real imag) be read as a complex number:
- </p>
-
- <pre class="indented">
- (set! *#readers* (cons (cons #\c (lambda (str) (apply complex (read)))) *#readers*))
-
- > #c(1 2)
- <em class="gray">1+2i</em>
- </pre>
-
- <p>Here's a reader macro for read-time evaluation:
- </p>
-
- <pre class="indented">
- (set! *#readers*
- (cons (cons #\. (lambda (str)
- (and (string=? str ".") (eval (read)))))
- *#readers*))
-
- > '(1 2 #.(* 3 4) 5)
- <em class="gray">(1 2 12 5)</em>
- </pre>
-
-
- <p>And a reader that implements #[...]# for literal hash-tables:
- </p>
-
- <pre class="indented">
- > (set! *#readers*
- (list (cons #\[ (lambda (str)
- (let ((h (make-hash-table)))
- (do ((c (read) (read)))
- ((eq? c ']#) h) ; ]# is a symbol from the reader's point of view
- (set! (h (car c)) (cdr c))))))))
- <em class="gray">((#\[ . #<lambda (str)>))</em>
- > #[(a . 1) (b . #[(c . 3)]#)]#
- <em class="gray">(hash-table '(b . (hash-table '(c . 3))) '(a . 1))</em>
- </pre>
-
-
- <p>To return no value from a reader, use <code>(values)</code>.
- </p>
- <pre class="indented">
- > (set! *#readers* (cons (cons #\; (lambda (str) (if (string=? str ";") (read)) (values))) *#readers*))
- <em class="gray">((#\; . #<lambda (str)>))</em>
- > (+ 1 #;(* 2 3) 4)
- <em class="gray">5</em>
- </pre>
- <p>Here is CL's #+ reader:
- </p>
- <pre class="indented">
- (define (sharp-plus str)
- ;; str here is "+", we assume either a symbol or an expression involving symbols follows
- (let ((e (if (string=? str "+")
- (read) ; must be #+(...)
- (string->symbol (substring str 1)))) ; #+feature
- (expr (read))) ; this is the expression following #+
- (if (symbol? e)
- (if (provided? e)
- expr
- (values))
- (if (not (pair? e))
- (error "strange #+ chooser: ~S~%" e)
- (begin ; evaluate the #+(...) expression as in cond-expand
- (define (traverse tree)
- (if (pair? tree)
- (cons (traverse (car tree))
- (if (null? (cdr tree)) () (traverse (cdr tree))))
- (if (memq tree '(and or not)) tree
- (and (symbol? tree) (provided? tree)))))
- (if (eval (traverse e))
- expr
- (values)))))))
- </pre>
- <p>See also the <a href="#circularlistreader">#n=</a> reader below.</p>
- </div>
-
- <div class="separator"></div>
-
- <p id="makelist">(<b>make-list</b> length (initial-element #f)) returns a list of 'length' elements defaulting to 'initial-element'.
- </p>
-
- <div class="separator"></div>
-
- <pre class="indented">
- (<em class=def id="charposition">char-position</em> char-or-string searched-string (start 0))
- (<em class=def id="stringposition">string-position</em> substring searched-string (start 0))
- </pre>
-
- <p>
- <b>char-position</b> and <b>string-position</b> search a string for the occurrence of a character,
- any of a set of characters, or a string. They return either #f if none is found, or the position
- within the searched string of the first occurrence. The optional third argument sets where the
- search starts in the second argument.
- </p>
-
- <p>If char-position's first argument is a string, it is treated as a set of characters, and
- char-position looks for the first occurrence of any member of that set.
- Currently, the strings involved are assumed to be C strings (don't expect embedded nulls
- to work right in this context).
- </p>
-
- <pre class="indented">
- (call-with-input-file "s7.c" ; report any lines with "static " but no following open paren
- (lambda (file)
- (let loop ((line (read-line file #t)))
- (or (eof-object? line)
- (let ((pos (<em class=red>string-position</em> "static " line)))
- (if (and pos
- (not (<em class=red>char-position</em> #\( (substring line pos))))
- (if (> (length line) 80)
- (begin (display (substring line 0 80)) (newline))
- (display line))))
- (loop (read-line file #t)))))))
- </pre>
-
-
- <div class="separator"></div>
-
- <p id="keywords">
- Keywords exist mainly for define*'s benefit. The keyword functions are:
- <b>keyword?</b>, <b>make-keyword</b>, <b>symbol->keyword</b>, and <b>keyword->symbol</b>.
- A keyword is a symbol that starts or ends with a colon. The colon
- is considered to be a part of the symbol name. A keyword is a constant that evaluates to itself.
- </p>
-
-
-
- <div class="separator"></div>
-
- <pre class="indented">
- (<em class=def id="symboltable">symbol-table</em>)
- (<em class=def id="symboltovalue">symbol->value</em> sym (env (curlet)))
- (<em class=def id="symboltodynamicvalue">symbol->dynamic-value</em> sym)
- (<em class=def id="definedp">defined?</em> sym (env (curlet)) ignore-global-env)
- </pre>
-
- <p>
- <code>defined?</code> returns #t if the symbol is defined in the environment:
- </p>
-
- <pre class="indented">
- (define-macro (defvar name value)
- `(unless (defined? ',name)
- (define ,name ,value)))
- </pre>
-
- <p>If ignore-global-env is #t, the search is confined to the given environment.
- <code>symbol->value</code> returns the value (lexically) bound to the symbol, whereas <code>symbol->dynamic-value</code>
- returns the value dynamically bound to it. A variable can access both of its values:
- </p>
- <pre class="indented">
- > (let ((x 32))
- (define (gx) ; return both bindings of 'x
- (list x (<em class=red>symbol->value</em> 'x) (<em class=red>symbol->dynamic-value</em> 'x)))
- (let ((x 100))
- (let ((x 12))
- (values (gx))))) ; this 'values' looks dumb, it is dumb, but see my unconvincing explanantion <a href="#weaselwords">below</a>.
- ; briefly: "dynamic binding" in s7 is not "lexically apparent dynamic binding"
- <em class="gray">(32 32 12)</em>
- </pre>
-
- <p>
- <code>symbol-table</code> returns a vector containing the symbols currently in the symbol-table.
- Here we scan the symbol table looking for any function that doesn't have documentation:
- </p>
-
- <pre class="indented">
- (for-each
- (lambda (sym)
- (if (<em class=red>defined?</em> sym)
- (let ((val (<em class=red>symbol->value</em> sym)))
- (if (and (procedure? val)
- (string=? "" (procedure-documentation val)))
- (format *stderr* "~S " sym)))))
- (<em class=red>symbol-table</em>))
- </pre>
-
-
- <div class="indented">
-
- <p>Here's a better example, an automatic software tester.
- </p>
-
- <pre class="indented">
- (for-each
- (lambda (sym)
- (if (<em class=red>defined?</em> sym)
- (let ((val (<em class=red>symbol->value</em> sym)))
- (if (procedure? val)
- (let ((max-args (cdr (arity val))))
- (if (or (> max-args 4)
- (memq sym '(exit abort)))
- (format () ";skip ~S for now~%" sym)
- (begin
- (format () ";whack on ~S...~%" sym)
- (let ((constants (list #f #t pi () 1 1.5 3/2 1.5+i)))
- (let autotest ((args ()) (args-left max-args))
- (catch #t (lambda () (apply func args)) (lambda any #f))
- (if (> args-left 0)
- (for-each
- (lambda (c)
- (autotest (cons c args) (- args-left 1)))
- constants)))))))))))
- (<em class=red>symbol-table</em>))
- </pre>
- </div>
-
- <details id="weaselwords">
- <summary class="indented">more on dynamic values</summary>
- <div class="indented">
- <p>Why the useless 'values' in our dynamic binding example? The short answer: tail calls.
- The long winded one goes something like this. symbol->dynamic-value searches the stack to find
- the latest binding of its argument. But because we want to support tail calls, "let" does not
- push anything on the stack. If we call a function as the last thing that happens in that let's body,
- and it tries (internally) to access a dynamic binding, the let that launched the function no longer exists; it might already
- be garbage collected, and it certainly isn't on the stack. Take our earlier example without the
- 'values':
- </p>
-
- <pre class="indented">
- (let ((x 32))
- (define (gx)
- (symbol->dynamic-value 'x))
- (let ((x 100))
- (gx)))
- </pre>
-
- <p>
- This returns 32 because the <code>(x 100)</code> binding no longer exists anywhere when the gx body is evaluated.
- So, in s7, the "dynamic binding" of x is the last binding of x that is accessible
- to s7. This may not be the last binding that we can see in the code text, but I don't see that as
- an inconsistency. It's not lexical after all.
- Leaving aside this special case, so to speak,
- dynamic binding does what you'd expect, even in the context of call/cc. See s7test.scm for
- the MIT-Scheme test of that interaction.
- </p>
-
- <p>There is another way to get the call-time value:
- </p>
- <pre class="indented">
- (define-macro (elambda args . body)
- `(define-macro (,(gensym) ,@args)
- `((lambda* ,(append ',args `((*env* (curlet))))
- ,'(begin ,@body))
- ,,@args)))
-
- (define efunc (elambda (x) (+ (*env* 'y) x))) ; add x to the run-time value of y
- (let ((y 3)) (efunc 1)) ; returns 4
- </pre>
-
- <p>elambda does not suffer from the symbol->dynamic-value defects mentioned above, but
- it's probably slower. We have to wrap the function in a macro because lambda*'s argument default
- values are evaluated in the definition environment, but we want the run-time environment.
- </p>
-
- <pre class="indented">
- (define-macro* (rlambda args . body) ; lambda* but eval arg defaults in run-time env
- (let ((arg-names (map (lambda (arg) (if (pair? arg) (car arg) arg)) args))
- (arg-defaults (map (lambda (arg) (if (pair? arg) `(,(car arg) (eval ,(cadr arg))) arg)) args)))
- `(define-bacro* (,(gensym) ,@arg-defaults)
- `((lambda ,',arg-names ,'(begin ,@body)) ,,@arg-names))))
- <!-- ( -->
- (let ()
- (define rx (rlambda ((a x))
- (if (> a 0)
- (let ((x (- x 1))) ;:)
- (rx))
- 0)))
- (let ((x 3))
- (rx)))
- </pre>
-
- <p>Now putting that idea with the procedure-annotation macro given earlier, and the __func__ business
- (to get the caller's name), and taking care to handle default arguments correctly, we make a
- macro that returns an anonymous macro that returns an anonymous function that — where was I?
- </p>
- <pre class="indented">
- (define-macro (Display definition) ; (Display (define (f1 x) (+ x 1))) -> an annotated version of f1
- (let ((func (caadr definition))
- (args (cdadr definition)))
- (let ((arg-names (map (lambda (a) (if (symbol? a) a (car a))) args))
- (body (proc-walk `(begin ,@(cddr definition))))) ; from procedure-annotation above
- `(define ,func
- (define-macro* (,(gensym) ,@args)
- (let ((e (gensym))
- (result (gensym)))
- `((lambda* ,(append ',arg-names `((,e (curlet)))) ; the calling environment
- (let ((,result '?))
- (dynamic-wind
- (lambda () ; display the caller, if any, as well as the incoming arguments
- (format *stderr* "(~A~{~^ ~A~})" ',',func
- (map (lambda (slot)
- (if (gensym? (car slot)) (values) slot))
- (outlet (outlet (curlet)))))
- (let ((caller (eval '__func__ ,e)))
- (if (not (eq? caller #<undefined>))
- (format *stderr* " ;called from ~S" caller))
- (newline *stderr*)))
- (lambda () ; evaluate the original function's body with annotations
- (set! ,result ,',body))
- (lambda () ; display the result (it is returned by the set! above)
- (format *stderr* " -> ~S~%" ,result)))))
- ,,@arg-names)))))))
- </pre>
- <p>stuff.scm has a more mature version of this macro.
- </p>
- </div>
- </details>
-
- <div class="separator"></div>
-
- <p id="s7help"><b>help</b> tries to find information about its argument.
- </p>
-
- <pre class="indented">
- > (help 'caadar)
- <em class="gray">"(caadar lst) returns (car (car (cdr (car lst)))): (caadar '((1 (2 3)))) -> 2"</em>
- </pre>
-
-
-
- <div class="separator"></div>
-
- <p id="s7gc"><b>gc</b> calls the garbage collector. <code>(gc #f)</code> turns off the GC, and <code>(gc #t)</code> turns it on.
- </p>
-
-
- <div class="separator"></div>
-
- <pre class="indented">
- (<b><em class=def id="morallyequalp">morally-equal?</em></b> x y)
- </pre>
-
- <p>
- Say we want to check that two different computations came to the same result, and that result might
- involve circular structures. Will equal? be our friend?
- </p>
-
- <pre class="indented">
- > (equal? 2 2.0)
- <em class="gray">#f</em>
- > (let ((x +nan.0)) (equal? x x))
- <em class="gray">#f</em>
- > (equal? .1 1/10)
- <em class="gray">#f </em>
- > (= .1 1/10)
- <em class="gray">#f</em>
- > (= 0.0 0+1e-300i)
- <em class="gray">#f</em>
- </pre>
-
- <p>No! We need an equality check that ignores epsilonic differences in real and
- complex numbers, and knows that NaNs are equal for all practical purposes.
- Leaving aside numbers,
- closed ports are not equal, yet nothing can be done with them.
- #() is not equal to #2d(). And two closures are never equal, even if their
- arguments, environments, and bodies are equal.
- Since there might be circles, it is not easy to write
- a replacement for equal? in Scheme.
- So, in s7, if one thing is basically the same as
- some other thing, they satisfy the function morally-equal?.
- </p>
-
- <pre class="indented">
- > (morally-equal? 2 2.0) ; would "equal!?" be a better name?
- <em class="gray">#t</em>
- > (morally-equal? 1/0 1/0) ; NaN
- <em class="gray">#t</em>
- > (morally-equal? .1 1/10)
- <em class="gray">#t</em> ; floating-point epsilon here is 1.0e-15 or thereabouts
- > (morally-equal? 0.0 1e-300)
- <em class="gray">#t</em>
- > (morally-equal? 0.0 1e-14)
- <em class="gray">#f</em> ; its not always #t!
- > (morally-equal? (lambda () #f) (lambda () #f))
- <em class="gray">#t</em>
- </pre>
-
- <p>The *s7* field morally-equal-float-epsilon sets the floating-point fudge factor.
- I can't decide how bignums should interact with morally-equal?. Currently,
- if a bignum is involved, either here or in a hash-table, s7 uses equal?.
- Finally, if either argument is an environment with a 'morally-equal? method,
- that method is invoked.
- </p>
-
-
- <div class="separator"></div>
-
- <p>
- <b><em class=def id="expansion">define-expansion</em></b> defines a macro that expands at read-time.
- It has the same syntax as
- define-macro, and (in normal use) the same result, but it is much faster because it expands only once.
- (See define-with-macros in s7test.scm for a way to expand macros in a function body at definition time).
- Since the reader knows almost nothing
- about the code it is reading,
- you need to make sure the expansion is defined at the top level and that its name is unique!
- The reader does know about global variables, so:
- </p>
-
- <pre class="indented">
- (define *debugging* #t)
-
- (define-expansion (assert assertion)
- (if *debugging* ; or maybe better, (eq? (symbol->value '*debugging*) #t)
- `(unless ,assertion
- (format *stderr* "~A: ~A failed~%" __func__ ',assertion))
- (values)))
- </pre>
-
- <p>Now the assertion code is only present in the function body (or wherever)
- if *debugging* is #t; otherwise assert expands into nothing. Another very handy
- use is to embed a source file line number into a message; see for example lint-format
- in lint.scm.
- Leaving aside
- read-time expansion and splicing, the real difference between define-macro and define-expansion
- is that the expansion's result is not evaluated.
- I'm no historian, but I believe that means that define-expansion creates
- a (gasp!) f*xpr. In fact:
- </p>
-
- <pre>
- (define-macro (define-f*xpr name-and-args . body)
- `(define ,(car name-and-args)
- (apply define-expansion
- (append (list (append (list (gensym)) ',(cdr name-and-args))) ',body))))
-
- > (define-f*xpr (mac a) `(+ ,a 1))
- <em class="gray">mac</em>
- > (mac (* 2 3))
- <em class="gray">(+ (* 2 3) 1)</em>
- </pre>
-
- <p>
- You can do something similar with a normal macro, or make the indirection explicit:
- </p>
-
- <pre class="indented">
- > (define-macro (fx x) `'(+ 1 ,x)) ; quote result to avoid evaluation
- <em class="gray">fx</em>
- > (let ((a 3)) (fx a))
- <em class="gray">(+ 1 a)</em>
- > (define-expansion (ex x) `(+ 1 ,x))
- <em class="gray">ex</em>
- > (let ((x ex) (a 3)) (x a)) ; avoid read-time splicing
- <em class="gray">(+ 1 a)</em>
- > (let ((a 3)) (ex a)) ; spliced in at read-time
- <em class="gray">4</em>
- </pre>
-
- <p>As this example shows, the reader knows nothing about the program context,
- so if it does not see a list whose first element is a expansion name, it does
- not do anything special. In the <code>(x a)</code> case above, the
- expansion happens when the code is evaluated, and the expansion result
- is simply returned, unevaluated.
- </p>
-
- <p>You can also use macroexpand to cancel the evaluation of a macro's expansion:
- </p>
- <pre>
- (define-macro (rmac . args)
- (if (null? args)
- ()
- (if (null? (cdr args))
- `(display ',(car args))
- (list 'begin
- `(display ',(car args))
- (apply macroexpand (list (cons 'rmac (cdr args))))))))
-
- > (macroexpand (rmac a b c))
- <em class="gray">(begin (display 'a) (begin (display 'b) (display 'c)))</em>
- > (begin (rmac a b c d) (newline))
- <em class="gray">abcd</em>
- </pre>
-
- <p>The main built-in expansion is <b><em class=def id="readercond">reader-cond</em></b>. The syntax is based on cond:
- the car of each clause is evaluated (in the read-time context), and if it is not false,
- the remainder of that clause is spliced into the code as if you had typed it from the start.
- </p>
-
- <pre class="indented">
- > '(1 2 (reader-cond ((> 1 0) 3) (else 4)) 5 6)
- <em class="gray">(1 2 3 5 6)</em>
- > ((reader-cond ((> 1 0) list 1 2) (else cons)) 5 6)
- <em class="gray">(1 2 5 6)</em>
- </pre>
-
-
- <!-- from kanren
- (define-syntax conj*
- (syntax-rules ()
- ((conj*) succeed)
- ((conj* g) g)
- ((conj* g gs ...)
- (conj g (lambda (s) ((conj* gs ...) s))))))
-
- is the same (in that context) as:
-
- (define-macro (conj* . args)
- (if (null? args)
- succeed
- (if (null? (cdr args))
- (car args)
- `(conj ,(car args)
- (lambda (s) ((conj* ,@(cdr args)) s))))))
- -->
-
-
- <div class="separator"></div>
- <p id="profiling">If you build s7 with the flag -DWITH_PROFILE=1, s7 accumulates profiling data
- in the (*s7* 'profile-info) hash-table. The line number reported sometimes points to the end of the form.
- Also, do loops and other iterations are sometimes highly optimized, so only the outer form is counted.
- profile.scm shows one way to sort and display this data. To clear the counts, <code>(fill! (*s7* 'profile-info) #f)</code>.
- </p>
-
-
- <div class="separator"></div>
- <p id="s7env"><b>*s7*</b> is an environment that gives access to some of s7's internal
- state:
- </p>
- <pre class="indented">
- print-length number of elements of a sequence to print
- max-string-length max size arg to make-string and read-string
- max-list-length max size arg to make-list
- max-vector-length max size arg to make-vector and make-hash-table
- max-vector-dimensions make-vector dimensions limit
- default-hash-table-length default size for make-hash-table (8, tables resize as needed)
- initial-string-port-length 128, initial size of a string port's buffer
- history-size eval history buffer size if s7 built WITH_HISTORY=1
- profile-info profiling data if s7 built WITH_PROFILE=1
-
- default-rationalize-error 1e-12 (settable)
- morally-equal-float-epsilon 1e-15 (settable)
- hash-table-float-epsilon 1e-12 (settable, but currently limited to less than 1e-3).
- bignum-precision bits for bignum floats (128)
- float-format-precision digits to print for floats (16)
- default-random-state the default arg for random (settable)
-
- cpu-time run time so far
- file-names currently loaded files (a list)
-
- safety 0
- undefined-identifier-warnings #f
-
- catches a list of the currently active catch tags
- exits a list of active call-with-exit exit functions
- c-types a list of c-object type names (from s7_new_type, etc)
- input-ports, output-ports, strings, gensyms, vectors, hash-tables, continuations
-
- stack-top current stack location
- stack-size current stack size
- max-stack-size max stack size (settable)
- stack the current stack entries
- stacktrace-defaults stacktrace formatting info for error handler
-
- symbol-table a vector
- symbol-table-locked? #f (if #t, no new symbols can be added to the symbol table)
- rootlet-size the number of globals
-
- heap-size total cells currently available
- free-heap-size the number of currently unused cells
- gc-freed number of cells freed by the last GC pass
- gc-protected-objects vector of the objects permanently protected from the GC
- gc-stats #f (#t turns on printout of the GC activity)
- memory-usage a description of current memory allocations
- </pre>
-
- <p>
- Use the standard environment syntax to access these fields:
- <code>(*s7* 'stack-top)</code>. stuff.scm has the function
- *s7*->list that returns most of these fields in a list.
- </p>
-
- <p>
- Set (*s7* 'safety) to 2 or higher
- to turn off optimization.
- </p>
-
- <p>stacktrace-defaults is a list of four integers and a boolean that tell the error
- handler how to format stacktrace information. The four integers are:
- how many frames to display,
- how many columns are devoted to code display,
- how many columns are available for a line of data,
- and where to place comments.
- The boolean sets whether the entire output should be displayed as a comment.
- The defaults are '(3 45 80 45 #t).
- </p>
-
- <pre class="indented">
- (<em class=def id="cobject">c-object?</em> obj)
- (<em class=def id="cpointer">c-pointer?</em> obj)
- (<em class=def id="cpoint">c-pointer</em> int)
- </pre>
-
- <p>
- You can wrap up raw C pointers and
- pass them around in s7 code. The function c-pointer returns a wrapped pointer,
- and c-pointer? returns #t if passed one. <code>(define NULL (c-pointer 0))</code>.
- c-object? returns the object's type tag if passed such an object (otherwise #f of course). This tag is also the position
- of the object's type in the (*s7* 'c-types) list.
- (*s7* 'c-types) returns a list of the types created by s7_new_type_x and friends.
- </p>
-
-
- <div class="separator"></div>
-
- <blockquote>
-
- <div class="indented">
- <p id="s7vsr5rs">Some other differences from r5rs:
- </p>
-
- <ul>
- <li>no force or delay (see <a href="#r7rs">below</a>).
- <li>no syntax-rules or any of its friends.
- <li>no scheme-report-environment, null-environment, or interaction-environment (use curlet).
- <li>no transcript-on or transcript-off.
- <li>begin returns the value of the last form; it can contain both definitions and other statements.
- <li>#<unspecified>, #<eof>, and #<undefined> are first-class objects.
- <li>for-each and map accept different length arguments; the operation stops when any argument reaches its end.
- <li>for-each and map accept any applicable object as the first argument, and any sequence or iterator as a trailing argument.
- <li>letrec*, but without conviction.
- <li>set! and *-set! return the new value (modulo symbol-access), not #<unspecified>.
- <li>define and its friends return the new value.
- <li>port-closed?
- <li>list? means "pair or null", proper-list? is r5rs list?, float? means "real and not rational", sequence? = length.
- <!-- a vector can be a member of itself, and yet vector? returns #t, why is list? different; we even call it a circular list! -->
- <!-- <li>current-input-port, current-output-port, and current-error-port have setters (but I may remove these) -->
- <li>the default IO ports are named *stdin*, *stdout*, and *stderr*.
- <li>#f as an output port means nothing is output (#f is /dev/null, I guess).
- <li>member and assoc accept an optional third argument, the comparison function (equal? is the default).
- <li>case accepts => much like cond (the function argument is the selector).
- <li>if WITH_SYSTEM_EXTRAS is 1, the following are built-in: directory?, file-exists?, delete-file, system, directory->list, getenv.
- <li>s7 is case sensitive.
- <li>when and unless (for r7rs), returning the value of the last form.
- <li>the "d", "f", "s", and "l" exponent markers are not supported by default (use "e", "E", or "@").
- <li>quasiquoted vector constants are not supported (use the normal list expansions wrapped in list->vector).
- </ul>
-
- <p>In s7 if a built-in function like gcd is referred to in a function
- body, the optimizer is free to replace it with #_function. That is, <code>(gcd ...)</code> can be changed
- to <code>(#_gcd ...)</code> at s7's whim, if gcd has its original value at the time the optimizer
- sees the expression using it. A subsequent <code>(set! gcd +)</code> does not affect this optimized call.
- I think I could wave my hands and mumble about "aggressive lexical scope" or something, but actually the
- choice here is that speed trumps that ol' hobgoblin consistency. If you want to change gcd to +, do it before
- loading code that calls gcd.
- I think most Schemes handle macros this way: the macro call is replaced by its expansion using its current
- definition, and a later redefinition does not affect earlier uses.
- </p>
-
- <!-- another case: (with-let (inlet '+ -) (+ 2 3)) -> 5 -->
- <!-- also, (eq? (if #f #f) (apply values ())) is #t, but memq and assq don't treat them as equal -->
-
- </div>
-
- <!-- null-environment does not fit very well; how to shadow earlier define-constant for example
- one of which is unlet; perhaps make a special environment at init time?
- the code for that is save_null_environment, but it is untested and commented out.
- -->
-
- <div class="indented">
-
- <p>Here are some changes I'd make to s7 if I didn't care about compatibility with other Schemes:
- </p>
-
- <ul>
- <li>remove the exact/inexact distinction including #i and #e
- <li>remove call-with-values and its friends
- <li>remove char-ready?
- <li>change eof-object? to eof? or just omit it (you can use eq? #<eof>)
- <li>change make-rectangular to complex, and remove make-polar.
- <li>remove unquote (the name, not the functionality).
- <li>remove cond-expand.
- <li>remove *-ci functions
- <li>remove #d
- </ul>
-
- <p>(most of these are removed if you set the compiler flag WITH_PURE_S7), and perhaps:
- </p>
-
- <ul>
- <li>remove even? and odd?, gcd and lcm.
- <li>remove string-length and vector-length.
- <li>remove list-ref|set!, string-ref|set!, vector-ref|set!, hash-table-ref|set!, set-car!|cdr!, and set-current-output|input|error-port.
- <li>change file-exists? to file? (or omit it and assume the use of libc.scm — why reinvent the wheel?).
- <li>remove all the conversion and copy functions like vector->list and vector-copy (use copy or map).
- <li>add typeq? and perhaps make null? generic (see stuff.scm).
- <li>change string->symbol to symbol (what to do with symbol->string in that case?)
- <li>change with-output-to-* and with-input-from-* to omit the pointless lambda.
- <li>remove the with-* IO functions (e.g. with-input-from-string), keeping the call-with-* versions (call-with-input-string).
- <li>remove assq, assv, memq, and memv (these are pointless now that assoc and member can be passed eq? and eqv?).
- </ul>
-
- <p>There are several less-than-ideal names. Perhaps s7 should use *pi*, *most-negative-fixnum*,
- *most-positive-fixnum* (*fixmost*?) so that all the built-in variables and constants have the
- same kind of name (or +pi+ to show it is a constant?).
- Perhaps object->string should be ->string.
- get-output-string should be current-output-string. write-char behaves like display, not write.
- provided? should be feature? or *features* should be *provisions*.
- </p>
-
- <p>
- The name "cond-expand" is bad — we're not expanding anything, and the macro's point is to make it easy to
- operate with the *features* list; perhaps "cond-provided"? Not only is cond-expand poorly named, but the whole
- idea is clumsy. Take libgsl.scm; different versions of the GSL library have different functions. We need to know
- when we're building the FFI what GSL version we're dealing with. It would be nuts to start pushing and checking dozens
- of library version symbols when all we actually want is <code>(> version 23.19)</code>. (We are also politely
- trying to ignore the ridiculous little language built into cond-expand; are we not running Scheme?).
- In place of cond-expand, s7 uses <a href="#readercond">reader-cond</a>,
- so the read-time decision involves normal evaluation.
- </p>
-
- <p>Then there's the case case: a case clause without a result appears to be an error in r7rs.
- But the notation used to indicate that is the same as that used for begin.
- So if we allow <code>(begin)</code>, we should allow case clauses to have no explicit result.
- Currently in s7, the case case is an error and <code>(begin)</code> returns nil, which strikes me
- as, *horrors*, inconsistent. But in cond,
- the "implicit progn" (in CL terminology) includes the test expression, so a clause without a result returns
- the test result (if true of course). In the case case, a similar approach might return
- the selector value. So should <code>(case x ((0 1)))</code> be an error, or equivalent
- to <code>(case x ((0 1) (begin)))</code> as in CL, or (my favorite) <code>(case x ((0 1) => values))</code>.
- If the latter, an empty else statement would also return the selector since it would be the
- same as <code>(else => values)</code>.
- </p>
-
-
- <p>
- Better ideas are always welcome!
- </p>
-
- <p>Here are the built-in s7 variables:
- </p>
- <ul>
- <li>*features* ; a list of symbols
- <li>*libraries* ; a list of (filename . let) pairs
- <li>*load-path* ; a list of directories
- <li>*cload-directory* ; directory for cload output
- <li>*autoload* ; autoload info
- <li>*#readers* ; a list of (char . handler) pairs
- </ul>
-
- <p>And the built-in constants:
- </p>
- <ul>
- <li>pi
- <li>*stdin* *stdout* *stderr*
- <li>*s7*
- <li>nan.0 -nan.0 inf.0 -inf.0 (what crappy names! nan.0 is an inexact integer that is not a number?)
- <li>most-positive-fixnum most-negative-fixnum
- <li>*unbound-variable-hook* *missing-close-paren-hook* *load-hook* *error-hook* *read-error-hook*
- </ul>
-
- <p>__func__ is the name (or name and location) of the function currently being called, as in C.
- </p>
-
- <p>Currently WITH_PURE_S7:
- </p>
- <ul>
- <li>places 'pure-s7 in *features*
- <li>omits char-ready, char-ci*, string-ci*
- <li>omits string-copy, string-fill!, vector-fill!, vector-append
- <li>omits list->string, list->vector, string->list, vector->list, let->list
- <li>omits string-length and vector-length
- <li>omits cond-expand, multiple-values-bind|set!, call-with-values, defmacro(*)
- <li>omits unquote (the name)
- <li>omits d/f/s/l exponents
- <li>omits make-polar and make-rectangular (use complex)
- <li>omits integer-length, exact?, inexact?, exact->inexact, inexact->exact, #i and #e
- <li>omits set-current-output-port and set-current-input-port
- </ul>
-
- </div>
-
-
-
- <details>
- <summary class="indented">quotes</summary>
- <div class="indented">
-
- <p>Schemes vary in their treatment of (). s7 considers it a constant that evaluates to itself,
- so you don't need to quote it. <code>(eq? () '())</code> is #t.
- This is consistent with, for example,
- <code>(eq? #f '#f)</code> which is also #t.
- The standard says "the empty list is a special object of its own type", so surely either choice is
- acceptable in that regard. One place where the quote matters is in a case statement; the selector is
- evaluated but the key is not:
- </p>
-
- <pre class="indented">
- > (case '() ((()) 2) (else 1))
- <em class="gray">2</em>
- > (case '() (('()) 2) (else 1)) ; (eqv? '() ''()) is #f
- <em class="gray">1</em>
- ;;; which parallels #f (or a number such as 2 etc):
- > (case '#f ((#f) 2) (else 1))
- <em class="gray">2</em>
- > (case '#f (('#f) 2) (else 1)) ; (eqv? '#f ''#f) is #f
- <em class="gray">1</em>
- </pre>
-
- <p>Similarly, vector constants do not have to be quoted. A list constant is quoted
- to keep it from being evaluated, but
- #(1 2 3) is as unproblematic as "123" or 123.
- </p>
-
- <!-- there's another sense in which '() is a constant: you can't apply it to anything. ('() 0) -> error
- -->
-
- <p>These examples bring up another odd corner of scheme: else. In <code>(cond (else 1))</code>
- the 'else is evaluated (like any cond test), so its value might be #f; in <code>(case 0 (else 1))</code>
- it is not evaluated (like any case key), so it's just a symbol.
- Since symbol accessors are local in s7,
- someone can <code>(let ((else #f)) (cond (else 1)))</code> even if we protect the rootlet 'else.
- Of course, in scheme this kind of trouble is pervasive, so rather than make 'else a constant
- I think the best path is to use unlet:
- <code>(let ((else #f)) (cond (#_else 1)))</code>. This is 1 (not ()) because the initial value of 'else
- can't be changed.
- </p>
- </div>
- </details>
-
-
- <details>
- <summary class="indented">mutable constants</summary>
- <div class="indented">
-
- <p>How should s7 treat this:
- <code>(string-set! "hiho" 1 #\z)</code>, or
- <code>(vector-set! #(1 2 3) 1 32)</code>, or
- <code>(list-set! '(1 2 3) 1 32)</code>?
- Originally, in s7, the first two were errors, and the third was allowed, which doesn't make much sense.
- Guile and Common Lisp accept all three, but that leads to weird cases where we can reach
- into a function's body:
- </p>
-
- <pre class="indented">
- > (let ((x (lambda () '(1 2 3)))) (list-set! (x) 1 32) (x))
- <em class="gray">(1 32 3)</em> ; s7, Guile
- > (flet ((x () '(1 2 3))) (setf (nth 1 (x)) 32) (x))
- <em class="gray">(1 32 3)</em> ; Clisp
- > (let ((x (lambda () (list 1 2 3)))) (list-set! (x) 1 32) (x))
- <em class="gray">(1 2 3)</em>
- </pre>
-
- <p>
- But it's possible to reach into a function's closure, even when the
- closed-over thing is a constant:
- </p>
-
- <pre class="indented">
- > (flet ((x () '(1 2 3))) (setf (nth 1 (x)) 32) (x))
- <em class="gray">(1 32 3)</em>
- > (let ((xx (let ((x '(1 2 3))) (lambda () x)))) (list-set! (xx) 1 32) (xx))
- <em class="gray">(1 32 3)</em>
- > (let ((xx (let ((x (list 1 2 3))) (lambda () x)))) (list-set! (xx) 1 32) (xx))
- <em class="gray">(1 32 3)</em>
- </pre>
-
- <p>
- And it's possible to reach into a constant list via list-set! (or set-car! of course):
- </p>
-
- <pre class="indented">
- > (let* ((x '(1 2)) (y (list x)) (z (car y))) (list-set! z 1 32) (list x y z))
- <em class="gray">((1 32) ((1 32)) (1 32))</em>
- </pre>
-
- <p>
- It would be a programmer's nightmare to have to keep track of which piece of a list is
- constant, and an implementor's nightmare to copy every list. set! in all its forms is
- used for its side-effects, so why should we try to put a fence around them?
- If we flush "immutable constant" because it is a ham-fisted, whack-it-with-a-shovel approach,
- the only real problem I can see is symbol->string. In CL, this is explicitly an error:
- </p>
-
- <pre class="indented">
- > (setf (elt (symbol-name 'xyz) 1) #\X)
- <em class="gray">*** - Attempt to modify a read-only string: "XYZ"</em>
- </pre>
-
- <p>And in Guile:
- </p>
-
- <pre class="indented">
- > (string-set! (symbol->string 'symbol->string) 1 #\X)
- <em class="gray">ERROR: string is read-only: "symbol->string"</em>
- </pre>
-
- <p>So both have a notion of immutable strings.
- I wonder what other Scheme programmers (not implementors!) want in this situation.
- Currently, there are no immutable list, string, or vector constants, and
- symbol->string
- returns a copy of the string.
- copy can protect some values. Or combine object->string and string->symbol:
- </p>
-
- <pre class="indented">
- > (let ((x (lambda () (copy "hiho")))) (string-set! (x) 1 #\x) (x))
- <em class="gray">"hiho"</em>
- > (let ((x (string->symbol "hiho"))) (string-set! (symbol->string x) 1 #\x) (symbol->string x))
- <em class="gray">"hiho"</em>
- > (define (immutable obj) (string->symbol (object->string obj :readable)))
- <em class="gray">immutable</em>
- > (define (symbol->object sym) (eval-string (symbol->string sym)))
- <em class="gray">symbol->object</em>
- > (symbol->object (immutable (list 1 2 3)))
- <em class="gray">(1 2 3)</em>
- </pre>
-
- <p>s7 normally tries to optimize garbage collection by
- removing some list constants from the heap. If you later
- set a member of it to something that needs GC protection, nobody in the heap points to it, so
- it is GC'd. Here is an example:
- </p>
-
- <pre class="indented">
- (define (bad-idea)
- (let* ((lst '(1 2 3))
- (result (list-ref lst 1)))
- (list-set! lst 1 (* 2.0 16.6))
- (gc)
- result))
- </pre>
-
- <p>Put this is a file, load it into the interpreter, then call <code>(bad-idea)</code> a
- few times. You can turn off the optimization in question by setting the variable <code>(*s7* 'safety)</code>
- to 1. <code>(*s7* 'safety)</code> defaults to 0.
- </p>
-
- <p>A similar problem arises when you want to walk a function's source or reuse a piece of
- code directly. When the function is evaluated, the optimizer changes the program source
- to speed up subsequent evaluation. This annotation process means that nothing in that
- source is what it appears to be, so a tree walker will be confused, and if you copy that
- source and try to insert it into some other program source, the existing annotations
- will not fit the new context. In both cases, you can get a clean version of the code
- by copying it with :readable as the second argument to copy. There is an example
- in lint.scm, and in the snd file tools/tgen.scm.
- </p>
-
- </div>
- </details>
-
-
- <details id="circle">
- <summary class="indented">circular lists</summary>
- <div class="indented">
-
- <p>s7 handles circular lists and vectors and dotted lists with its customary aplomb.
- You can pass them to memq, or print them, for example; you can even evaluate them.
- The print syntax is borrowed from CL:
- </p>
-
- <pre class="indented">
- > (let ((lst (list 1 2 3)))
- (set! (cdr (cdr (cdr lst))) lst)
- lst)
- <em class="gray">#1=(1 2 3 . #1#)</em>
- > (let* ((x (cons 1 2))
- (y (cons 3 x)))
- (list x y))
- <em class="gray">(#1=(1 . 2) (3 . #1#))</em>
- </pre>
-
- <p id="circularlistreader">
- But should this syntax be readable as well? I'm inclined to say no because
- then it is part of the language, and it doesn't look like the rest of the language.
- (I think it's kind of ugly). Perhaps we could implement it via *#readers*:
- </p>
-
- <pre>
- (define circular-list-reader
- (let ((known-vals #f)
- (top-n -1))
- (lambda (str)
-
- (define (replace-syms lst)
- ;; walk through the new list, replacing our special keywords
- ;; with the associated locations
-
- (define (replace-sym tree getter)
- (if (keyword? (getter tree))
- (let ((n (string->number (symbol->string (keyword->symbol (getter tree))))))
- (if (integer? n)
- (let ((lst (assoc n known-vals)))
- (if lst
- (set! (getter tree) (cdr lst))
- (format *stderr* "#~D# is not defined~%" n)))))))
-
- (let walk-tree ((tree (cdr lst)))
- (if (pair? tree)
- (begin
- (if (pair? (car tree)) (walk-tree (car tree)) (replace-sym tree car))
- (if (pair? (cdr tree)) (walk-tree (cdr tree)) (replace-sym tree cdr))))
- tree))
-
- ;; str is whatever followed the #, first char is a digit
- (let* ((len (length str))
- (last-char (str (- len 1))))
- (and (memv last-char '(#\= #\#)) ; is it #n= or #n#?
- (let ((n (string->number (substring str 0 (- len 1)))))
- (and (integer? n)
- (begin
- (if (not known-vals) ; save n so we know when we're done
- (begin
- (set! known-vals ())
- (set! top-n n)))
-
- (if (char=? last-char #\=) ; #n=
- (and (eqv? (peek-char) #\() ; eqv? since peek-char can return #<eof>
- (let ((cur-val (assoc n known-vals)))
- ;; associate the number and the list it points to
- ;; if cur-val, perhaps complain? (#n# redefined)
- (let ((lst (catch #t
- read
- (lambda args ; a read error
- (set! known-vals #f) ; so clear our state
- (apply throw args))))) ; and pass the error on up
- (if cur-val
- (set! (cdr cur-val) lst)
- (set! known-vals
- (cons (set! cur-val (cons n lst)) known-vals))))
-
- (if (= n top-n) ; replace our special keywords
- (let ((result (replace-syms cur-val)))
- (set! known-vals #f) ; '#1=(#+gsl #1#) -> '(:1)!
- result)
- (cdr cur-val))))
- ; #n=<not a list>?
- ;; else it's #n# — set a marker for now since we may not
- ;; have its associated value yet. We use a symbol name that
- ;; string->number accepts.
- (symbol->keyword
- (symbol (number->string n) (string #\null) " "))))))
- ; #n<not an integer>?
- ))))) ; #n<something else>?
-
- (do ((i 0 (+ i 1)))
- ((= i 10))
- ;; load up all the #n cases
- (set! *#readers*
- (cons (cons (integer->char (+ i (char->integer #\0))) circular-list-reader)
- *#readers*)))
- <!-- ) -->
- > '#1=(1 2 . #1#)
- <em class="gray">#1=(1 2 . #1#)</em>
- > '#1=(1 #2=(2 . #2#) . #1#)
- <em class="gray">#2=(1 #1=(2 . #1#) . #2#)</em>
- </pre>
-
- <p>And of course, we can treat these as labels:
- </p>
-
- <pre class="indented">
- (let ((ctr 0)) #1=(begin (format () "~D " ctr) (set! ctr (+ ctr 1)) (if (< ctr 4) #1# (newline))))
- </pre>
-
- <p>which prints "0 1 2 3" and a newline.
- </p>
-
- <br>
-
-
- <p>Length returns +inf.0 if passed a circular list, and returns a negative
- number if passed a dotted list. In the dotted case, the absolute value of the length is the list length not counting
- the final cdr. <code>(define (circular? lst) (infinite? (length lst)))</code>.
- </p>
-
- <p>
- <em class=def id="cyclicsequences">cyclic-sequences</em> returns a list of the cyclic
- sequences in its argument, or nil.
- <code>(define (cyclic? obj) (pair? (cyclic-sequences obj)))</code>.
- </p>
-
- <p>Here's an amusing use of circular lists:
- </p>
-
- <pre class="indented">
- (define (for-each-permutation func vals)
- ;; apply func to every permutation of vals:
- ;; (for-each-permutation (lambda args (format () "~{~A~^ ~}~%" args)) '(1 2 3))
- (define (pinner cur nvals len)
- (if (= len 1)
- (apply func (car nvals) cur)
- (do ((i 0 (+ i 1))) ; I suppose a named let would be more Schemish
- ((= i len))
- (let ((start nvals))
- (set! nvals (cdr nvals))
- (let ((cur1 (cons (car nvals) cur))) ; add (car nvals) to our arg list
- (set! (cdr start) (cdr nvals)) ; splice out that element and
- (pinner cur1 (cdr start) (- len 1)) ; pass a smaller circle on down, "wheels within wheels"
- (set! (cdr start) nvals)))))) ; restore original circle
- (let ((len (length vals)))
- (set-cdr! (list-tail vals (- len 1)) vals) ; make vals into a circle
- (pinner () vals len)
- (set-cdr! (list-tail vals (- len 1)) ()))) ; restore its original shape
- </pre>
- </div>
- </details>
-
-
- <details>
- <summary class="indented">unprintable symbols</summary>
- <div class="indented">
-
- <p>s7 and Snd use "*" in a variable name, *features* for example, to indicate
- that the variable is predefined. It may occur unprotected in a macro, for
- example. The "*" doesn't mean that the variable is special in the CL sense of dynamic scope,
- but some clear marker is needed for a global variable so that the programmer
- doesn't accidentally step on it.
- </p>
-
- <p>Although a variable name's first character is more restricted, currently
- only #\null, #\newline, #\tab, #\space, #\), #\(, #\", and #\; can't
- occur within the name. I did not originally include double-quote in this set, so wild stuff like
- <code>(let ((nam""e 1)) nam""e)</code>
- would work, but that means that <code>'(1 ."hi")</code> is parsed as a 1 and the
- symbol <code>."hi"</code>, and <code>(string-set! x"hi")</code> is an error.
- The first character should not be #\#, #\', #\`, #\,, #\:, or any of those mentioned above,
- and some characters can't occur by themselves. For example, "." is not a legal variable
- name, but ".." is.
- These weird symbols have to be printed sometimes:
- </p>
-
- <pre class="indented">
- > (list 1 (string->symbol (string #\; #\" #\\)) 2)
- <em class="gray">(1 ;"\ 2)</em> <!-- " -->
- > (list 1 (string->symbol (string #\.)) 2)
- <em class="gray">(1 . 2)</em>
- </pre>
-
- <p>which is a mess. Guile prints the first as <code>(1 #{\;\"\\}# 2)</code>.
- In CL and some Schemes:
- </p>
-
- <pre class="indented">
- [1]> (list 1 (intern (coerce (list #\; #\" #\\) 'string)) 2) ; thanks to Rob Warnock
- <em class="gray">(1 |;"\\| 2)</em> <!-- " -->
- [2]> (equalp 'A '|A|) ; in CL case matters here
- <em class="gray">T</em>
- </pre>
-
- <p>This is clean, and has the weight of tradition behind it, but
- I think I'll use "symbol" instead:
- </p>
-
- <pre class="indented">
- > (list 1 (string->symbol (string #\; #\" #\\)) 2)
- <em class="gray">(1 (symbol ";\"\\") 2)</em> <!-- " -->
- </pre>
-
- <p>
- This output is readable, and does not eat up perfectly good
- characters like vertical bar, but it means we can't easily use
- variable names like "| e t c |". We could allow a name to
- contain any characters if it starts and ends with "|",
- but then one vertical bar is trouble. (The symbol function
- actually accepts any number of string arguments which it concatenates
- to form the new symbol name).
- </p>
-
- <p>
- These symbols are not just an optimization of string comparison:
- </p>
-
- <pre class="indented">
- > (define-macro (hi a)
- (let ((funny-name (string->symbol ";")))
- `(let ((,funny-name ,a)) (+ 1 ,funny-name))))
- <em class="gray">hi</em>
- > (hi 2)
- <em class="gray">3</em>
- > (macroexpand (hi 2))
- <em class="gray">(let ((; 2)) (+ 1 ;))</em> ; for a good time, try (string #\")
-
- > (define-macro (hi a)
- (let ((funny-name (string->symbol "| e t c |")))
- `(let ((,funny-name ,a)) (+ 1 ,funny-name))))
- <em class="gray">hi</em>
- > (hi 2)
- <em class="gray">3</em>
- > (macroexpand (hi 2))
- <em class="gray">(let ((| e t c | 2)) (+ 1 | e t c |))</em>
- > (let ((funny-name (string->symbol "| e t c |"))) ; now use it as a keyword arg to a function
- (apply define* `((func (,funny-name 32)) (+ ,funny-name 1)))
- ;; (procedure-source func) is (lambda* ((| e t c | 32)) (+ | e t c | 1))
- (apply func (list (symbol->keyword funny-name) 2)))
- <em class="gray">3</em>
- </pre>
-
- <p>I hope that makes you as happy as it makes me!
- </p>
- </div>
- </details>
-
-
-
- <div class="indented">
-
- <p id="legolambda">The built-in syntactic forms, such as "begin", are almost first-class citizens.
- </p>
-
- <pre class="indented">
- > (let ((progn begin))
- (progn
- (define x 1)
- (set! x 3)
- (+ x 4)))
- <em class="gray">7</em>
- > (let ((function lambda))
- ((function (a b) (list a b)) 3 4))
- <em class="gray">(3 4)</em>
- > (apply begin '((define x 3) (+ x 2)))
- <em class="gray">5</em>
- > ((lambda (n) (apply n '(((x 1)) (+ x 2)))) let)
- <em class="gray">3</em>
-
- (define-macro (symbol-set! var val) ; like CL's set
- `(apply set! ,var ',val ())) ; trailing nil is just to make apply happy — apply*?
-
- (define-macro (progv vars vals . body)
- `(apply (apply lambda ,vars ',body) ,vals))
-
- > (let ((s '(one two)) (v '(1 2))) (progv s v (+ one two)))
- <em class="gray">3</em>
- </pre>
-
- <p>We can snap together program fragments ("look Ma, no macros!"):
- </p>
-
- <pre class="indented">
- (let* ((x 3)
- (arg '(x))
- (body `((+ ,x x 1))))
- ((apply lambda arg body) 12)) ; "legolambda"?
-
- (define (engulph form)
- (let ((body `(let ((L ()))
- (do ((i 0 (+ i 1)))
- ((= i 10) (reverse L))
- (set! L (cons ,form L))))))
- (define function (apply lambda () (list (copy body :readable))))
- (function)))
-
- (let ()
- (define (hi a) (+ a x))
- ((apply let '((x 32)) (list (procedure-source hi))) 12)) ; one function, many closures?
-
- (let ((ctr -1)) ; (enum zero one two) but without using a macro
- (apply begin
- (map (lambda (symbol)
- (set! ctr (+ ctr 1))
- (list 'define symbol ctr)) ; e.g. '(define zero 0)
- '(zero one two)))
- (+ zero one two))
- </pre>
-
- <p>But there's a prettier way to implement enum ("transparent-for-each"):
- </p>
-
- <pre class="indented">
- > (define-macro (enum . args)
- `(for-each define ',args (iota (length ',args))))
- <em class="gray">enum</em>
- > (enum a b c)
- <em class="gray">#<unspecified></em>
- > b
- <em class="gray">1</em>
- </pre>
-
-
- <p><code>(apply define ...)</code> is similar to CL's set.
- </p>
-
- <pre class="indented">
- > ((apply define-macro '((m a) `(+ 1 ,a))) 3)
- <em class="gray">4</em>
- > ((apply define '((hi a) (+ a 1))) 3)
- <em class="gray">4</em>
- </pre>
-
- <p>This gives us a way to make anonymous macros, just as lambda returns an anonymous function:
- </p>
-
- <pre class="indented">
- > (define-macro (mu args . body)
- `(apply define-macro '((,(gensym) ,@args) ,@body)))
- <em class="gray">mu</em>
- > ((mu (a) `(+ 1 ,a)) 3)
- <em class="gray">4</em>
- > (define-macro (glambda args) ; returns an anonymous macro that will return a function given a body
- `(define-macro (,(gensym) . body)
- `(lambda ,',args ,@body)))
- <em class="gray">glambda</em>
- > (let ((gf (glambda (a b)))) ; gf is now ready for any body that involves arguments 'a and 'b
- ((gf (+ a b)) 1 2)) ; apply (lambda (a b) (+ a b)) to '(1 2)
- <em class="gray">3</em>
- </pre>
-
- <p>catch, dynamic-wind, and many of the other functions that take function
- arguments in standard Scheme, accept macros in s7.
- </p>
-
- <p>Apply let is very similar to eval:
- </p>
- <pre>
- > (apply let '((a 2) (b 3)) '((+ a b)))
- <em class="gray">5</em>
- > (eval '(+ a b) (inlet 'a 2 'b 3))
- <em class="gray">5</em>
- > ((apply lambda '(a b) '((+ a b))) 2 3)
- <em class="gray">5</em>
- > (apply let '((a 2) (b 3)) '((list + a b))) ; a -> 2, b -> 3
- <em class="gray">(+ 2 3)</em>
- </pre>
- <p>The redundant-looking double lists are for apply's benefit. We could
- use a trailing null instead (mimicking apply* in some ancient lisps):
- </p>
- <pre>
- > (apply let '((a 2) (b 3)) '(list + a b) ())
- <em class="gray">(+ 2 3)</em>
- </pre>
-
- <p>
- Currently, you can't set! a built-in syntactic keyword to some new value:
- <code>(set! if 3)</code>.
- I hope this kind of thing is not actually very useful, but let me
- know if you need it. The issue is purely one of speed.
- </p>
-
- <p>Speaking of speed... It is widely believed
- that a Scheme with first class everything can't hope to compete with any
- "real" Scheme. Humph I say. Take this little example (which is not
- so misleading that I feel guilty about it):
- </p>
- <pre class="indented">
- (define (do-loop n)
- (do ((i 0 (+ i 1)))
- ((= i n))
- (if (zero? (modulo i 1000))
- (display ".")))
- (newline))
-
- (for-each
- (lambda (n) (do-loop n))
- (list 1000 1000000 10000000))
- </pre>
-
- <p>In s7, that takes 0.24 seconds on my home machine. In tinyScheme, from
- whence we sprang, it takes 85 seconds. In the chicken interpreter, 5.3
- seconds, and after compilation (using -O2) of the chicken compiler output,
- 0.75 seconds. So, s7 is comparable to chicken in speed, even though chicken
- is compiling to C. I think Guile 2.0.9 takes about 1 second.
- The equivalent in CL:
- clisp interpreted 9.3 seconds, compiled 0.85 seconds; sbcl 0.21 seconds.
- </p>
- </div>
-
-
-
-
- <div class="indented">
-
- <p>In s7, there is only one kind of begin statement,
- and it can contain both definitions and expressions. These are evaluated in the order
- in which they occur, and in the environment at the point of the evaluation. I think
- of it as being a little REPL. begin does not introduce a new frame in
- the current environment, so defines happen in the enclosing environment.
- </p>
- </div>
-
-
- <div class="indented">
-
- <p id="r7rs">The r7rs compatibility code is in r7rs.scm. I used to include it here, but
- as r7rs grew, this section got too large. In general, all the conversion routines in
- r7rs are handled in s7 via generic functions, records are classes, byte-vectors are
- strings, and so on.
- </p>
- </div>
-
-
-
- <details>
- <summary class="indented">threads</summary>
- <div class="indented">
-
- <p>s7 originally had multithreading support, but I removed it in August, 2011.
- It turned out to be less useful than I hoped,
- mainly because s7 threads shared the heap and therefore had to coordinate
- all cell allocations. It was faster and simpler to use multiple
- processes each running a separate s7 interpreter, rather than one s7
- running multiple s7 threads. In CLM, there was also contention for access
- to the output stream. In GUI-related situations,
- threads were not useful mainly because the GUI toolkits are not thread safe.
- Last but not least, the effort to make the non-threaded
- s7 faster messed up parts of the threaded version. Rather than
- waste a lot of time fixing this, I chose to flush multithreading.
- Here's a very simple example of using an s7 interpreter per thread:
- </p>
-
- <pre class="indented">
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <pthread.h>
- #include "s7.h"
-
- typedef struct {
- s7_scheme *sc;
- s7_pointer func;
- pthread_t *thread;
- } thred;
-
- static void *run_thread(void *obj)
- {
- thred *f = (thred *)obj;
- return((void *)s7_call(f->sc, f->func, s7_nil(f->sc)));
- }
-
- static thred *make_thread(s7_function func)
- {
- thred *f;
- f = (thred *)malloc(sizeof(thred));
- f->sc = s7_init();
- f->func = s7_make_function(f->sc, "a-test", func, 0, 0, false, "a test");
- f->thread = (pthread_t *)malloc(sizeof(pthread_t));
- pthread_create(f->thread, NULL, run_thread, (void *)f);
- return(f);
- }
-
- static s7_pointer a_test(s7_scheme *sc, s7_pointer args)
- {
- fprintf(stderr, "I am %p\n", sc);
- /* do something time-consuming... */
- return(args);
- }
-
- int main(int argc, char **argv)
- {
- thred *f1, *f2;
- f1 = make_thread(a_test);
- f2 = make_thread(a_test);
-
- pthread_join(*(f1->thread), NULL);
- pthread_join(*(f2->thread), NULL);
- }
-
- /* build s7 with -DWITH_THREADS, then
- * gcc -o repl repl.c s7.o -g3 -Wl,-export-dynamic -lpthread -lm -I. -ldl
- */
- </pre>
-
- <p>Unfortunately, there's no way yet to
- free all the resources s7_init allocates (the heap, stack, etc).
- </p>
-
- </div>
- </details>
-
- <div class="indented">
-
- <p>"Life", a poem.
- </p>
-
- <pre class="indented">
- (+(*(+))(*)(+(+)(+)(*)))
- (((((lambda () (lambda () (lambda () (lambda () 1))))))))
- (+ (((lambda () values)) 1 2 3))
- (map apply (list map) (list map) (list (list *)) '((((1 2)) ((3 4 5)))))
- (do ((do do do)) (do do do))
- (*(*)(*) (+)(+) 1)
- </pre>
-
- </div>
-
- </blockquote>
- <br><br>
-
-
-
- <div class="topheader" id="FFIexamples">FFI examples</div>
-
- <p>s7 exists only to serve as an extension of some other application, so
- it is primarily a foreign function interface. s7.h has lots of comments about the individual
- functions. Here I'll collect some complete examples. s7.c depends on the following
- compile-time flags:
- </p>
-
- <pre class="indented">
- SIZEOF_VOID_P 8 (default) or 4.
- WITH_GMP 1 if you want multiprecision arithmetic (requires gmp, mpfr, and mpc, default is 0)
- HAVE_COMPLEX_NUMBERS 1 if your compiler supports complex numbers
- HAVE_COMPLEX_TRIG 1 if your math library has complex versions of the trig functions
- DISABLE_DEPRECATED 1 if you want to make sure you're not using any deprecated s7 stuff (default is 0)
-
- WITH_IMMUTATBLE_UNQUOTE 1 if you want "unquote" omitted (default is 0)
- WITH_EXTRA_EXPONENT_MARKERS 1 if you want "d", "f", "l", and "s" in addition to "e" as exponent markers (default is 0)
- if someone defends these exponent markers, ask him to read 1l11+11l1i
- (in 2 million lines of open-source Scheme, there is not one use of these silly things)
- WITH_SYSTEM_EXTRAS 1 if you want some additional OS-related functions built-in (default is 0)
- WITH_MAIN 1 if you want s7.c to include a main program section that runs a REPL.
- WITH_C_LOADER 1 if you want to be able to load shared object files with load.
- </pre>
-
- <p>See the comment at the start of s7.c for more information about these switches.
- s7.h defines the two main number types: s7_int and s7_double.
- The examples that follow show:
- </p>
-
- <ul>
- <li><a href="#repl">read-eval-print loop (and emacs)</a>
- <li><a href="#defun">define a function with arguments and a returned value, and define a variable </a>
- <li><a href="#defvar">call a Scheme function from C, and get/set Scheme variable values in C</a>
- <li><a href="#juce">C++ and Juce</a>
- <li><a href="#sndlib">load sndlib using the Xen functions and macros</a>
- <li><a href="#pwstype">add a new Scheme type and a procedure with a setter</a>
- <li><a href="#functionportexample">redirect display output to a C procedure</a>
- <li><a href="#extendop">extend a built-in operator ("+" in this case)</a>
- <li><a href="#definestar1">C-side define* (s7_define_function_star)</a>
- <li><a href="#definemacro1">C-side define-macro (s7_define_macro)</a>
- <li><a href="#definegeneric">define a generic function in C</a>
- <li><a href="#signal">signal handling (C-C to break out of an infinite loop)</a>
- <li><a href="#vector">direct multidimensional vector element access</a>
- <li><a href="#notify">notification in C that a Scheme variable has been set!</a>
- <li><a href="#namespace">Load C defined stuff into a separate namespace</a>
- <li><a href="#Cerrors">Error handling in C</a>
- <li><a href="#testhook">Hooks in C and Scheme</a>
- <li><a href="#dload">Load a C module dynamically</a>
- <li><a href="#gmpex">gmp and friends</a>
- <li><a href="#glistener">glistener.c</a>
- <li><a href="#gdb">gdb</a>
- </ul>
-
-
-
-
-
- <div class="header" id="repl"><h4>A simple listener</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include "s7.h"
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = <em class=red>s7_init</em>(); /* initialize the interpreter */
- while (1) /* fire up a read-eval-print loop */
- {
- fprintf(stdout, "\n> "); /* prompt for input */
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- { /* evaluate the input and print the result */
- sprintf(response, "(write %s)", buffer);
- <em class=red>s7_eval_c_string</em>(s7, response);
- }
- }
- }
-
- /* make mus-config.h (it can be empty), then
- *
- * gcc -c s7.c -I.
- * gcc -o repl repl.c s7.o -lm -I. -ldl
- *
- * run it:
- *
- * repl
- * > (+ 1 2)
- * <em class="gray">3</em>
- * > (define (add1 x) (+ 1 x))
- * <em class="gray">add1</em>
- * > (add1 2)
- * <em class="gray">3</em>
- * > (exit)
- *
- * for long-term happiness in linux use:
- * gcc -o repl repl.c s7.o -Wl,-export-dynamic -lm -I. -ldl
- * freebsd:
- * gcc -o repl repl.c s7.o -Wl,-export-dynamic -lm -I.
- * osx:
- * gcc -o repl repl.c s7.o -lm -I.
- * openbsd:
- * gcc -o repl repl.c s7.o -I. -ftrampolines -Wl,-export-dynamic -lm
- */
- </pre>
- </div>
-
-
- <p>Since this reads stdin and writes stdout, it can be run as a Scheme subjob of emacs.
- One (inconvenient) way to do this is to set the emacs variable scheme-program-name to
- the name of the exectuable created above ("doc7"), then call the emacs function run-scheme:
- M-x eval-expression in emacs, followed by (setq scheme-program-name "doc7"), then
- M-x run-scheme, and you're talking to s7 in emacs. Of course, this connection can be
- customized indefinitely. See, for example, inf-snd.el in the Snd package.
- </p>
-
- <p>Here are the not-always-built-in indentations I use in emacs:
- </p>
- <pre class="indented">
- (put 'with-let 'scheme-indent-function 1)
- (put 'with-baffle 'scheme-indent-function 0)
- (put 'with-sound 'scheme-indent-function 1)
- (put 'catch 'scheme-indent-function 1)
- (put 'lambda* 'scheme-indent-function 1)
- (put 'when 'scheme-indent-function 1)
- (put 'let-temporarily 'scheme-indent-function 1)
- (put 'let*-temporarily 'scheme-indent-function 1)
- (put 'call-with-input-string 'scheme-indent-function 1)
- (put 'unless 'scheme-indent-function 1)
- (put 'letrec* 'scheme-indent-function 1)
- (put 'sublet 'scheme-indent-function 1)
- (put 'varlet 'scheme-indent-function 1)
- </pre>
-
- <p>To read stdin while working in a GUI-based program is trickier. In glib/gtk, you can use
- something like this:
- </p>
-
- <blockquote>
- <div class="indented">
- <pre>
- static gboolean read_stdin(GIOChannel *source, GIOCondition condition, gpointer data)
- {
- /* here read from g_io_channel_unix_get_fd(source) and call s7_eval_string */
- return(true);
- }
-
- /* ... during initialization ... */
-
- GIOChannel *channel;
- channel = g_io_channel_unix_new(STDIN_FILENO); /* watch stdin */
- stdin_id = g_io_add_watch_full(channel, /* and call read_stdin above if input is noticed */
- G_PRIORITY_DEFAULT,
- (GIOCondition)(G_IO_IN | G_IO_HUP | G_IO_ERR),
- <em class=red>read_stdin</em>, NULL, NULL);
- g_io_channel_unref(channel);
- </pre></div>
- </blockquote>
-
- <details>
- <summary class="indented">repl with libtecla</summary>
- <p>Here's a version that uses libtecla for the line editor:
- </p>
-
- <blockquote>
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <libtecla.h>
- #include "s7.h"
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char *buffer;
- char response[1024];
- GetLine *gl; /* The tecla line editor */
-
- gl = new_GetLine(500, 5000);
- s7 = s7_init();
-
- while (1)
- {
- buffer = gl_get_line(gl, "> ", NULL, 0);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- fprintf(stdout, "\n");
- }
- }
- gl = del_GetLine(gl);
- }
-
- /*
- * gcc -c s7.c -I. -O2 -g3
- * gcc -o ex1 ex1.c s7.o -lm -I. -ltecla -ldl
- */
- </pre></div>
- </blockquote>
- </details>
-
- <p>A repl (based on repl.scm) is built into s7. Include the compiler flag -DWITH_MAIN:
- </p>
-
- <pre class="indented">
- in Linux: gcc s7.c -o repl -DWITH_MAIN -I. -O2 -g -ldl -lm -Wl,-export-dynamic
- in *BSD: gcc s7.c -o repl -DWITH_MAIN -I. -O2 -g -lm -Wl,-export-dynamic
- in OSX: gcc s7.c -o repl -DWITH_MAIN -I. -O2 -g -lm
- </pre>
-
-
- <details>
- <summary class="indented">repl in C++</summary>
- <p>If you prefer C++, here's a C++ version of the listener, extracted from Rick Taube's
- Common Music package:
- </p>
-
- <blockquote>
- <div class="indented">
- <pre>
- #include <iostream>
- #include "s7.h"
-
- static s7_pointer main_quit(s7_scheme *sc, s7_pointer args);
- static bool is_balanced(std::string str);
- static bool is_not_white(std::string str);
-
- int main(int argc, const char* argv[])
- {
- s7_scheme* s7 = s7_init();
- s7_pointer val;
- std::string str;
-
- try
- {
- while (std::cin)
- {
- std::cout << "\ns7> ";
- str = "";
- while (true)
- {
- std::string lin;
- std::getline(std::cin, lin);
- str = str + lin + "\n";
- if (is_balanced(str))
- break;
- }
- if (is_not_white(str))
- {
- val = s7_eval_c_string(s7, str.c_str());
- std::cout << s7_object_to_c_string(s7, val);
- }
- }
- }
- catch(...)
- {
- }
- std::cout << "Bye!\n";
- return 0;
- }
-
- static s7_pointer main_quit(s7_scheme *sc, s7_pointer args)
- {
- throw 0;
- return(s7_nil(sc));
- }
-
- static bool is_balanced(std::string str)
- {
- int parens = 0;
- int quotes = 0;
- unsigned i = 0;
- while (i < str.size())
- {
- if (str[i] == ';')
- {
- for (i = i + 1; i < str.size(); i++)
- {
- if (str[i] == '\n')
- break;
- }
- }
- else if (str[i] == '"')
- {
- if (i == 0 || str[i - 1] != '\\')
- {
- quotes = 1;
- for (i = i + 1; i < str.size(); i++)
- {
- if (str[i] == '"' && str[i - 1] != '\\')
- {
- quotes = 0;
- break;
- }
- }
- if (quotes)
- return false;
- }
- }
- else if (str[i] == '(')
- parens++;
- else if (str[i] == ')')
- parens--;
- i++;
- }
- return (parens == 0) && (quotes == 0);
- }
-
- static bool is_not_white(std::string str)
- {
- for (unsigned i = 0; (i < str.size() && str[i] != ';'); i++)
- if (str[i] != ' ' && str[i] != '\n' && str[i] != '\t')
- return true;
- return false;
- }
-
- /* g++ -I. -c repl.cpp
- * g++ -o repl repl.o s7.o -ldl
- */
- </pre></div>
- </blockquote>
- </details>
-
-
- <p id="beginhook">
- Common Lisp has something called "evalhook" that makes it possible
- to insert your own function into the eval loop. In s7, we have a "begin_hook" which sits at the opening of many begin blocks
- (implicit or explicit). begin_hook is a (C) function;
- if it sets its bool argument to true,
- s7 interrupts the current evaluation.
- Here is a version of the REPL in which begin_hook watches for C-g to interrupt
- some long computation:
- </p>
-
- <blockquote>
- <div class="indented">
- <pre>
- /* terminal-based REPL,
- * an expansion of the <a href="#repl">read-eval-print loop</a> program above.
- * type C-g to interrupt an evaluation.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <termios.h>
- #include <signal.h>
-
- #include "s7.h"
-
- static struct termios save_buf, buf;
-
- static void sigcatch(int n)
- {
- /* put things back the way they were */
- tcsetattr(fileno(stdin), TCSAFLUSH, &save_buf);
- exit(0);
- }
-
- static char buffer[512];
- static int type_ahead_point = 0;
-
- static void <em class=red>watch_for_c_g</em>(s7_scheme *sc, bool *all_done)
- {
- char c;
- /* watch for C-g without blocking, save other chars as type-ahead */
- tcsetattr(fileno(stdin), TCSAFLUSH, &buf);
- if (read(fileno(stdin), &c, 1) == 1)
- {
- if (c == 7) /* C-g */
- {
- *all_done = true;
- type_ahead_point = 0;
- }
- else buffer[type_ahead_point++] = c;
- }
- tcsetattr(fileno(stdin), TCSAFLUSH, &save_buf);
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- bool use_begin_hook;
-
- use_begin_hook = (tcgetattr(fileno(stdin), &save_buf) >= 0);
- if (use_begin_hook)
- {
- buf = save_buf;
- buf.c_lflag &= ~ICANON;
- buf.c_cc[VMIN] = 0;
- buf.c_cc[VTIME] = 0;
-
- signal(SIGINT, sigcatch);
- signal(SIGQUIT, sigcatch);
- signal(SIGTERM, sigcatch);
- }
- s7 = s7_init();
-
- if (argc == 2)
- {
- fprintf(stderr, "load %s\n", argv[1]);
- s7_load(s7, argv[1]);
- }
- else
- {
- char response[1024];
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets((char *)(buffer + type_ahead_point), 512 - type_ahead_point, stdin);
- type_ahead_point = 0;
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
-
- if (use_begin_hook)
- <em class=red>s7_set_begin_hook</em>(s7, watch_for_c_g);
- s7_eval_c_string(s7, response);
- if (use_begin_hook)
- <em class=red>s7_set_begin_hook</em>(s7, NULL);
- }
- }
- }
- if (use_begin_hook)
- tcsetattr(fileno(stdin), TCSAFLUSH, &save_buf);
- }
- </pre></div>
- </blockquote>
-
-
-
-
- <div class="header" id="defun"><h4>Define a function with arguments and a returned value, and a variable</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer add1(s7_scheme *sc, s7_pointer args)
- {
- /* all added functions have this form, args is a list,
- * s7_car(args) is the first arg, etc
- */
- if (<em class=red>s7_is_integer</em>(s7_car(args)))
- return(<em class=red>s7_make_integer</em>(sc, 1 + <em class=red>s7_integer</em>(s7_car(args))));
- return(s7_wrong_type_arg_error(sc, "add1", 1, s7_car(args), "an integer"));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
-
- s7_define_function(s7, "add1", add1, 1, 0, false, "(add1 int) adds 1 to int");
- /* add the function "add1" to the interpreter.
- * 1, 0, false -> one required arg,
- * no optional args,
- * no "rest" arg
- */
- <em class=red>s7_define_variable</em>(s7, "my-pi", <em class=red>s7_make_real</em>(s7, 3.14159265));
-
- while (1) /* fire up a "repl" */
- {
- fprintf(stdout, "\n> "); /* prompt for input */
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response); /* evaluate input and write the result */
- }
- }
- }
-
- /* doc7
- * > my-pi
- * <em class="gray">3.14159265</em>
- * > (+ 1 (add1 1))
- * <em class="gray">3</em>
- * > (exit)
- */
- </pre></div>
-
-
-
-
- <div class="header" id="defvar"><h4>Call a Scheme-defined function from C, and get/set Scheme variable values in C</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- s7 = s7_init();
-
- s7_define_variable(s7, "an-integer", s7_make_integer(s7, 1));
- s7_eval_c_string(s7, "(define (add1 a) (+ a 1))");
-
- fprintf(stderr, "an-integer: %lld\n",
- s7_integer(<em class=red>s7_name_to_value</em>(s7, "an-integer")));
-
- <em class=red>s7_symbol_set_value</em>(s7, <em class=red>s7_make_symbol</em>(s7, "an-integer"), s7_make_integer(s7, 32));
-
- fprintf(stderr, "now an-integer: %lld\n",
- s7_integer(<em class=red>s7_name_to_value</em>(s7, "an-integer")));
-
- fprintf(stderr, "(add1 2): %lld\n",
- s7_integer(<em class=red>s7_call</em>(s7,
- s7_name_to_value(s7, "add1"),
- s7_cons(s7, s7_make_integer(s7, 2), s7_nil(s7)))));
- }
-
- /*
- * doc7
- * an-integer: 1
- * now an-integer: 32
- * (add1 2): 3
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="juce"><h4>C++ and Juce, from Rick Taube</h4></div>
-
-
- <div class="indented">
- <pre>
- int main(int argc, const char* argv[])
- {
- initialiseJuce_NonGUI();
-
- s7_scheme *s7 = s7_init();
- if (!s7)
- {
- std::cout << "Can't start S7!\n";
- return -1;
- }
-
- s7_pointer val;
- std::string str;
- while (true)
- {
- std::cout << "\ns7> ";
- std::getline(std::cin, str);
- val = s7_eval_c_string(s7, str.c_str());
- std::cout << s7_object_to_c_string(s7, val);
- }
-
- free(s7);
- std::cout << "Bye!\n";
- return 0;
- }
- </pre></div>
-
-
-
-
-
- <div class="header" id="sndlib"><h4>Load sndlib into an s7 repl</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
-
- /* assume we've configured and built sndlib, so it has created a mus-config.h file.
- * also assume we've built s7 with WITH_SYSTEM_EXTRAS set, so we have file-exists? and delete-file
- */
-
- #include "mus-config.h"
- #include "s7.h"
- #include "xen.h"
- #include "clm.h"
- #include "clm2xen.h"
-
- /* we need to redirect clm's mus_error calls to s7_error */
-
- static void mus_error_to_s7(int type, char *msg)
- {
- s7_error(s7, /* s7 is declared in xen.h, defined in xen.c */
- s7_make_symbol(s7, "mus-error"),
- s7_cons(s7, s7_make_string(s7, msg), s7_nil(s7)));
- }
-
- int main(int argc, char **argv)
- {
- char buffer[512];
- char response[1024];
-
- s7 = s7_init(); /* initialize the interpreter */
- s7_xen_initialize(s7); /* initialize the xen stuff (hooks and the xen s7 FFI used by sndlib) */
- Init_sndlib(); /* initialize sndlib with all the functions linked into s7 */
-
- mus_error_set_handler(mus_error_to_s7); /* catch low-level errors and pass them to s7-error */
-
- while (1) /* fire up a "repl" */
- {
- fprintf(stdout, "\n> "); /* prompt for input */
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response); /* evaluate input and write the result */
- }
- }
- }
-
- /* gcc -o doc7 doc7.c -lm -I. /usr/local/lib/libsndlib.a -lasound -ldl
- *
- * (load "sndlib-ws.scm")
- * (with-sound () (outa 10 .1))
- * (load "v.scm")
- * (with-sound () (fm-violin 0 .1 440 .1))
- *
- * you might also need -lgsl -lgslcblas -lfftw3
- */
- </pre>
- </div>
-
- <p>If you built libsndlib.so, it is possible to use it directly in the s7 repl:
- </p>
- <pre>
- repl ; this is a bare s7 running repl.scm via -DWITH_MAIN=1
- loading libc_s7.so
- > (load "/home/bil/test/sndlib/libsndlib.so" (inlet 'init_func 's7_init_sndlib))
- #t ; s7_init_sndlib ties all the sndlib functions and variables into s7
- > (load "sndlib-ws.scm")
- tmpnam
- > (set! *clm-player* (lambda (file) (system (format #f "sndplay ~A" file))))
- > (load "v.scm")
- fm-violin
- > (with-sound (:play #t) (fm-violin 0 1 440 .1))
- "test.snd"
- </pre>
-
- <p>You can use autoload to load libsndlib when needed:
- </p>
-
- <pre class="indented">
- (define (find-library name)
- (if (or (file-exists? name)
- (char=? (name 0) #\/))
- name
- (call-with-exit
- (lambda (return)
- (for-each
- (lambda (path)
- (let ((new-name (string-append path "/" name)))
- (if (file-exists? new-name)
- (return new-name))))
- *load-path*)
- (let ((libs (getenv "LD_LIBRARY_PATH")) ; colon separated directory names
- (start 0))
- (do ((colon (char-position #\: libs) (char-position #\: libs start)))
- ((or (not colon)
- (let ((new-name (string-append (substring libs start colon) "/" name)))
- (and (file-exists? new-name)
- (return new-name)))))
- (set! start (+ colon 1))))
- name))))
-
- (<em class=red>autoload</em> 'clm
- (lambda (e)
- (load (find-library "libsndlib.so") (inlet '(init_func . s7_init_sndlib)))
- (set! *features* (cons 'clm *features*))
- (with-let (rootlet) (define clm #t))
- (load "sndlib-ws.scm")
- (set! *clm-player* (lambda (file) (system (format #f "sndplay ~A" file))))))
- </pre>
-
- <p>and use the repl's vt100 stuff to (for example) post the current begin time
- as a note list computes:
- </p>
-
- <pre class="indented">
- (define (clm-notehook . args)
- ;; assume second arg is begin time (first is instrument name)
- (when (and (pair? args)
- (pair? (cdr args))
- (number? (cadr args)))
- (with-let (sublet (*repl* 'repl-let) :begin-time (cadr args))
- (let ((coords (cursor-coords))
- (col (floor (/ last-col 2))))
- (let ((str (number->string begin-time)))
- (format *stderr* "~C[~D;~DH" #\escape prompt-row col)
- (format *stderr* "~C[K~A" #\escape (if (> (length str) col) (substring str 0 (- col 1)) str)))
- (format *stderr* "~C[~D;~DH" #\escape (cdr coords) (car coords))))))
-
- (set! *clm-notehook* clm-notehook)
- </pre>
-
-
-
-
-
- <div class="header" id="pwstype"><h4>Add a new Scheme type and a procedure with a setter</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- /* define *listener-prompt* in scheme, add two accessors for C get/set */
-
- static const char *listener_prompt(s7_scheme *sc)
- {
- return(s7_string(s7_name_to_value(sc, "*listener-prompt*")));
- }
-
- static void set_listener_prompt(s7_scheme *sc, const char *new_prompt)
- {
- s7_symbol_set_value(sc, s7_make_symbol(sc, "*listener-prompt*"), s7_make_string(sc, new_prompt));
- }
-
- /* now add a new type, a struct named "dax" with two fields, a real "x" and a list "data" */
- /* since the data field is an s7 object, we'll need to mark it to protect it from the GC */
-
- typedef struct {
- s7_double x;
- s7_pointer data;
- } dax;
-
- static char *print_dax(s7_scheme *sc, void *val)
- {
- char *data_str, *str;
- int data_str_len;
- dax *o = (dax *)val;
- data_str = s7_object_to_c_string(sc, o->data);
- data_str_len = strlen(data_str);
- str = (char *)calloc(data_str_len + 32, sizeof(char));
- snprintf(str, data_str_len + 32, "#<dax %.3f %s>", o->x, data_str);
- free(data_str);
- return(str);
- }
-
- static void free_dax(void *val)
- {
- if (val) free(val);
- }
-
- static bool equal_dax(void *val1, void *val2)
- {
- return(val1 == val2);
- }
-
- static void mark_dax(void *val)
- {
- dax *o = (dax *)val;
- if (o) s7_mark_object(o->data);
- }
-
- static int dax_type_tag = 0;
-
- static s7_pointer make_dax(s7_scheme *sc, s7_pointer args)
- {
- dax *o;
- o = (dax *)malloc(sizeof(dax));
- o->x = s7_real(s7_car(args));
- if (s7_cdr(args) != s7_nil(sc))
- o->data = s7_cadr(args);
- else o->data = s7_nil(sc);
- return(<em class=red>s7_make_object</em>(sc, dax_type_tag, (void *)o));
- }
-
- static s7_pointer is_dax(s7_scheme *sc, s7_pointer args)
- {
- return(s7_make_boolean(sc,
- <em class=red>s7_is_object</em>(s7_car(args)) &&
- <em class=red>s7_object_type</em>(s7_car(args)) == dax_type_tag));
- }
-
- static s7_pointer dax_x(s7_scheme *sc, s7_pointer args)
- {
- dax *o;
- o = (dax *)<em class=red>s7_object_value</em>(s7_car(args));
- return(s7_make_real(sc, o->x));
- }
-
- static s7_pointer set_dax_x(s7_scheme *sc, s7_pointer args)
- {
- dax *o;
- o = (dax *)s7_object_value(s7_car(args));
- o->x = s7_real(s7_cadr(args));
- return(s7_cadr(args));
- }
-
- static s7_pointer dax_data(s7_scheme *sc, s7_pointer args)
- {
- dax *o;
- o = (dax *)s7_object_value(s7_car(args));
- return(o->data);
- }
-
- static s7_pointer set_dax_data(s7_scheme *sc, s7_pointer args)
- {
- dax *o;
- o = (dax *)s7_object_value(s7_car(args));
- o->data = s7_cadr(args);
- return(o->data);
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
-
- s7_define_variable(s7, "*listener-prompt*", s7_make_string(s7, ">"));
-
- dax_type_tag = <em class=red>s7_new_type</em>("dax", print_dax, free_dax, equal_dax, mark_dax, NULL, NULL);
- s7_define_function(s7, "make-dax", make_dax, 2, 0, false, "(make-dax x data) makes a new dax");
- s7_define_function(s7, "dax?", is_dax, 1, 0, false, "(dax? anything) returns #t if its argument is a dax object");
-
- s7_define_variable(s7, "dax-x",
- <em class=red>s7_dilambda</em>(s7, "dax-x", dax_x, 1, 0, set_dax_x, 2, 0, "dax x field"));
-
- s7_define_variable(s7, "dax-data",
- <em class=red>s7_dilambda</em>(s7, "dax-data", dax_data, 1, 0, set_dax_data, 2, 0, "dax data field"));
-
- while (1)
- {
- fprintf(stdout, "\n%s ", listener_prompt(s7));
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response); /* evaluate input and write the result */
- }
- }
- }
-
- /*
- * > *listener-prompt*
- * <em class="gray">">"</em>
- * > (set! *listener-prompt* ":")
- * <em class="gray">":"</em>
- * : (define obj (make-dax 1.0 (list 1 2 3)))
- * <em class="gray">obj</em>
- * : obj
- * <em class="gray">#<dax 1.000 (1 2 3)></em>
- * : (dax-x obj)
- * <em class="gray">1.0</em>
- * : (dax-data obj)
- * <em class="gray">(1 2 3)</em>
- * : (set! (dax-x obj) 123.0)
- * <em class="gray">123.0</em>
- * : obj
- * <em class="gray">#<dax 123.000 (1 2 3)></em>
- * : (dax? obj)
- * <em class="gray">#t</em>
- * : (exit)
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="functionportexample"><h4>Redirect output (and input) to a C procedure</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static void my_print(s7_scheme *sc, unsigned char c, s7_pointer port)
- {
- fprintf(stderr, "[%c] ", c);
- }
-
- static s7_pointer my_read(s7_scheme *sc, s7_read_t peek, s7_pointer port)
- {
- return(<em class=red>s7_make_character</em>(sc, fgetc(stdin)));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
-
- <em class=red>s7_set_current_output_port</em>(s7, <em class=red>s7_open_output_function</em>(s7, my_print));
- s7_define_variable(s7, "io-port", <em class=red>s7_open_input_function</em>(s7, my_read));
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * > (+ 1 2)
- * <em class="gray">[3]</em>
- * > (display "hiho")
- * <em class="gray">[h] [i] [h] [o] [#] [<] [u] [n] [s] [p] [e] [c] [i] [f] [i] [e] [d] [>] </em>
- * > (define (add1 x) (+ 1 x))
- * <em class="gray">[a] [d] [d] [1] </em>
- * > (add1 123)
- * <em class="gray">[1] [2] [4] </em>
- * > (read-char io-port)
- * a ; here I typed "a" in the shell
- * <em class="gray">[#] [\] [a] </em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="extendop"><h4>Extend a built-in operator ("+" in this case)</h4></div>
-
- <p>There are several ways to do this. In the first example, we save the original function,
- and replace it with ours, calling the original whenever possible:
- </p>
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer old_add; /* the original "+" function for non-string cases */
- static s7_pointer old_string_append; /* same, for "string-append" */
-
- static s7_pointer our_add(s7_scheme *sc, s7_pointer args)
- {
- /* this will replace the built-in "+" operator, extending it to include strings:
- * (+ "hi" "ho") -> "hiho" and (+ 3 4) -> 7
- */
- if ((s7_is_pair(args)) &&
- (s7_is_string(s7_car(args))))
- return(<em class=red>s7_apply_function</em>(sc, old_string_append, args));
- return(s7_apply_function(sc, old_add, args));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
- s7 = s7_init();
-
- /* get built-in + and string-append */
- old_add = s7_name_to_value(s7, "+");
- old_string_append = s7_name_to_value(s7, "string-append");
-
- /* redefine "+" */
- s7_define_function(s7, "+", our_add, 0, 0, true, "(+ ...) adds or appends its arguments");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /* > (+ 1 2)
- * <em class="gray">3</em>
- * > (+ "hi" "ho")
- * <em class="gray">"hiho"</em>
- */
- </pre></div>
-
- <p>In the next example, we use the method (inlet) machinery:
- </p>
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
-
- #include "s7.h"
-
- static s7_pointer our_abs(s7_scheme *sc, s7_pointer args)
- {
- s7_pointer x;
- x = s7_car(args);
- if (!s7_is_number(x))
- {
- s7_pointer method;
- method = <em class=red>s7_method</em>(sc, x, s7_make_symbol(sc, "abs"));
- if (method == s7_undefined(sc)) /* no method found, so raise an error */
- s7_wrong_type_arg_error(sc, "abs", 1, x, "a real");
- return(s7_apply_function(sc, method, args)); /* else apply the method to the args */
- }
- return(s7_make_real(sc, (s7_double)fabs(s7_number_to_real(sc, x))));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
- s7_define_function(s7, "our-abs", our_abs, 1, 0, false, "abs replacement");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /* > (our-abs -1)
- * <em class="gray">1.0</em>
- * > (our-abs (openlet (inlet 'value -3.0 'abs (lambda (x) (abs (x 'value))))))
- * <em class="gray">3.0</em>
- */
-
- </pre>
- </div>
-
-
-
- <div class="header" id="definestar1"><h4>C-side define* (s7_define_function_star)</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer plus(s7_scheme *sc, s7_pointer args)
- {
- /* (define* (plus (red 32) blue) (+ (* 2 red) blue)) */
- return(s7_make_integer(sc, 2 * s7_integer(s7_car(args)) + s7_integer(s7_cadr(args))));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
- <em class=red>s7_define_function_star</em>(s7, "plus", plus, "(red 32) blue", "an example of define* from C");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * > (plus 2 3)
- * <em class="gray">7</em>
- * > (plus :blue 3)
- * <em class="gray">67</em>
- * > (plus :blue 1 :red 4)
- * <em class="gray">9</em>
- * > (plus 2 :blue 3)
- * <em class="gray">7</em>
- * > (plus :blue 3 :red 1)
- * <em class="gray">5</em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="definemacro1"><h4>C-side define-macro (s7_define_macro)</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer plus(s7_scheme *sc, s7_pointer args)
- {
- /* (define-macro (plus a b) `(+ ,a ,b)) */
- s7_pointer a, b;
- a = s7_car(args);
- b = s7_cadr(args);
- return(s7_list(sc, 3, s7_make_symbol(sc, "+"), a, b));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
- <em class=red>s7_define_macro</em>(s7, "plus", plus, 2, 0, false, "plus adds its two arguments");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * > (plus 2 3)
- * <em class="gray">5</em>
- */
- </pre></div>
-
-
-
- <div class="header" id="definegeneric"><h4>define a generic function in C</h4></div>
-
- <p>In scheme, a function becomes generic simply by <code>(apply ((car args) 'func) args)</code>.
- To accomplish the same thing in C, we use s7_method and s7_apply_function:
- </p>
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer plus(s7_scheme *sc, s7_pointer args)
- {
- #define plus_help "(plus obj ...) applies obj's plus method to obj and any trailing arguments."
- s7_pointer obj, method;
- obj = s7_car(args);
- method = <em class=red>s7_method</em>(sc, obj, s7_make_symbol(sc, "plus"));
- if (s7_is_procedure(method))
- return(<em class=red>s7_apply_function</em>(sc, method, args));
- return(s7_f(sc));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- s7 = s7_init();
- s7_define_function(s7, "plus", plus, 1, 0, true, plus_help);
- while (1)
- {
- char buffer[512];
- char response[1024];
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /* gcc -c s7.c -I.
- * gcc -o ex15 ex15.c s7.o -I. -lm -ldl
- *
- * > (plus 1 2)
- * <em class="gray">#f</em>
- * > (define obj (openlet (inlet 'plus (lambda args (apply + 1 (cdr args))))))
- * <em class="gray">obj</em>
- * > (plus obj 2 3)
- * <em class="gray">6</em>
- */
- </pre>
- </div>
-
-
-
- <div class="header" id="signal"><h4>Signal handling and continuations</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <signal.h>
-
- #include "s7.h"
-
- static s7_scheme *s7;
- struct sigaction new_act, old_act;
-
- static void handle_sigint(int ignored)
- {
- fprintf(stderr, "interrupted!\n");
- s7_symbol_set_value(s7, s7_make_symbol(s7, "*interrupt*"), <em class=red>s7_make_continuation</em>(s7)); /* save where we were interrupted */
- sigaction(SIGINT, &new_act, NULL);
- s7_quit(s7); /* get out of the eval loop if possible */
- }
-
- static s7_pointer our_sleep(s7_scheme *sc, s7_pointer args)
- {
- /* slow down our infinite loop for demo purposes */
- sleep(1);
- return(s7_f(sc));
- }
-
- int main(int argc, char **argv)
- {
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
- s7_define_function(s7, "sleep", our_sleep, 0, 0, false, "(sleep) sleeps");
- s7_define_variable(s7, "*interrupt*", s7_f(s7));
- /* Scheme variable *interrupt* holds the continuation at the point of the interrupt */
-
- sigaction(SIGINT, NULL, &old_act);
- if (old_act.sa_handler != SIG_IGN)
- {
- memset(&new_act, 0, sizeof(new_act));
- new_act.sa_handler = &handle_sigint;
- sigaction(SIGINT, &new_act, NULL);
- }
-
- while (1)
- {
- fprintf(stderr, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * > (do ((i 0 (+ i 1))) ((= i -1)) (format () "~D " i) (sleep))
- * ;;; now type C-C to break out of this loop
- * 0 1 2 ^Cinterrupted!
- * ;;; call the continuation to continue from where we were interrupted
- * > (*interrupt*)
- * 3 4 5 ^Cinterrupted!
- * > *interrupt*
- * #<continuation>
- * > (+ 1 2)
- * 3
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="vector"><h4>Multidimensional vector element access</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdarg.h>
-
- #include "s7.h"
-
- static s7_pointer multivector_ref(s7_scheme *sc, s7_pointer vector, int indices, ...)
- {
- /* multivector_ref returns an element of a multidimensional vector */
- int ndims;
- ndims = <em class=red>s7_vector_rank</em>(vector);
-
- if (ndims == indices)
- {
- va_list ap;
- s7_int index = 0;
- va_start(ap, indices);
-
- if (ndims == 1)
- {
- index = va_arg(ap, s7_int);
- va_end(ap);
- return(s7_vector_ref(sc, vector, index));
- }
- else
- {
- int i;
- s7_pointer *elements;
- s7_int *offsets, *dimensions;
-
- elements = <em class=red>s7_vector_elements</em>(vector);
- dimensions = <em class=red>s7_vector_dimensions</em>(vector);
- offsets = <em class=red>s7_vector_offsets</em>(vector);
-
- for (i = 0; i < indices; i++)
- {
- int ind;
- ind = va_arg(ap, int);
- if ((ind < 0) ||
- (ind >= dimensions[i]))
- {
- va_end(ap);
- return(s7_out_of_range_error(sc,
- "multivector_ref", i,
- s7_make_integer(sc, ind),
- "index should be between 0 and the dimension size"));
- }
- index += (ind * offsets[i]);
- }
- va_end(ap);
- return(elements[index]);
- }
- }
- return(s7_wrong_number_of_args_error(sc,
- "multivector_ref: wrong number of indices: ~A",
- s7_make_integer(sc, indices)));
- }
-
- int main(int argc, char **argv)
- {
- char buffer[512];
- char response[1024];
- s7_scheme *s7;
-
- s7 = s7_init();
- s7_eval_c_string(s7, "(define vect (make-vector '(2 3 4) 0))");
- s7_eval_c_string(s7, "(set! (vect 1 1 1) 32)");
-
- fprintf(stdout, "vect[0,0,0]: %s, vect[1,1,1]: %s\n",
- s7_object_to_c_string(s7, <em class=red>multivector_ref</em>(s7, s7_name_to_value(s7, "vect"), 3, 0, 0, 0)),
- s7_object_to_c_string(s7, <em class=red>multivector_ref</em>(s7, s7_name_to_value(s7, "vect"), 3, 1, 1, 1)));
- }
-
- /* vect[0,0,0]: 0, vect[1,1,1]: 32
- */
- </pre>
- </div>
-
- <p>Much later... I decided to add s7_vector_ref_n and s7_vector_set_n to s7.
- </p>
-
-
-
-
-
-
- <div class="header" id="notify"><h4>Notification from Scheme that a given Scheme variable has been set</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer scheme_set_notification(s7_scheme *sc, s7_pointer args)
- {
- /* this function is called when the Scheme variable is set! */
- fprintf(stderr, "%s set to %s\n",
- s7_object_to_c_string(sc, s7_car(args)),
- s7_object_to_c_string(sc, s7_cadr(args)));
- return(s7_cadr(args));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- s7 = s7_init();
-
- s7_define_function(s7, "notify-C", scheme_set_notification, 2, 0, false, "called if notified-var is set!");
- s7_define_variable(s7, "notified-var", s7_make_integer(s7, 0));
- <em class=red>s7_symbol_set_access</em>(s7, s7_make_symbol(s7, "notified-var"), s7_name_to_value(s7, "notify-C"));
-
- if (argc == 2)
- {
- fprintf(stderr, "load %s\n", argv[1]);
- s7_load(s7, argv[1]);
- }
- else
- {
- char buffer[512];
- char response[1024];
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
- }
-
- /* > notified-var
- * <em class="gray">0</em>
- * > (set! notified-var 32)
- * <em class="gray">notified-var set to 32</em>
- * <em class="gray">32</em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="namespace"><h4>Load C defined stuff into a separate namespace</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer func1(s7_scheme *sc, s7_pointer args)
- {
- return(s7_make_integer(sc, s7_integer(s7_car(args)) + 1));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- s7_pointer new_env;
-
- s7 = s7_init();
-
- /* "func1" and "var1" will be placed in an anonymous environment,
- * accessible from Scheme via the global variable "lib-exports"
- */
-
- new_env = <em class=red>s7_inlet</em>(s7, s7_curlet(s7), s7_nil(s7));
- /* make a private environment for func1 and var1 below (this is our "namespace") */
- s7_gc_protect(s7, new_env);
-
- s7_define(s7, <em class=red>new_env</em>,
- s7_make_symbol(s7, "func1"),
- <em class=red>s7_make_function</em>(s7, "func1", func1, 1, 0, false, "func1 adds 1 to its argument"));
-
- s7_define(s7, <em class=red>new_env</em>, s7_make_symbol(s7, "var1"), s7_make_integer(s7, 32));
- /* those two symbols are now defined in the new environment */
-
- /* add "lib-exports" to the global environment */
- s7_define_variable(s7, "lib-exports", <em class=red>s7_let_to_list</em>(s7, new_env));
-
- if (argc == 2)
- {
- fprintf(stderr, "load %s\n", argv[1]);
- s7_load(s7, argv[1]);
- }
- else
- {
- char buffer[512];
- char response[1024];
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
- }
-
- /* > func1
- * <em class="gray">;func1: unbound variable, line 1</em>
- * > lib-exports
- * <em class="gray">((var1 . 32) (func1 . func1))</em>
- * ;; so lib-exports has the C-defined names and values
- * ;; we can use these directly:
- *
- * > (define lib-env (apply <em class=red>sublet</em> (curlet) lib-exports))
- * <em class="gray">lib-env</em>
- * > (<em class=red>with-let</em> lib-env (func1 var1))
- * <em class="gray">33</em>
- *
- * ;; or rename them to prepend "lib:"
- * > (define lib-env (apply sublet
- (curlet)
- (map (lambda (binding)
- (cons (string->symbol
- (string-append "lib:" (symbol->string (car binding))))
- (cdr binding)))
- lib-exports)))
- * <em class="gray">lib-env</em>
- * > (with-let lib-env (lib:func1 lib:var1))
- * <em class="gray">33</em>
- *
- * ;;; now for convenience, place "func1" in the global environment under the name "func2"
- * > (define func2 (cdadr lib-exports))
- * <em class="gray">func2</em>
- * > (func2 1)
- * <em class="gray">2</em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="Cerrors"><h4>Handle scheme errors in C</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer error_handler(s7_scheme *sc, s7_pointer args)
- {
- fprintf(stdout, "error: %s\n", s7_string(s7_car(args)));
- return(s7_f(sc));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
- bool with_error_hook = false;
-
- s7 = s7_init();
- s7_define_function(s7, "error-handler", error_handler, 1, 0, false, "our error handler");
-
- if (with_error_hook)
- s7_eval_c_string(s7, "(set! (hook-functions *error-hook*) \n\
- (list (lambda (hook) \n\
- (error-handler \n\
- (apply format #f (hook 'data))) \n\
- (set! (hook 'result) 'our-error))))");
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- s7_pointer old_port, result;
- int gc_loc = -1;
- const char *errmsg = NULL;
-
- /* trap error messages */
- old_port = s7_set_current_error_port(s7, s7_open_output_string(s7));
- if (old_port != s7_nil(s7))
- gc_loc = s7_gc_protect(s7, old_port);
-
- /* evaluate the input string */
- result = s7_eval_c_string(s7, buffer);
-
- /* print out the value wrapped in "{}" so we can tell it from other IO paths */
- fprintf(stdout, "{%s}", s7_object_to_c_string(s7, result));
-
- /* look for error messages */
- errmsg = s7_get_output_string(s7, s7_current_error_port(s7));
-
- /* if we got something, wrap it in "[]" */
- if ((errmsg) && (*errmsg))
- fprintf(stdout, "[%s]", errmsg);
-
- s7_close_output_port(s7, s7_current_error_port(s7));
- s7_set_current_error_port(s7, old_port);
- if (gc_loc != -1)
- s7_gc_unprotect_at(s7, gc_loc);
- }
- }
- }
-
- /*
- * gcc -c s7.c -I. -g3
- * gcc -o ex3 ex3.c s7.o -lm -I. -ldl
- *
- * if with_error_hook is false,
- *
- * > (+ 1 2)
- * <em class="gray">{3}</em>
- * > (+ 1 #\c)
- * <em class="gray">{wrong-type-arg}[</em>
- * <em class="gray">;+ argument 2, #\c, is character but should be a number, line 1</em>
- * ]
- *
- * so s7 by default prepends ";" to the error message, and appends "\n",
- * sending that to current-error-port, and the error type ('wrong-type-arg here)
- * is returned.
- *
- * if with_error_hook is true,
- *
- * > (+ 1 2)
- * <em class="gray">{3}</em>
- * > (+ 1 #\c)
- * <em class="gray">error: + argument 2, #\c, is character but should be a number</em>
- * <em class="gray">{our-error}</em>
- *
- * so now the *error-hook* code handles both the error reporting and
- * the value returned ('our-error in this case).
- */
- </pre></div>
-
-
-
- <div class="header" id="testhook"><h4>C and Scheme hooks</h4></div>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- static s7_pointer my_hook_function(s7_scheme *sc, s7_pointer args)
- {
- fprintf(stderr, "a is %s\n", s7_object_to_c_string(sc, s7_symbol_local_value(sc, s7_make_symbol(sc, "a"), s7_car(args))));
- return(s7_car(args));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
- s7_pointer test_hook;
-
- s7 = s7_init();
-
- /* define test_hook in C, test-hook in Scheme, arguments are named a and b */
- test_hook = <em class=red>s7_eval_c_string</em>(s7, "(make-hook 'a 'b)");
- s7_define_constant(s7, "test-hook", test_hook);
-
- /* add my_hook_function to the test_hook function list */
- <em class=red>s7_hook_set_functions</em>(s7, test_hook,
- s7_cons(s7,
- s7_make_function(s7, "my-hook-function", my_hook_function, 1, 0, false, "my hook-function"),
- s7_hook_functions(s7, test_hook)));
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * > test-hook
- * <em class="gray">#<lambda (hook)></em>
- * > (hook-functions test-hook)
- * <em class="gray">(my-hook-function)</em>
- * > (test-hook 1 2)
- * <em class="gray">a is 1</em>
- * <em class="gray">#<unspecified></em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="dload"><h4>Load a shared library</h4></div>
-
- <p>We can use dlopen to load a shared library, and dlsym to initialize
- that library in our main program. The tricky part is to conjure up the right
- compiler and loader flags.
- First we define a module that defines a new s7 function, add-1 that we'll tie
- into s7 explicitly, and another
- function that we'll try to call by waving a wand.
- </p>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
-
- double a_function(double an_arg);
- double a_function(double an_arg)
- {
- return(an_arg + 1.0);
- }
-
- static s7_pointer add_1(s7_scheme *sc, s7_pointer args)
- {
- return(s7_make_integer(sc, s7_integer(s7_car(args)) + 1));
- }
-
- void init_ex(s7_scheme *sc);
- void init_ex(s7_scheme *sc) /* this needs to be globally accessible (not "static") */
- {
- /* tell s7 about add-1, but leave a_function hidden */
- s7_define_function(sc, "add-1", add_1, 1, 0, false, "(add-1 x) adds 1 to x");
- }
-
- </pre></div>
-
-
- <p>And here is our main program:
- </p>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "s7.h"
- #include <dlfcn.h>
-
- static void *library = NULL;
-
- static s7_pointer try(s7_scheme *sc, s7_pointer args)
- {
- /* try tries to call an arbitrary function in the shared library */
- void *func;
- func = <em class=red>dlsym</em>(library, s7_string(s7_car(args)));
- if (func)
- {
- /* we'll assume double f(double) */
- typedef double (*dl_func)(double arg);
- return(s7_make_real(sc, ((dl_func)<em class=red>func</em>)(s7_real(s7_cadr(args)))));
- }
- return(s7_error(sc, s7_make_symbol(sc, "can't find function"),
- s7_list(sc, 2, s7_make_string(sc, "loader error: ~S"),
- s7_make_string(sc, dlerror()))));
- }
-
- static s7_pointer cload(s7_scheme *sc, s7_pointer args)
- {
- /* cload loads a shared library */
- #define CLOAD_HELP "(cload so-file-name) loads the module"
- library = dlopen(s7_string(s7_car(args)), RTLD_LAZY);
- if (library)
- {
- /* call our init func to define add-1 in s7 */
- void *init_func;
- init_func = <em class=red>dlsym</em>(library, s7_string(s7_cadr(args)));
- if (init_func)
- {
- typedef void *(*dl_func)(s7_scheme *sc);
- ((dl_func)<em class=red>init_func</em>)(sc); /* call the initialization function (init_ex above) */
- return(s7_t(sc));
- }
- }
- return(s7_error(sc, s7_make_symbol(sc, "load-error"),
- s7_list(sc, 2, s7_make_string(sc, "loader error: ~S"),
- s7_make_string(sc, dlerror()))));
- }
-
- int main(int argc, char **argv)
- {
- char buffer[512];
- char response[1024];
- s7_scheme *s7;
-
- s7 = s7_init();
-
- s7_define_function(s7, "cload", cload, 2, 0, false, CLOAD_HELP);
- s7_define_function(s7, "try", try, 2, 0, false,
- "(try name num) tries to call name in the shared library with the argument num.");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
-
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /* Put the module in the file ex3a.c and the main program in ex3.c, then
- *
- * in Linux:
- * gcc -c -fPIC ex3a.c
- * gcc ex3a.o -shared -o ex3a.so
- * gcc -c s7.c -I. -fPIC -shared
- * gcc -o ex3 ex3.c s7.o -lm -ldl -I. -Wl,-export-dynamic
- * # omit -ldl in freeBSD, openBSD might want -ftrampolines
- *
- * in Mac OSX:
- * gcc -c ex3a.c
- * gcc ex3a.o -o ex3a.so -dynamic -bundle -undefined suppress -flat_namespace
- * gcc -c s7.c -I. -dynamic -bundle -undefined suppress -flat_namespace
- * gcc -o ex3 ex3.c s7.o -lm -ldl -I.
- *
- * and run it:
- * ex3
- * > (cload "/home/bil/snd-16/ex3a.so" "init_ex")
- * <em class="gray">#t</em>
- * > (add-1 2)
- * <em class="gray">3</em>
- * > (try "a_function" 2.5)
- * <em class="gray">3.5</em>
- */
- </pre></div>
-
- <p>All of this is just boring boilerplate, so with a little support from s7,
- we can write a script to do the entire linkage. The s7 side is an extension
- to "load" that loads a shared object file if its extension is "so", and
- runs an initialization function whose name is defined in the load
- environment (the optional second argument to load). An example of the scheme side is cload.scm,
- included in the s7 tarball. It defines a function that can be
- called:
- </p>
-
- <pre class="indented">
- (c-define '(double j0 (double)) "m" "math.h")
- </pre>
-
- <p>This links the s7 function m:j0 to the math library
- function j0. See <a href="#cload">cload.scm</a> for more details.
- </p>
-
-
-
- <div class="header" id="gmpex"><h4>Bignums in C</h4></div>
-
- <p>Bignum support depends on gmp, mpfr, and mpc. In this example, we define "add-1" which adds
- 1 to any kind of number. The s7_big_* functions return the underlying gmp/mpfr/mpc pointer,
- so we have to copy that into a new number before adding.
- </p>
-
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #include <gmp.h>
- #include <mpfr.h>
- #include <mpc.h>
-
- #include "s7.h"
-
- static s7_pointer big_add_1(s7_scheme *sc, s7_pointer args)
- {
- /* add 1 to either a normal number or a bignum */
- s7_pointer x;
- x = s7_car(args);
- if (s7_is_bignum(x))
- {
- s7_pointer n;
- if (s7_is_integer(x))
- {
- mpz_t *big_n;
- n = s7_make_big_integer(sc, s7_big_integer(x)); /* copy x */
- big_n = s7_big_integer(n); /* get mpz_t pointer of copy */
- mpz_add_ui(*big_n, *big_n, 1); /* add 1 to that */
- return(n); /* return the new bignum */
- }
- if (s7_is_ratio(x))
- {
- mpq_t *big_q;
- mpz_t num, den;
- n = s7_make_big_ratio(sc, s7_big_ratio(x));
- big_q = s7_big_ratio(n);
- mpz_init_set(num, mpq_numref(*big_q));
- mpz_init_set(den, mpq_denref(*big_q));
- mpz_add(num, num, den);
- mpq_set_num(*big_q, num);
- mpz_clear(num);
- mpz_clear(den);
- return(n);
- }
- if (s7_is_real(x))
- {
- mpfr_t *big_x;
- n = s7_make_big_real(sc, s7_big_real(x));
- big_x = s7_big_real(n);
- mpfr_add_ui(*big_x, *big_x, 1, GMP_RNDN);
- return(n);
- }
- /* x must be big complex */
- {
- mpc_t *big_z;
- n = s7_make_big_complex(sc, s7_big_complex(x));
- big_z = s7_big_complex(n);
- mpc_add_ui(*big_z, *big_z, 1, MPC_RNDNN);
- return(n);
- }
- }
- else
- {
- if (s7_is_integer(x))
- return(s7_make_integer(sc, 1 + s7_integer(x)));
- if (s7_is_rational(x))
- return(s7_make_ratio(sc, s7_numerator(x) + s7_denominator(x), s7_denominator(x)));
- if (s7_is_real(x))
- return(s7_make_real(sc, 1.0 + s7_real(x)));
- if (s7_is_complex(x))
- return(s7_make_complex(sc, 1.0 + s7_real_part(x), s7_imag_part(x)));
- }
- return(s7_wrong_type_arg_error(sc, "add-1", 0, x, "a number"));
- }
-
- int main(int argc, char **argv)
- {
- s7_scheme *s7;
- char buffer[512];
- char response[1024];
-
- s7 = s7_init();
- s7_define_function(s7, "add-1", big_add_1, 1, 0, false, "(add-1 num) adds 1 to num");
-
- while (1)
- {
- fprintf(stdout, "\n> ");
- fgets(buffer, 512, stdin);
- if ((buffer[0] != '\n') ||
- (strlen(buffer) > 1))
- {
- sprintf(response, "(write %s)", buffer);
- s7_eval_c_string(s7, response);
- }
- }
- }
-
- /*
- * gcc -DWITH_GMP=1 -c s7.c -I. -O2 -g3
- * gcc -DWITH_GMP=1 -o ex2 ex2.c s7.o -I. -O2 -lm -ldl -lgmp -lmpfr -lmpc
- *
- * ex2
- * > (add-1 1)
- * <em class="gray">2</em>
- * > (add-1 2/3)
- * <em class="gray">5/3</em>
- * > (add-1 1.4)
- * <em class="gray">2.4</em>
- * > (add-1 1.5+i)
- * <em class="gray">2.5+1i</em>
- * > (add-1 (bignum "3"))
- * <em class="gray">4</em> ; this is the bignum 4
- * > (add-1 (bignum "3/4"))
- * <em class="gray">7/4</em>
- * > (add-1 (bignum "1.4"))
- * <em class="gray">2.399999999999999911182158029987476766109E0</em>
- * > (add-1 (bignum "1.5+i"))
- * <em class="gray">2.500E0+1.000E0i</em>
- */
- </pre></div>
-
-
-
-
-
- <div class="header" id="glistener"><h4>glistener.c</h4></div>
-
- <p>glistener.c is a gtk-based repl. It is not specific to s7:
- Snd uses it as its Forth and Ruby listener as well as for s7.
- Here's a short example:
- </p>
-
- <div class="indented">
- <pre>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdbool.h>
-
- #include <gtk/gtk.h>
-
- #include "s7.h"
- #include "glistener.h"
-
- static s7_scheme *s7;
-
- static gint quit_repl(GtkWidget *w, GdkEvent *event, gpointer context) {exit(0);}
-
- static void evaluator(glistener *g, const char *text)
- {
- /* this sends "text" to s7 for evaluation, then displays the result */
- int gc_loc;
- s7_pointer old_port, result;
- const char *errmsg = NULL;
- char *msg = NULL;
-
- old_port = s7_set_current_error_port(s7, s7_open_output_string(s7));
- gc_loc = s7_gc_protect(s7, old_port);
-
- result = s7_eval_c_string(s7, text);
- errmsg = s7_get_output_string(s7, s7_current_error_port(s7));
- if ((errmsg) && (*errmsg))
- {
- msg = (char *)calloc(strlen(errmsg) + 1, sizeof(char));
- strcpy(msg, errmsg);
- }
-
- s7_close_output_port(s7, s7_current_error_port(s7));
- s7_set_current_error_port(s7, old_port);
- s7_gc_unprotect_at(s7, gc_loc);
-
- glistener_append_text(g, "\n");
- if (msg) /* some error occurred during evaluation */
- glistener_append_text(g, msg);
- else
- { /* evaluation produced an s7 object which we need to display */
- msg = s7_object_to_c_string(s7, result);
- glistener_append_text(g, msg);
- }
- if (msg) free(msg);
- glistener_append_prompt(g); /* prompt for more input */
- }
-
- static void listener_init(glistener *g, GtkWidget *w)
- {
- /* this is the glistener initialization function. "w" above is the new text-view widget,
- * "g" is the new glistener pointer, passed to any function that wants to talk to this
- * listener.
- */
- unsigned char prompt[4] = {0xce, 0xbb, '>', '\0'}; /* lambda as prompt */
- GtkTextBuffer *buffer;
-
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
- glistener_set_font(g, pango_font_description_from_string("Monospace 10"));
-
- /* our prompt will be a red lambda */
- glistener_set_prompt_tag(g, gtk_text_buffer_create_tag(buffer, "glistener_prompt_tag",
- "weight", PANGO_WEIGHT_BOLD,
- "foreground", "red",
- NULL));
- glistener_set_prompt(g, prompt);
- }
-
- static const char *helper(glistener *g, const char *text)
- {
- /* this function is called whenever the listener thinks help is needed.
- * Any string it returns is posted in the listener statusbar.
- */
- s7_pointer sym;
- sym = s7_symbol_table_find_name(s7, text);
- if (sym)
- return(s7_help(s7, sym));
- glistener_clear_status(g);
- return(NULL);
- }
-
- static void completer(glistener *g, bool (*symbol_func)(const char *symbol_name, void *data), void *data)
- {
- /* this function is called when <tab> is typed after a partial symbol name.
- * "symbol_func" above should be called on each member of the symbol-table, passing it
- * the symbol name (as a string) and the data passed as "completer's" third argument.
- * If symbol_func returns true, it is done, so the loop through the symbol-table can stop.
- */
- s7_for_each_symbol_name(s7, symbol_func, data);
- }
-
- int main(int argc, char **argv)
- {
- GtkWidget *shell, *frame;
- glistener *g;
-
- s7 = s7_init();
-
- gtk_init(&argc, &argv);
- shell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- g_signal_connect(G_OBJECT(shell), "delete_event", G_CALLBACK(quit_repl), NULL);
-
- frame = gtk_frame_new(NULL);
- gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
- gtk_widget_show(frame);
-
- gtk_container_add(GTK_CONTAINER(shell), frame);
-
- /* make a new listener */
- g = glistener_new(frame, listener_init);
- glistener_set_evaluator(g, evaluator);
- glistener_set_helper(g, helper);
- glistener_set_completer(g, completer);
-
- gtk_widget_show(shell);
- gdk_window_resize(gtk_widget_get_window(shell), 400, 200);
- gtk_main();
- }
-
- /* in gtk-2: gcc gcall.c -o gcall s7.o glistener.o `pkg-config --libs gtk+-2.0 --cflags` -lm -ldl
- * in gtk-3: gcc gcall.c -o gcall s7.o glistener.o `pkg-config --libs gtk+-3.0 --cflags` -lm -ldl
- */
- </pre>
-
- <div class="listener">
- <pre>
- <em class=redb>λ></em> (define λ lambda)
- <em class="gray">λ</em>
- <em class=redb>λ></em> ((λ (a b) (+ a b)) 1 2)
- <em class="gray">3</em>
- <em class=redb>λ></em>
- </pre>
- </div>
-
- <br>
- <p>The five or six functions supplied by the caller (evaluator, helper,
- completer, checker, colorizer, keyer) all have defaults, so you don't have to supply
- anything but an evaluator. The default evaluator just prints "?" and prompts
- for more input. See glistener.h for the full API and an earnest attempt
- at helpful documentation.
- </p>
-
- <p>A multi-listener test program is the Snd file tools/gcall.c which
- is used by tools/gtest.scm for regression testing. One way to name unicode characters
- is: <code>(define-constant |lambda| #u8(#xce #xbb))</code>. This can be embedded
- in an ordinary s7 string with any string operation: <code>(string-append |lambda| "ambda")</code>
- which returns "λambda". (string-length will still return the number of bytes; to get
- the number of characters in a case like this, use g_utf8_strlen).
- So, to set the prompt to be a red lambda and the font to be "Nimbus mono 10" from Scheme,
- assuming we have the usual Scheme-to-C linkages (see snd-glistener.c):
- </p>
-
- <pre class="indented">
- (set! (listener-prompt) (byte-vector #xce #xbb (char->integer #\>) (char->integer #\space)))
- (set! (listener-font) "Nimbus mono 10")
- (listener-set-prompt-tag *listener* ; ideally this too would be a setter
- (gtk_text_buffer_create_tag
- (GTK_TEXT_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (listener-text-widget *listener*))))
- "" (list "weight" PANGO_WEIGHT_BOLD "foreground" "red")))
- </pre>
-
- <p>In Snd, all the gtk code is in the *gtk* environment, so we need to use:
- </p>
- <pre class="indented">
- (listener-set-prompt-tag *listener*
- (with-let (sublet *gtk* 'textw (listener-text-widget *listener*)) ; use *gtk*
- (gtk_text_buffer_create_tag
- (GTK_TEXT_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW textw)))
- "" (list "weight" PANGO_WEIGHT_BOLD "foreground" "red"))))
- </pre>
- </div>
-
-
- <div class="header" id="gdb"><h4>gdb</h4></div>
-
- <p>It is possible to make a mistake while writing C code.
- I switched from Common Lisp to Scheme a long time ago
- partly because it was so painful to debug FFI troubles in Common Lisp, and I
- chose Guile at that time partly because I thought gdb would have native support
- for it. As far as I know it is still impossible to debug CL FFI troubles, 20 years later!
- And in gdb Python has muscled Guile aside. Anyway, say you have hit a segfault
- and find yourself staring at a stackful of opaque pointers. Print statements are your
- friend, of course, and at the gdb command level, the main one in this context is
- s7_object_to_c_string. Here are some commands (intended for your .gdbinit file)
- that can speed up the process. They assume the s7_scheme pointer is named "sc".
- (These are now included in the gdbinit file in the s7 tarball).
- </p>
-
- <pre class="indented">
- define s7print
- print s7_object_to_c_string(sc, $arg0)
- end
- document s7print
- interpret the argument as an s7 value and display it
- end
- # the current expression is sc->cur_code
- # the current environment is sc->envir
- # the error environment is sc->owlet
- # so for example, to see the current local variables, s7p sc->envir
-
- define s7eval
- print s7_object_to_c_string(sc, s7_eval_c_string(sc, $arg0))
- end
- document s7eval
- eval the argument (a string)
- end
-
- define s7stack
- print s7_object_to_c_string(sc, s7_stacktrace(sc))
- end
- document s7stack
- display the current stack
- end
-
- define s7value
- print s7_object_to_c_string(sc, s7_name_to_value(sc, $arg0))
- end
- document s7value
- print the value of the variable passed by its print name: s7v "*features*"
- end
- </pre>
-
- <p>gdbinit also has s7cell to decode every field of an s7_pointer, and two backtrace
- decoders: s7bt and s7btfull (heh heh). The bt replacements print the gdb backtrace info,
- replacing bare pointer numbers with their s7 value, wherever possible:
- </p>
-
- <pre class="indented">
- #1 0x000000000042104e in find_symbol_unchecked (sc=0x97edf0, symbol=<b>vars</b>) at s7.c:6677
- x = <b>(inlet 'f import-lambda-definition-2)</b>
- __FUNCTION__ = "find_symbol_unchecked"
- #2 0x00000000006e3424 in eval (sc=0x97edf0, first_op=9) at s7.c:63673
- _x_ = <b>import-lambda-definition-2</b>
- _slot_ = <b>'form import-lambda-definition-2</b>
- _sym_ = <b>env</b>
- _val_ = <b>import-lambda-definition-2</b>
- args = <b>(vars)</b>
- p = <b>(env)</b>
- func = <b>lint-walk</b>
- e = <b>(inlet 'name import-lambda-definition-2 'form import-lambda-definition-2)</b>
- code = <b>(lint-walk name f vars)</b>
- __FUNCTION__ = "eval"
- </pre>
-
- <br><br>
-
-
- <div class="topheader" id="s7examples">s7 examples</div>
-
- <p>The s7 tarball includes several scheme files including s7test.scm,
- lint.scm, cload.scm, write.scm, mockery.scm, and stuff.scm.
- s7test.scm is a regression test for s7,
- lint.scm is the s7 equivalent of the ancient C program named lint (modern equivalent: cppcheck),
- write.scm has a pretty printer,
- mockery.scm has mock data libraries,
- cload.scm is a wrapper for the FFI stuff described above, and
- stuff.scm is just some arbitrary stuff.
- gdbinit has some gdb commands for s7.
- repl.scm is a repl.
- profile.scm provides access to profiling data, if it's enabled.
- </p>
-
-
- <div class="header" id="cload"><h4>cload.scm</h4></div>
-
- <p>cload.scm defines the macro c-define that reduces the overhead
- involved in (dynamically) linking C entities into s7.
- </p>
-
- <pre class="indented">
- (<em class=def id="definecfunction">c-define</em> c-info (prefix "") (headers ()) (cflags "") (ldflags "") output-name)
- </pre>
-
- <p>For example, <code>(c-define '(double j0 (double)) "m" "math.h")</code>
- links the C math library function j0 into s7 under the name m:j0,
- passing it a double argument and getting a double result (a real in s7).
- </p>
-
- <p><em>prefix</em> is some arbitrary prefix that you want prepended to various names.
- </p>
-
- <p><em>headers</em> is a list of headers (as strings) that the c-info relies on, (("math.h") for example).
- </p>
-
- <p><em>cflags</em> are any special C compiler flags that are needed ("-I." in particular), and
- <em>ldflags</em> is the similar case for the loader. <em>output-name</em> is the name of the
- output C file and associated library. It defaults to "temp-s7-output" followed by a number.
- In libm.scm, it is set to "libm_s7" to protect it across cload calls. If cload finds an
- up-to-date output C file and shared library, it simply loads the library, rather than
- going through all the trouble of writing and compling it.
- </p>
-
- <p><em>c-info</em> is a list that describes the C entities that you want to load into s7.
- It can be either one list describing one entity, or a list of such lists.
- Each description has the form:
- </p>
-
- <pre class="indented">
- (return-type entity-name-in-C (argument-type...))
- </pre>
-
- <p>where each entry is a symbol, and C names are used throughout. So, in the j0
- example above, <code>(double j0 (double))</code> says we want access to j0, it returns
- a C double, and it takes one argument, also a C double. s7 tries to figure out
- what the corresponding s7 type is, but in tricky cases, you should tell it
- by replacing the bare type name with a list: <code>(C-type underlying-C-type)</code>. For example,
- the Snd function set_graph_style takes an (enum) argument of type graph_style_t.
- This is actually an int, so we use <code>(graph_style_t int)</code> as the type:
- </p>
-
- <pre class="indented">
- (void set_graph_style ((graph_style_t int)))
- </pre>
-
- <p>If the C entity is a constant, then the descriptor list has just two entries,
- the C-type and the entity name: <code>(int F_OK)</code> for example. The entity name can also be a list:
- </p>
-
- <pre class="indented">
- ((graph_style_t int) (GRAPH_LINES GRAPH_DOTS GRAPH_FILLED GRAPH_DOTS_AND_LINES GRAPH_LOLLIPOPS))
- </pre>
-
- <p>This defines all the names in the list as integers.
- If the C type has a space ("struct tm*"), use <code>(symbol "struct tm*")</code>
- to construct the corresponding symbol.
- </p>
-
- <p>The entity is placed in the current s7 environment under the name <code>(string-append prefix ":" name)</code>
- where the ":" is omitted if the prefix is null. So in the j0 example, we get in s7 the function m:j0.
- c-define returns #t if it thinks the load worked, and #f otherwise.
- </p>
-
- <p>There are times when the only straightforward approach is to write the desired
- C code directly. To insert C code on the fly, use (in-C "code..."). Two more such
- cases that come up all the time: C-function for linkage to functions written
- directly in s7 style using in-C, and C-macro for macros in the C header file that
- need to be wrapped in #ifdefs.
- Here are some examples:
- </p>
-
- <pre class="indented">
- ;;; various math library functions
- (c-define '((double j0 (double))
- (double j1 (double))
- (double erf (double))
- (double erfc (double))
- (double lgamma (double)))
- "m" "math.h")
-
-
- ;;; getenv and setenv
- (c-define '(char* getenv (char*)))
- (c-define '(int setenv (char* char* int)))
-
-
- ;;; file-exists? and delete-file
- (define file-exists? (let () ; define F_OK and access only within this let
- (c-define '((int F_OK) (int access (char* int))) "" "unistd.h")
- (lambda (arg) (= (access arg F_OK) 0))))
-
- (define delete-file (let ()
- (c-define '(int unlink (char*)) "" "unistd.h")
- (lambda (file) (= (unlink file) 0)))) ; 0=success
-
-
- ;;; examples from Snd:
- (c-define '(char* version_info ()) "" "snd.h" "-I.")
-
- (c-define '(mus_float_t mus_degrees_to_radians (mus_float_t)) "" "snd.h" "-I.")
-
- (c-define '(snd_info* any_selected_sound ()) "" "snd.h" "-I.")
- (c-define '(void select_channel (snd_info* int)) "" "snd.h" "-I.")
-
- (c-define '(((graph_style_t int) (GRAPH_LINES GRAPH_DOTS GRAPH_FILLED GRAPH_DOTS_AND_LINES GRAPH_LOLLIPOPS))
- (void set_graph_style ((graph_style_t int))))
- "" "snd.h" "-I.")
-
-
- ;;; getcwd, strftime
- (c-define '(char* getcwd (char* size_t)) "" "unistd.h")
-
- (c-define (list '(void* calloc (size_t size_t))
- '(void free (void*))
- '(void time (time_t*)) ; ignore returned value
- (list (symbol "struct tm*") 'localtime '(time_t*))
- (list 'size_t 'strftime (list 'char* 'size_t 'char* (symbol "struct tm*"))))
- "" "time.h")
-
- > (let ((p (calloc 1 8))
- (str (make-string 32)))
- (time p)
- (strftime str 32 "%a %d-%b-%Y %H:%M %Z" (localtime p))
- (free p)
- str)
- <em class="gray">"Sat 11-Aug-2012 08:55 PDT\x00 "</em>
-
-
- ;;; opendir, read_dir, closedir
- (c-define '((int closedir (DIR*))
- (DIR* opendir (char*))
- (in-C "static char *read_dir(DIR *p) \
- { \
- struct dirent *dirp; \
- dirp = readdir(p); \
- if (!dirp) return(NULL); \
- return(dirp->d_name); \
- }")
- (char* read_dir (DIR*)))
- "" '("sys/types.h" "dirent.h"))
-
- (let ((dir (opendir "/home/bil/gtk-snd")))
- (do ((p (read_dir dir) (read_dir dir)))
- ((= (length p) 0))
- (format *stderr* "~A " p))
- (closedir dir))
- </pre>
-
-
- <p>For the simple cases above, include "-ldl -Wl,-export-dynamic" in the gcc command. So the first
- FFI example is built (this is in Linux):
- </p>
-
- <pre class="indented">
- gcc -c s7.c -I.
- gcc -o ex1 ex1.c s7.o -lm -I. -ldl -Wl,-export-dynamic
- ex1
- > (load "cload.scm")
- <em class="gray">c-define-1</em>
- > (c-define '(double j0 (double)) "m" "math.h")
- <em class="gray">#t</em>
- > (m:j0 0.5)
- <em class="gray">0.93846980724081</em>
- </pre>
-
- <p>See also r7rs.scm, libc.scm, libgsl.scm, libm.scm, libdl.scm, and libgdbm.scm.
- libutf8proc.scm exists, but I have not tested it at all.
- </p>
-
- <div class="indented" id="libc">
- <pre>
- (require libc.scm)
-
- (define (copy-file in-file out-file)
- (with-let (sublet *libc* :in-file in-file :out-file out-file)
-
- ;; the rest of the function body exists in the *libc* environment, with the
- ;; function parameters in-file and out-file imported, so, for example,
- ;; (open ...) below calls the libc function open.
-
- (let ((infd (open in-file O_RDONLY 0)))
- (if (= infd -1)
- (error 'io-error "can't find ~S~%" in-file)
- (let ((outfd (creat out-file #o666)))
- (if (= outfd -1)
- (begin
- (close infd)
- (error 'io-error "can't open ~S~%" out-file))
- (let* ((BUF_SIZE 1024)
- (buf (malloc BUF_SIZE)))
- (do ((num (read infd buf BUF_SIZE) (read infd buf BUF_SIZE)))
- ((or (<= num 0)
- (not (= (write outfd buf num) num)))))
- (close outfd)
- (close infd)
- (free buf)
- out-file)))))))
-
- (define (glob->list pattern)
- (with-let (sublet *libc* :pattern pattern)
- (let ((g (glob.make)))
- (glob pattern 0 g)
- (let ((res (glob.gl_pathv g)))
- (globfree g)
- res))))
-
- ;; now (load "*.scm") is (for-each load (glob->list "*.scm"))
- </pre>
- </div>
-
-
- <div class="indented" id="libgsl">
- <pre>
- (require libgsl.scm)
-
- (define (eigenvalues M)
- (with-let (sublet *libgsl* :M M)
- (let* ((len (sqrt (length M)))
- (gm (gsl_matrix_alloc len len))
- (m (float-vector->gsl_matrix M gm))
- (evl (gsl_vector_complex_alloc len))
- (evc (gsl_matrix_complex_alloc len len))
- (w (gsl_eigen_nonsymmv_alloc len)))
-
- (gsl_eigen_nonsymmv m evl evc w)
- (gsl_eigen_nonsymmv_free w)
- (gsl_eigen_nonsymmv_sort evl evc GSL_EIGEN_SORT_ABS_DESC)
-
- (let ((vals (make-vector len)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (set! (vals i) (gsl_vector_complex_get evl i)))
- (gsl_matrix_free gm)
- (gsl_vector_complex_free evl)
- (gsl_matrix_complex_free evc)
- vals))))
- </pre>
- </div>
-
- <p>We can use gdbm (or better yet, mdb), the :readable argument to object->string, and
- the fallback methods in the environments to create name-spaces (lets) with billions of
- thread-safe local variables, which can be saved and communicated between s7 runs:
- </p>
- <div class="indented" id="libgdbm">
- <pre>
- (require libgdbm.scm)
-
- (with-let *libgdbm*
-
- (define *db*
- (openlet
- (inlet :file (gdbm_open "test.gdbm" 1024 GDBM_NEWDB #o664
- (lambda (str) (format *stderr* "gdbm error: ~S~%" str)))
-
- :let-ref-fallback (lambda (obj sym)
- (eval-string (gdbm_fetch (obj 'file) (symbol->string sym))))
-
- :let-set!-fallback (lambda (obj sym val)
- (gdbm_store (obj 'file)
- (symbol->string sym)
- (object->string val :readable)
- GDBM_REPLACE)
- val)
-
- :make-iterator (lambda (obj)
- (let ((key #f)
- (length (lambda (obj) (expt 2 20))))
- (#_make-iterator
- (let ((iterator? #t))
- (openlet
- (lambda ()
- (if key
- (set! key (gdbm_nextkey (obj 'file) (cdr key)))
- (set! key (gdbm_firstkey (obj 'file))))
- (if (pair? key)
- (cons (string->symbol (car key))
- (eval-string (gdbm_fetch (obj 'file) (car key))))
- key))))))))))
-
- (set! (*db* 'str) "123") ; add a variable named 'str with the value "123"
- (set! (*db* 'int) 432)
-
- (with-let *db*
- (+ int (length str))) ; -> 435
- (map values *db*) ; -> '((str . "123") (int . 432))
-
- (gdbm_close (*db* 'file)))
- </pre>
-
- </div>
-
-
-
- <div class="header" id="schemerepl"><h4>repl.scm</h4></div>
-
- <p>repl.scm implements a repl using vt100 codes and libc.scm. It includes
- symbol and filename completion, a history buffer, paren matching,
- indentation, multi-line edits, and a debugger window.
- To move around in the history buffer, use M-p, M-n or M-. (C-p and C-n are used to move the cursor in the current expression).
- You can change the keymap or the prompt; all the repl functions are
- accessible through the *repl* environment. One field is 'repl-let which
- gives you access to all the repl's internal variables and functions.
- Another is 'top-level-let, normally (sublet (rootlet)), which is the environment in
- which the repl's evaluation takes place. You can reset the repl back to its
- starting point with: <code>(set! (*repl* 'top-level-let) (sublet (rootlet)))</code>.
- You can save the current repl state via <code>((*repl* 'save-repl))</code>, and
- restore it later via <code>((*repl* 'restore-repl))</code>. The repl's saved state
- is in the file save.repl, or the filename can be passed as an argument to save-repl and restore-repl.
- The special symbol '** holds the last value.
- </p>
-
- <p>Meta keys are a problem on the Mac. You can use ESC instead, but that requires
- super-human capacities. I stared at replacement control keys, and nothing seemed
- right. If you can think of something, it's easy to define replacements: see repl.scm
- which has a small table of mappings.
- </p>
-
- <p>To run the repl, either build s7 with the compiler flag -DWITH_MAIN,
- or conjure up a wrapper:
- </p>
-
- <pre class="indented">
- #include "s7.h"
-
- int main(int argc, char **argv)
- {
- s7_scheme *sc;
- sc = s7_init();
- s7_load(sc, "repl.scm");
- s7_eval_c_string(sc, "((*repl* 'run))");
- return(0);
- }
-
- /* gcc -o r r.c s7.o -Wl,-export-dynamic -lm -I. -ldl
- */
- </pre>
-
- <p>Besides evaluating s7 expressions, like any repl,
- you can also type shell commands just as in a shell:
- </p>
-
- <pre class="indented">
- > pwd
- <em class="gray">/home/bil/cl</em>
- > cd ..
- <em class="gray">/home/bil</em>
- > date
- <em class="gray">Wed 15-Apr-2015 17:32:24 PDT</em>
- > **
- <em class="gray">"Wed 15-Apr-2015 17:32:24 PDT
- "</em>
- </pre>
-
- <p>In most cases, these are handled through *unbound-variable-hook*, checked using "command -v", then passed
- to the underlying shell via the system function. If s7's (as opposed to libc's) system command
- is accessible, the '** variable holds whatever the command printed.
- </p>
-
- <p>The prompt is set by the function (*repl* 'prompt). It gets one argument,
- the current line number, and should set the prompt string and its length.
- </p>
- <pre class="indented">
- (set! (*repl* 'prompt) (lambda (num)
- (with-let (*repl* 'repl-let)
- (set! prompt-string "scheme> ")
- (set! prompt-length (length prompt-string)))))
- </pre>
- <p>or, to use the red lambda example mentioned earlier:
- </p>
- <pre class="indented">
- (set! (*repl* 'prompt)
- (lambda (num)
- (with-let (*repl* 'repl-let)
- (set! prompt-string (bold (red (string #\xce #\xbb #\> #\space))))
- (set! prompt-length 3)))) ; until we get unicode length calc
- </pre>
-
- <p>The line number provides a quick way to move around in the history buffer.
- To get a previous line without laboriously typing M-p over and over,
- simply type the line number (without control or meta bits), then M-.
- </p>
-
- <p>Here is an example of adding to the keymap:
- </p>
- <pre class="indented">
- (set! ((*repl* 'keymap) (integer->char 17)) ; C-q to quit and return to caller
- (lambda (c)
- (set! ((*repl* 'repl-let) 'all-done) #t)))
- </pre>
-
- <p>To access the meta keys (in the keymap), use a string:
- <code>((*repl* 'keymap) (string #\escape #\p))</code>; this is Meta-p which normally accesses
- the history buffer.
- </p>
-
- <p>You can call the repl from other code, poke around in the current environment (or whatever),
- then return to the caller:
- </p>
-
- <pre class="indented">
- (load "repl.scm")
-
- (define (drop-into-repl e)
- (let ((C-q (integer->char 17))) ; we'll use the C-q example above to get out
- (let ((old-C-q ((*repl* 'keymap) C-q))
- (old-top-level (*repl* 'top-level-let)))
- (dynamic-wind
- (lambda ()
- (set! (*repl* 'top-level-let) e)
- (set! ((*repl* 'keymap) C-q)
- (lambda (c)
- (set! ((*repl* 'repl-let) 'all-done) #t))))
- (lambda ()
- ((<em class=red>*repl* 'run</em>))) ; run the repl
- (lambda ()
- (set! (*repl* 'top-level-let) old-top-level)
- (set! ((*repl* 'keymap) C-q) old-C-q))))))
-
- (let ((x 32))
- (format *stderr* "x: ~A~%" x)
- (<em class=red>drop-into-repl</em> (curlet))
- (format *stderr* "now x: ~A~%" x))
- </pre>
-
- <p>Now load that code and:
- </p>
-
- <pre class="indented">
- x: 32
- > x
- <em class="gray">32</em>
- > (set! x 91)
- <em class="gray">91</em>
- > x
- <em class="gray">91</em>
- > now x: 91 ; here I typed C-q at the prompt
- </pre>
-
- <p>Another possibility:
- </p>
- <pre class="indented">
- (set! (hook-functions *error-hook*)
- (list (lambda (hook)
- (apply format *stderr* (hook 'data))
- (newline *stderr*)
- (drop-into-repl (owlet)))))
- </pre>
-
- <p>See the end of repl.scm for more examples.
- </p>
-
- <!--
- (load "/home/bil/test/sndlib/libsndlib.so" (inlet 'init_func 's7_init_sndlib))
-
- not tested:
- (load "/home/bil/test/libxm/libxm.so" (inlet 'init_func 'Init_libxm))
-
- -->
-
-
- <div class="header" id="lint"><h4>lint.scm</h4></div>
-
- <p>lint tries to find errors or infelicities in your scheme code.
- To try it:
- </p>
-
- <pre class="indented">
- (load "lint.scm")
- (lint "some-code.scm")
- </pre>
-
-
- <p>
- There are several
- variables at the start of lint.scm to control additional output:
- </p>
-
-
- <pre class="indented">
- *report-unused-parameters*
- *report-unused-top-level-functions*
- *report-shadowed-variables*
- *report-undefined-identifiers*
- *report-multiply-defined-top-level-functions*
- *report-nested-if*
- *report-short-branch*
- *report-one-armed-if*
- *report-loaded-files*
- *report-any-!-as-setter*
- *report-doc-strings*
- *report-func-as-arg-arity-mismatch*
- *report-constant-expressions-in-do*
- *report-bad-variable-names*
- *report-built-in-functions-used-as-variables*
- *report-forward-functions*
- *report-sloppy-assoc*
- *report-bloated-arg*
- </pre>
-
- <p>See lint.scm for more about these switches. You can also extend lint by adding your own code,
- or adding your functions to lint's tables, or most simply by defining signatures for your functions.
- snd-lint.scm performs these tasks for Snd. (lint exports its innards via *lint*).
- lint is not smart about functions defined outside the current file, so *report-undefined-variables*
- sometimes gets confused. You'll sometimes get a recommendation from lint that is less than helpful; nobody's perfect.
- If it's actually wrong, and not just wrong-headed, please let me know.
- Also in lint.scm are html-lint and C-lint. html-lint reads an HTML file looking for
- Scheme code. If any is found, it runs s7 and then lint over it, reporting troubles.
- Similarly C-lint reads a C file looking for s7_eval_c_string and running lint over its string.
- </p>
-
-
- <blockquote>
- <div class="indented">
- <p>After months of intense typing,
- Insanely declares his labors complete. "Ship it!" says Mr Big, and hands
- him a million stock options. Meanwhile, in the basement behind an old door
- with the eldritch sign "eep Ou", in a labyrinth of pounding pipes and fluorescent lights,
- a forgotten shadow types <code>(lint "insanely-great.scm")</code>...
- </p>
- </div>
- </blockquote>
-
-
-
- <!-- ================================================================================ -->
-
- <script language=JavaScript>
-
- /** from Remy Sharp at GitHub
- * Note that this script is intended to be included at the *end* of the document, before </body>
- */
- (function (window, document) {
- if ('open' in document.createElement('details')) return;
-
- // made global by myself to be reused elsewhere
- var addEvent = (function () {
- if (document.addEventListener) {
- return function (el, type, fn) {
- if (el && el.nodeName || el === window) {
- el.addEventListener(type, fn, false);
- } else if (el && el.length) {
- for (var i = 0; i < el.length; i++) {
- addEvent(el[i], type, fn);
- }
- }
- };
- } else {
- return function (el, type, fn) {
- if (el && el.nodeName || el === window) {
- el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
- } else if (el && el.length) {
- for (var i = 0; i < el.length; i++) {
- addEvent(el[i], type, fn);
- }
- }
- };
- }
- })();
-
-
- /** details support - typically in it's own script */
- // find the first /real/ node
- function firstNode(source) {
- var node = null;
- if (source.firstChild.nodeName != "#text") {
- return source.firstChild;
- } else {
- source = source.firstChild;
- do {
- source = source.nextSibling;
- } while (source && source.nodeName == '#text');
-
- return source || null;
- }
- }
-
- function isSummary(el) {
- var nn = el.nodeName.toUpperCase();
- if (nn == 'DETAILS') {
- return false;
- } else if (nn == 'SUMMARY') {
- return true;
- } else {
- return isSummary(el.parentNode);
- }
- }
-
- function toggleDetails(event) {
- // more sigh - need to check the clicked object
- var keypress = event.type == 'keypress',
- target = event.target || event.srcElement;
- if (keypress || isSummary(target)) {
- if (keypress) {
- // if it's a keypress, make sure it was enter or space
- keypress = event.which || event.keyCode;
- if (keypress == 32 || keypress == 13) {
- // all's good, go ahead and toggle
- } else {
- return;
- }
- }
-
- var open = this.getAttribute('open');
- if (open === null) {
- this.setAttribute('open', 'open');
- } else {
- this.removeAttribute('open');
- }
-
- // this.className = open ? 'open' : ''; // Lame
- // trigger reflow (required in IE - sometimes in Safari too)
- setTimeout(function () {
- document.body.className = document.body.className;
- }, 13);
-
- if (keypress) {
- event.preventDefault && event.preventDefault();
- return false;
- }
- }
- }
-
- function addStyle() {
- var style = document.createElement('style'),
- head = document.getElementsByTagName('head')[0],
- key = style.innerText === undefined ? 'textContent' : 'innerText';
-
- var rules = ['details{display: block;}','details > *{display: none;}','details.open > *{display: block;}','details[open] > *{display: block;}','details > summary:first-child{display: block;cursor: pointer;}','details[open]{display: block;}'];
- i = rules.length;
-
- style[key] = rules.join("\n");
- head.insertBefore(style, head.firstChild);
- }
-
- var details = document.getElementsByTagName('details'),
- wrapper,
- i = details.length,
- j,
- first = null,
- label = document.createElement('summary');
-
- label.appendChild(document.createTextNode('Details'));
-
- while (i--) {
- first = firstNode(details[i]);
-
- if (first != null && first.nodeName.toUpperCase() == 'SUMMARY') {
- // we've found that there's a details label already
- } else {
- // first = label.cloneNode(true); // cloned nodes weren't picking up styles in IE - random
- first = document.createElement('summary');
- first.appendChild(document.createTextNode('Details'));
- if (details[i].firstChild) {
- details[i].insertBefore(first, details[i].firstChild);
- } else {
- details[i].appendChild(first);
- }
- }
-
- // this feels *really* nasty, but we can't target details :text in css :(
- j = details[i].childNodes.length;
- while (j--) {
- if (details[i].childNodes[j].nodeName === '#text' && (details[i].childNodes[j].nodeValue||'').replace(/\s/g, '').length) {
- wrapper = document.createElement('text');
- wrapper.appendChild(details[i].childNodes[j]);
- details[i].insertBefore(wrapper, details[i].childNodes[j]);
- }
- }
-
- first.legend = true;
- first.tabIndex = 0;
- }
-
- // trigger details in case this being used on it's own
- document.createElement('details');
- addEvent(details, 'click', toggleDetails);
- addEvent(details, 'keypress', toggleDetails);
- addStyle();
-
- })(window, document);
- </script>
-
- </body>
- </html>
-
-
- <!--
- circular env without set!:
- (let ()
- (let ((b (curlet)))
- (curlet)))
-
- circular func (implicit set!):
- (letrec* ((b (lambda () (a)))
- (a (lambda () (b))))
- (a))
-
- (letrec ((b (lambda () (a)))
- (a (lambda () (b))))
- (a))
- -->
|