of

Legitimacy of two Victorian local government elections in question after duplicate votes detected - ABC News

  1. Legitimacy of two Victorian local government elections in question after duplicate votes detected  ABC News
  2. Victorian council election results 2024 LIVE updates: Suspected postal vote tampering in council elections  Sydney Morning Herald
  3. VEC investigates potential vote tampering in two Melbourne councils  The Age




of

Battle Of The Sexes (who gives more?)



Good fundraisers typically stick with what works in a fund raising campaign choosing not to ask to many questions about why it works. While intuitively, many fundraisers know that women tend to make more donations in a given campaign or series of campaigns and perhaps if the fundraiser is intuitive enough, that men who give, generally tend to give higher amounts overall.

It’s not always easy to understand why this is so, and therefore for a fundraiser to take full advantage of this information.

Many studies have been done on philanthropy; fewer have been done on telephone fund raising, however the research on this subject is not insignificant. Two studies that we will highlight here at onfundraising are one commissioned by the Share Group Inc on fund raising and direct mail campaigns, and the other is “The Effects of Race, Gender, and Marital Status on Giving and Volunteering in Indiana” as published in the Nonprofit and voluntary Sector Quartley, in 2006 while report focuses on Indiana, much of the reporting is general in nature and therefore suitable for this post. The study’s main purpose was to examine whether factors like race, age, education or marital status make a difference to philanthropy of volunteering.


Both studies are available on line, and are linked to at the bottom of this article.

In 2006, Share Group. Inc, of Massachusetts participated in a comprehensive study on donor characteristics .Among other things this study quantifies some of the major difference between male and female donors; something that many people knew intuitively but lacked the hard data to validate. The study also measured donor contributions by age, and gave some general insights into what age group is the most supportive of charitable organizations.

According to the study, when contacted, women are more likely to support a charity campaign than men.

This data corresponds with past studies on philanthropic giving, but is limited to telephone fund raising The data indicates the women generally are more likely to donate to fund raising efforts and are also more likely to leave bequests to charitable organizations. The research indicates that woman as a whole are more likely to donate than men are.

However, the study found that based on the statistical evidence, men who do give to a campaign are likely to make larger contributions than female donors. This information as well is corroborated by independent research.

The study observes that the optimal donor age is fifty years old. Contributions tend to dwindle as donors reach retirement age. Donors ages sixty five to seventy five were most receptive to and renewal of support campaigns.

Interestingly for fund raisers, the study found that female callers secure pledges less often than male callers, but the average size of the pledges female fundraisers secure is larger than their male counterparts.

In general, women according to the research in both studies, are more likely give a larger contribution than they have previously given (upgrade). Men who could be convinced to upgrade generally upgraded at higher percentage versus female donors.

Breaking down the data further showed that when it comes to the actual giving of money, single women give twice as much as single men. These results are consistent with what has been published in the past. Furthermore, single women are more likely than men to give to a variety of charities and philanthropic efforts. A not so surprising finding was that married men also were 2-3 times more likely to give as much as single men or single women. However, the amount of money donated by single men was found to be far greater than that of married couples.

When the race factor is assessed for donation, there is little or no difference. Whites, Blacks and other minorities are no more or less likely to give relative to each other. The one obvious finding is that donations are significantly increased when the donor is educated, earns a higher income, and is older. This finding was again observed in all ethnic groups.

The Indiana study also looked at another aspect of giving-volunteering. The results are not much different to the philanthropic study. Again, single females are more likely to volunteer their time when compared to single men. Infact single females are more likely to volunteer for many more hours (>146 per year) than single men. The one surprising finding is that there is no difference in volunteering between married couples and single individuals when it comes to volunteering. It would be expected that married couples would have less time due to their familial obligations but this is not so.

Race did not reveal any differences in volunteering- Blacks, Whites and other minorities were just as likely to volunteer.

Volunteering was found to decrease when the levels of income and education were low. In fact, there was a sharp drop in volunteering levels for those with less than a high school education relative to the general population, and this was seen across all age groups.

However, increased income did not correlate with the numbers of hours volunteered. Individuals with low income were just as likely to volunteer for longer durations of time and vice versa.

The one curious observation from the study is that when individuals were asked if they had volunteered in the past, women were more likely to recall this act. Minorities could not recall if they had volunteered. Why someone can’t remember a dignified act like volunteering is a mystery- perhaps an error in how the questionnaire was set up or comprehension difficulty. The writers of the Indiana study surmised that often minorities do volunteer work without formally characterizing it as such.

Most of the results of these studies are also supported by many previous national surveys which have also found that education and higher income are the strongest and most consistent predictors of philanthropic activity.

Why single women volunteer more is not fully understood but it is speculated that because single women have less social and financial means, they compensate this by giving more of their time as volunteers while at the same time establishing their social networks.

These studies provide some insight into differences in donors by sex, marital status, race and income. Both men and women are motivated to give to charity for different reasons therefore when communicating with donors, fundraisers are advised to take this into effect and structure their solicitations accordingly.

We can take from these studies the fact that most Americans do give to charity in one way or another. Fund raisers can best serve the causes they work for buy understanding the broad characteristics of donors and structuring their donation requests accordingly.







of

Asking High; the art of the proper fundraising ask.

When asking for a pledge, the fundraiser has many informational advantages over the donor. Simply asking for the proper amount ensures that a donor will consider giving at the level you request. Starting out with a sufficiently high donation request amount allows you to find your donors preferred level of giving as rather than targeting the amount which is the lowest possible amount that a donor can possibly give to your campaign.


If a donor could give more to their charity, they probably would. When donors who maintain a strict charitable budget and give a fixed amount annually hear requests for support that are above the amount which they are willing to give, they let fundraisers know just how much they are willing to give and when they intend to give it. In the rare event that a donor is offended by large or additional requests, a skilled fundraiser can apologize for the offense and leave the donor feeling good about the level of support which they do give.


There is no downside to “asking high”.




Becoming a better fundraiser is a continuing process. There is always more to learn and more skills to master.

The conclusion to this article will be posted here in the coming days.




of

Fundraising by Phone is for every Non-Profit: The Basics of Telephone fundraising.


 An increasing number of charities and nonprofit groups are electing to solicit donations through Telefundraising campaigns. Despite having different goals and needs, disparate organizations find that Telefundraising is an effective use of resources . For many groups, the option of telefundraisng makes sense.
For large groups with vast amounts of members, telephone fundraising provides an effective means of outreach.  Charities withsignificant fundraising budgets see impressive returns from telephone fundraising efforts. Large organizations are results oriented, this is why they continue to support Telefundraising campaigns.

Small non-profits use telephone fundraising efforts as ameans to gain support for little upfront investment.While wide-scale print and media campaigns are often beyond the reach of small organizations, phonecampaigns can be executed in an extremely cost effective manner. Adjustments to telephone campaigns are virtually instantaneous, while other types of fundraising efforts need much more planning to alter.
Organizations of any size can quickly realize significant benefits from telephone campaigns. The relative low cost of phone fundraising campaigns is an attractive feature, regardless of organizational size. Paired with simple initial equipment requirements, this makes raising money by phone an easy choice for most non-profits.


Raising money is not the only goal of telephone solicitation campaigns. Contacts that end without a donation can still prove to be useful. Basing success on donations alone, overlooks other possible benefits to an organization.
Receiving a call from an organization helps to personalize the charity to its donors. Phone calls keep organizations on the minds of their supporters. These calls provide donors with the latest information on the cause they support. Well informed donors provide more frequent support.
Donation calls properly made, can be an important source of feedback for an organization as well. Donors use telefundraisng contacts as an opportunity to sound-off about their feelings about an organization.  Organizations can take these valuable insights, and use them to tweak their efforts.
Solicitation calls can provide much more than donations alone. Fully grasping this truth greatly improves campaign results.Positive campaign results are limited only by the imaginations of the organizations which run them.


Calling campaigns are always an effective solicitation method. For the best possible outcomes, additional factors should be considered as well. Holidays represent an excellent time for telephone fundraising efforts. This time of year is when many donors are most receptive to giving.Wise non-profits leverage the holidays to increase their rate of success.
Topical campaigns have increase significance to givers. These campaigns can be combined with additional media efforts. By closely monitoring the news cycle, it is possible to create campaigns that take advantage of current news, and require no additional effort to spark media interest.
Close attention to relevant news is useful, but not essential to telephone fundraising. Calling campaigns that are well structured can drive their own press coverage.  Press coverage is useful in some cases, but not an absolute requirement for all calling campaigns.


Implementing a successful telephone campaign is not substantially different than other types of solicitation methods. Good Telefundraising applys traditional fundraising methods to the phone. The benefits of applying telephone fundraising are obvious.
Telephone campaigns are an excellent way to overcome the problem of donor fatigue. Combined with other strategies, Telefundraising can strengthen listing campaigns. Fundraising by phone can produce results in and of itself.
Carried out properly, there is no application where telephone fundraising campaigns cannot be attempted with positive outcome.Effective fundraising campaigns require detailed planning. Phone fundraising and other solicitation efforts should be designed to work in harmony.
Fundraising by telephone requires serious preparation. Calling efforts should be well managed and adequately staffed. The results of telephone campaigns are based largely on the dedication of the organizations which run them.


Pound for pound, telephone fundraising delivers better results than other fundraising techniques. The advantages of telephone fundraising fluctuate across organizations, but remain significant. While income is an important factor, it is not the only way to evaluate the performance of a fundraising effort.
Phone campaigns can be implemented with minimal staff requirements. Well managed small teams can out performs much larger groups . Small teams are capable of delivering large gains.
Changes to phone fundraising campaigns can be implemented without halting the camapaign. Important updates can be quickly integrated. News and current events can be incorporated into the campaigns virtually instantly.Comparable fundraising methods need significant time to alter.
Speaking directly with prospects ensures valid contact. Direct  contact with donors is assured with calling campaigns.




of

What Kind of Fundraiser Are You?

Fundraisers come in all varieties. From aggressive to docile, from passionate to indifferent, there are many ways to bring money into a non-profit or political campaign. Just what is the best method of fundraising? The best method is the one that brings in the most money for the cause. There is only one caveat; donors must be respected at all times.

While fundraising happens in call centers, fundraising isn't a typical call center job. Most call centers don't require the depth of experience that fundraiser call center jobs require. A fund raiser can be expected to have a solid grasp of political, social and environmental issues. A telephone fundraiser can spend the morning trying to elect certain politicians and the afternoon trying to protect natural resources  The best telefundraisers have flexible minds and are gifted speakers.

Passion helps a lot but is certainly not the only way to get a donor to contribute. Knowing the issues you're calling about, even if you aren't as passionate about them as others, goes a long way to building credibility with donors. Rapport building is just as important, Donors give to people they like, Get to know your donors and they'll respond,

The next tip is counter-intuitive  Ask high. Most callers, and especially new callers, think donors will be insulted by large requests. In fact high asks have the opposite effect, Many donors are actually flattered to receive a large request, In other cases donors find these request to be humorous given their personal financial situation. Humor builds rapport and rapport secures donations.

The best fundraiser to be is the fundraiser that is most effective with the donor that's currently on the line. Be versatile  The more fundraising styles in your portfolio, the more money you'll raise.

Thank you for reading this. Please leave your comments below and check back often for new posts all about the world of telephone fundraising.




of

Elements of a Great Fundraising Script.

Some call centers are very strict about fundraisers reading directly from a script. Other call centers advise fundraisers to stick to the script, but add additional details when the call requires these extras. Still other fundraising call centers allow fundraisers a great deal of freedom as long as the fundraiser stays within the general parameters of the fundraising campaign. Each strategy has its own merits, however there are some common elements that should be included in all fundraising calls.

Getting a foot in the door.

The first step in most calls is the greeting. Usually the fundraiser identifies their self and the group which they are calling on behalf of. This step should be gotten through quickly but not rushed. The realities of the campaign will dictate how the fundraiser proceeds with the introduction. In some cases the introduction can be delayed until after the a basic description of the organization and its funding need is made. Promise to be brief with your call and stick to this promise./

Expressing gratitude.

The next step is to thank the donor. Whether its a simple thank you for taking the call or a more elaborate thank you for past contributions and supporting the cause, this is an important step. Sincere and elaborate thank yous let the donor know that their help is appreciated. Thank yous also tend to extend the call; people rarely hang up on callers while the caller is praising their support and reaffirming their decision to support the cause. Additionally, the longer a donor stays on the phone, the more likely they are to make a contribution.

The reason for the call.

Next, quickly go into some of the current issues faced by the organization and what is being done about these issues. Don't skimp on the details but don't speak in a monotonous way either. Express some real interest in the cause. Listen for cues from the donor during this and all stages. If they agree with something you're saying; elaborate on the subject. Build rapport. Remember; men and women process information differently. Read other posts on this site to find out which language to use for each kind of donor. Alternatively, if the donor indicates that they're busy; acknowledge that. Repeat that you'll be brief or just get right into your first donation request.

Going for the donation.

The first ask. Given the reasons stated above make a solid ask for a minimum of 3 times highest past gift. Be assertive and let the donor respond. Don't laugh, don't whine. If the need is real, the request should be real as well. Defend your request if required to; don't just lower it. Defending the amount of the first ask gives instant credibility to the importance of the issue, In fact, state that the reason you're requesting a large donation is because of the serious nature of the issue, Only then begin to lower the amount that your'e requesting.

A second attempt.

The second ask. Quickly elaborate on the need. Acknowledge that the donor isn't able to give 3 times their highest past donation. Considering the need, ask for 2 times the past donation. Again defend your request. The more legitimate you sound, the more likely the donor is to give you money. For many fundraisers lowering ask amounts deteriorates into desperation. Although this is a negotiation the need is legitimates and as a fundraiser you want to get the highest possible donation,

One more try.

The third and, not necessarily, final ask, This is where the fundraiser asks the donor to meet the level of their last contribution. This is obviously the level that the donor has been comfortable giving at in the past. Again stress the need and elaborate on the potential consequences of not reaching an adequate level of funding for the campaign in question.

Taking no for an answer.

If stopping here, without securing a donation, take the time to sincerely thank the donor once more. This establishes that you, the fundraiser and the organization, respect the donor no matter what they can or cannot give at the moment. This also helps to reinforce an attitude of respect and gratitude which should be extended to all donors at all times.

The forth ask and so on.

Many times a donor who can't match a previous donation will express regret that they simply cant afford the same amount. If applicable, ask for an even lower amount down to the minimum level of donation that can be taken on a specific campaign. Remind the donor and yourself that every donation, no matter what size, counts. After all in most fundraising campaigns, the many small donations greatly out number the amount of money which is generated by the larger ones.

Get it on a credit card.

The credit card ask. Credit card donations fulfill instantly. There are no pledge cards to send out. Obviously securing a donation on a credit card is favorable to a mailed in pledge card. Credit cards on the phone are favorable to online donations as well; donors can easily be distracted and forget to make their donation.

Ease their mind.

Security is the main concern with credit card donations, Donors are rightfully fearful of identity theft. Every call center has methods in place to protect the credit card information of donors. Patiently explain these procedures as well as why credit card gifts are the best gifts that donors can make.

Be prepared to further explain  the value of credit card donations and their secure nature. Many donors will give by credit card once they have been properly assured of security measures. If not, follow your organization's standard pledge card procedure.

Wrapping up the call.

Again, take the time to sincerely thank the donor for their help. Answer any additional questions and then politely end the call. Following this method on every call improves dollars raised as well as the quality of each outbound call.




of

The Future of Fundraising is Local.

With the economy finally starting to gain some traction, few people are thinking about those whom society is le aving behind. These are the working poor, the sick and the elderly. As the wars in Iraq and Afghanistan wind down, we will see more soldiers and their families in need.

Sometimes fundraising calls for a national effort. The only way to gather money and resources quickly is by calling or mailing into every state for support. This is a proven method of soliciting money. This isn't the case for every fundraising effort however.

There are some issues that could just as well be taken care of locally. Additionally, some causes are actually better served by using local fundraisers. Local has a number of benefits; we'll name a few below.

More of the money goes to the cause.
Some national fundraising companies can take as much as 80% of what they raise for an organization. Millions of dollars are wasted in this way. Local groups just can't afford to pay these prices. A local campaign, whether staffed by volunteers or local professionals, ensures that more of the donated dollars go to work immediately.

No one knows local issues like local people.
Using locals to fund raise means having a team that understands the issue and is passionate about it. The more knowledgeable and interested in an issue a fundraiser is, the more likely they are to secure donations.

As a nation, a number of important issues are affecting Americans;
children going to bed hungry, seniors and veterans not getting the care that they deserve and homelessness are just a few of these issues. What all of these issues all have in common is that they begin and end locally.

We can choose to wait for a solution from Washington, but President Obama's job forces him to look at the big issues, not the small ones. National organizations face similar problems; they have the money and resources, but not the organization to implement relief locally. Local fundraisers raising and spending money locally have an immediate effect on communities.

Bringing the idea of raising money in the community and solving problems locally is one whose time has come. More local groups are starting to see the power of collecting donations at home. As the idea spreads, the benefits will only become greater.




of

The etymologies of ballot and bigot

That's all I've got, so far, for linguistic commentary on the U.S. election results. According to the OED, the etymology of ballot is < (i) Middle French ballotte (French †ballotte) small ball (beginning of the 15th cent. as †balote), small coloured ball placed in a container to register a secret vote (1498) or its etymon […]




of

A bushel of buzzwords from Japan; the advent of phoneticization

Below are two lists of nominations for Japanese buzzword of the year.  Each has 30 entries, and from each list one will be chosen as the respective winner.  Since the two lists are already quite long and rich, I will keep my own comments (mostly at the bottom and focusing on phoneticization) to a minimum. […]




of

Autumn 2022 issue of Agapé available

The Autumn 2022 issue of Agapé, the official journal of U.S. Grand Lodge O.T.O., is now available. This and all previous issues can be found here.




of

Winter 2022 issue of Agapé available

The Winter 2022 issue of Agapé, the official journal of U.S. Grand Lodge O.T.O., is now available. This and all previous issues can be found here.




of

Spring 2023 issue of Agapé available

The Spring 2023 issue of Agapé, the official journal of U.S. Grand Lodge O.T.O., is now available. This and all previous issues can be found here.




of

Summer 2023 issue of Agapé available

The Summer 2023 issue of Agapé, the official journal of U.S. Grand Lodge O.T.O., is now available. This and all previous issues can be found here.




of

From now on, the title of the post is allowed to just be "January 2024" (only when it is January 2024, however)

Hello again,

This month I've been plugging away on the project I mentioned in the previous post which involves among other things a PDF generator and now an implementation of ML (as in Standard ML, but also the other one). This is probably the 10th "compiler" I've written in my life, and it's kind of fun to revisit these problems that you've done many times and try out different approaches, although this time one of the approaches is "Use C++" (for reasons of making good on a joke, but also for reasons of mlton doesn't work on my computer any more). And although C++ is a fine tool for many applications, it does have some deficiencies for the task of writing a compiler (one of the most irritating: a very modest limit on the stack depth? Like my computer has 256 Gigabytes of RAM and 2^64 virtual addresses and somehow it can only manage 1 megabyte for the stack and there's no standard way to increase it? Get off my lawn). But then you can also experience new ways of struggling with C++, like: A middle of the night power failure wrecked my computer's GPT (as in GUID Partition Table, but also the other one) and I was deep in the depths of taking the computer apart to reset its parts, its BIOS (its Basic In/Out System, which is where it stores its biography) and its hard drives were everywhere on the floor, and it could not be saved, and this after I already broke my computer this year by trying to put the world's biggest video card in it, too hard. And I could not merely perform recovery because of Unknown Error, so I had to begin anew again and restore from backups. But when you restore from backup and you're in the mood of "why is this so complicated and I don't understand how computers work any more?" it occurs to you (me) to also change your underlying development environment instead of reinstalling the devil you know. So I ended my friendship with Cygwin64 and switched to new best friend MSYS2. Both of these things are different ways of wishing that you were using Linux while you're using Windows. The main reason I tried this new way of struggling is that Cygwin is very behind on its version of x86_64 clang (C++ compiler), which I wanted to try because it supports AddressSanitizer and clangd on Windows, and I wanted to give LSP in emacs a shot (it's finally good!). There were a few growing pains, but I think MSYS2 is what I would recommend now. One of the nice things they did was create multiple different environments depending on what you want to do (e.g. "I want to use clang to compile x86_64 code" or "I want to do 32-bit cross compilation for ARM") and in that environment, you just say "g++" and it invokes the compiler you want, instead of the weird contortions I've been doing for years with manually invoking x86_64-w64-mingw32-g++. I was also able to get clblast working before being too filled with rage to continue, so that is nice for the ML inference on the world's biggest graphics card. I made these graphics to help me tune the correct settings of GPU layers (y axis) and number of threads (x axis):


tune-single

tune-batch


In some sense the results are obvious (more threads and more layers is faster) but it was interesting to me how the cliff of performance drops off at a different number of layers for single and batch mode (I guess because the batch needs some memory itself?) and how it's clearly better to use fewer threads than cores for batch as well. I was not surprised to see performance drop off for >32 threads (everybody knows that hyper-threads kinda suck) but I was very surprised to see performance pick up again when it gets back up to 64? And only for single mode? I wish I understood that better. But mostly I'm a sucker for the custom visualizations.

Right but when writing this compiler I realized that I wanted to use some Greek letters, and I can't handle it when some characters are in a different font in my source code, so I finally made some space for those in my programming font FixederSys. These certainly still need some tweaks, but it's already better than just being in some other weird font:


{{{caption}}}


You can also see that I have been adding some "useful" emoji at the top. It is an interesting puzzle to try to make these things recognizable (especially for the 1x version, whose charboxes are 8x16 pixels). I am pretty sure I will not try to do all of the emoji (like, the flags are totally hopeless at 8x16), but it is tempting to round out the Unicode support somewhat. Like I was trying to make a ¯\_(ツ)_/¯ today and had to settle for ~\_( :) )_/~ which is pretty much (ノಠ益ಠ)ノ彡┻━┻.

Also: Adam revived our old game jam game Headcat, which I described in post 927, now over 16 years ago. You can play it online at Headcat.org. It is harder than I remember, perhaps explaining why it did not reach #1 on the One Appstore Per Child charts.

Also: I started and finished (true ending, but just with one character) Slay the Spire. Good game, but you don't need me to tell you that. Same for Alwa's Legacy, which is the sequel to Alwa's Awakening. Both of these are very true-to-form "8-bit" and "16-bit" platformers that I enjoyed and would recommend for genre fans, though I did not try to 100% them. The graphics are the highlight and I thought it was very cute how these could easily have been a pair of games from the NES and SNES. The good old days. And speaking of good-old days, I am now playing Katamari Damacy, which I had played at a friend's house many years ago, and always wanted to spend more time with. It totally holds up (aside from stuff like: You have to play through the tutorial and first level before you can access the menus at all, like to make the game fullscreen?) and it's honestly inspiring how unhinged the game design and writing are, and how fun it manages to be. What an accomplishment!




of

Of all homonymic months, August is the most majestic

I’m traveling for the long weekend. Either I’m having bad luck with the epic heat waves or there have been a lot of epic heat waves, because again the short road trip threatens to be tyrannized by the hot air. It did at least touch 100°F this time, so at least it is a proper respectable heat wave. We are in a place called Hocking Hills, whose AirBnB has these OBX-style stickers that say “HHO”, which could either be confusingly “Hills, HOcking,” or perhaps “Hocking Hills, Ohio”, but not “Hocking hills OHio” as one might expect. I plan to stick the sticker upside-down for “OHH”, as in “Ohh yeah, I need to write a post on Tom 7 Radar for the month of August, and I need to do it on this mediocre wi-fi which Google Internet Speed Test describes as ‘fine’ while everyone else drinks beers outside.” Fair enough: This is a self-imposed curse and one that’s easily tended to at any time during the month.

During the month: I worked again on making my own video codec, which is a very bad way to spend one’s time, but I don’t think there are any modern lossless codecs that would be suitable for my use case. And I do like a data compression project because of the inherent benchmarkability. The use case is for the increasingly common situation where I have a program generating a series of video frames (e.g. BoVeX is making an animation), which I usually do by writing a sequence of PNG files to disk. I’m way ahead of PNG files so far even without doing any inter-frame stuff, which is not impressive, but does make me feel like it’s at least not totally pointless. (Still, it’s quite pointless: Sure I can make these files smaller at significant cost of complexity and encoding times, but these animations typically use space similar to like one second of 4K 60fps XF-AVC footage.)

Sometimes programming your own lossless video codec is a bit too fast-paced so you need to write a Wikipedia article from scratch about Clairton Coke Works by digging through newspaper archives. I haven't even gotten to the last 30 years of its history yet! I also rounded out the Cyrillic in FixederSys though I don't think I've uploaded a new version of that yet. As usual I did some hacking on secret projects.

UHH, elsewise, I did finish off Animal Well which I liked very much. My spoilerless advice to you is: Don't try to 100% this game without at least looking at a spoiler-controlled guide! But I did have fun once I felt like I was stuck-ish finishing the remaining postgame puzzles. I have also been playing Chippy, a bullet-hell twin-stick shooter that is quite hard (I usually feel good at this genre) and has several new good ideas in it. It's essentially all boss fights, and the chief innovation is that you fight the giant bosses by disconnecting pieces of them. I'm on the last boss so I will probably finish that one soon. As I have confessed many times, I like dumb first-person shooter games, and I played through Trepang 2 this month as well. It does have a few moments, but it was mostly pretty dumb, like I wanted. And then I started Touhou Luna Nights, which is a "Metroidvania" fan-game with great pixel art and music.

OK, I should get back to this vacation!




of

Card Deck Review: THE LEGEND OF SLEEPY HOLLOW TAROT

The Legend of Sleepy Hollow Tarot: Headless Horseman edition Nick Lawyer REDFeather (October 28, 2023) Reviewed by N. Richards What a wonderful way to honour the Irving Washington classic gothic story of 1822, “The Legend of Sleepy Hollow,” and the season of autumn as well as the art of Tarot all in one hit of […]

The post Card Deck Review: THE LEGEND OF SLEEPY HOLLOW TAROT first appeared on Hellnotes.




of

Book Review: THE EERIE BROTHERS AND THE WITCHES OF AUTUMN

The Eerie Brothers and the Witches of Autumn Sheldon Higdon Scary Dairy Press LLC (September 4, 2023) Reviewed by Nora B. Peevy The Eerie Brothers and the Witches of Autumn finds Horace and Edgar, the twin Eerie brothers, battling monsters to stop Hex from collecting one of the four globes to absorb the abilities of […]

The post Book Review: THE EERIE BROTHERS AND THE WITCHES OF AUTUMN first appeared on Hellnotes.




of

Book Review: OF TEETH AND PINE

Of Teeth and Pine Desiree Horton Independently published (October 31, 2024) Reviewed by Nora B. Peevy Desiree Horton’s second novel is filled with blood, a taste of the beautiful outdoors, and a lot of snark. I fell in love with her main character, Vick, the female forest ranger who will not put up with any […]

The post Book Review: OF TEETH AND PINE first appeared on Hellnotes.




of

A Change of Routine

Some of the most common trials to face JPs nowadays are the Section 172 cases that arise from the ubiquitous speed cameras. Some people (such as Christopher Huhne) simply lie about who was driving the speeding vehicle, and others claim not to know the ID of the driver, but the consequences of being caught can be nasty. These cases tend to be listed in just a few courts, which can be pretty tiresome for the bench members. As it happens these cases are a rarity in my court, so this week the S172 that appeared on my list was the first that I have ever done, in about 30 years on the bench. The evidence was pretty thin, and we acquitted. The clerk told us later that these cases often fail to stick.

They are a tidy source of revenue for the various loophole specialists in the legal profession, as many people will cough up a hefty sum to keep their driving licence.




of

Out of The Ordinary

The right-wing fanatic who is accused of the murder of Jo Cox MP has opted not to give evidence nor to call any witnesses in his defence. The jury will be directed by the judge as to how to deal with this.

He is of course perfectly entitled to remain silent, just as the jury is perfectly entitled to draw the inferences that it finds proper from his decision. In times past courts sometimes had to decide whether the accused was 'mute of Malice' or 'mute by Visitation of God'.

This is a situation that I have only faced a few times in court. We gravely retired to consider, and I took the bench carefully through the decision making process as if we were assessing a real defence. We then took great care to prepare our reasons for our blindingly obvious decision, reading them out slowly and carefully before handing them down to the Clerk for the file. It all felt a bit unreal, but it is in odd cases such as these that everything has to be done just so.

Guilty it was then, to no one's great surprise.




of

Scots curlers miss the play-offs

Scotland beat Italy and Canada but fall short of reaching the World Women's Curling Championship play-offs.




of

Capital of Texas Triathlon Preview

Monday I'm going to be running in the 25th Capital of Texas Triathlon!  It's my first triathlon (Olympic distance) in twenty years and I'm pretty jazzed.

Steely-eyed determination 20 years ago. :-)

One of the great things about triathlons (and running races in general) is that you get to occupy unusual spaces: the last ones I did were Leon's Triathlon in Hammond, Indiana, a couple of Bud Light triathlons and others in Chicago. Leon's had a swim in Wolf Lake (shudder), followed by a cycle leg on an elevated highway that ran past the old U.S. Steel plant, and a run leg through an industrial downtown.  The Chicago ones were on the lakefront, just north of Navy Pier, with a bike on Lake Shore Drive and a run along the lake.

2013 CapTexTri
The CapTexTri also has a great location in downtown Austin, with a 1.5k swim in Lady Bird Lake; a 40k (24.8 mile) bike on a quadruple loop through downtown Austin, including Congress Avenue and Cesar Chavez; and a 10k (6.2 mile) run through Zilker Park.

The only thing I'm not too keen on is the bike route, since it requires you to do the same loop four times with a bunch of corresponding hairpin turns.  I don't like loop routes because I always think of how many more times I have to do the thing...Still, going up and down Congress Avenue without any cars is going to be pretty cool. As long as there are no poles in the middle of the road, I should be okay. :-).

Don't ask.

I feel fairly good about my training.  I've maintained good running mileage after the Austin Marathon and Austin Distance Festival and got some good workouts in even while traveling doing school visits.
On Stone Arch Bridge in Minneapolis
The swim is probably my weakest event -- I could stand to do more work on technique and probably do more open water swimming, but the distance won't be an issue.  Also, Lady Bird Lake isn't going to have waves (I seem to recall a couple of triathlons in Chicago with 3-4 foot waves on Lake Michigan (and this was on the near side of the breakwater).  Also, I won't have to deal with a wet suit.  My biggest concern is to not get kicked in the face. :-).
Lady Bird Lake during 2013 CapTexTri
The bike I'm feeling good about as well.  I'll be using the bike I used for my triathlons back in the day - a Trek 1000 I bought when I was in grad school for $450 (a guy at one bicycle shop here tried to sell me a new one, asking if I had a "nostalgic attachment" to it.). I do, but I also don't think a new bike is going to drastically transform my performance.  At least not $2000 worth :-). (A guy at another bike shop told me the Trek 1000 was his first road bike and he wished he still had it.  It's possible he was being kind :-)).

Tomorrow is packet pick-up, bike drop-off, and a chance to scope out the transition area, which I'll need because I can't see without my glasses...:-)

Oh, well.  Qapla!















of

Capital of Texas Triathlon/Duathlon/10K/5K

So my plan to do my first triathlon in twenty years on Monday didn't turn out so well.

It rained.

A lot. But not so much in town.

At Camp Mabry, just north of central Austin, we got less than an inch of rain last Thursday. At Bergstrom Airport, just south and east of downtown, they got about nine inches.  And it was much worse farther east, along the Colorado River (which also runs through Austin).

On Friday and Saturday, it rained in the Hill Country.  West of Austin.  Upriver.

On Sunday, there was the CapTexTri expo and packet pickup and bike dropoff.

Swag!  My first cowboy hat since I was around seven.
Bike drop off.  I got there early.
Packet pickup
Coveting my neighbor's bike, Part I.
It was clear and sunny.  But because of all the rain, the LCRA opened at least one floodgate from the Tom Miller Dam, releasing water into Lady Bird Lake.  Which was where the swim portion of the CapTexTri was supposed to take place.

At the course talk at the expo, they announced that there was a flow of about 3 mph and they were considering changing the course so that it ran point to point (downriver).
Course talk.  Could've used a projector.


Later that day, they announced the swim was canceled.  Which was disappointing, but I've weathered a couple of triathlons in Chicago where that had happened and one when it probably should've.

Monday, race day, I awoke at 5 AM, fed the cats, ate breakfast and drank coffee, and then I heard rain.  Lots of rain.

Nevertheless (discovering, to my chagrin, that I am apparently an optimist), I headed out.  When I arrived at the transition area, I was told it was closed, and that we should shelter at Palmer Events Center or its garage.  This was around 6-620.

In the garage and on the deck of the Palmer Events Center, folks seemed to take things in stride and with humor.  Some people were concerned about hairpin turns on a wet course, but were generally willing to take it slow.
Sheltering in the garage

We heard a tentative plan to cut short the bike portion to 20k, but the rain and lightning continued. Finally, at around 830, race officials called off the bike portion entirely because of flooding on the course. 
Still a bit lightning-y
They announced that Olympic distance participants could do a 10k, while sprint participants could do a 5k and that start time would be at 10 am. Most folks removed their bikes and went home or back to their hotels. There was some grumbling -- last year the event had been cut short due to flooding, as well, and I gather there had been similar problems in 2014, too.
Athletes clearing out the transition area
 
I took my bike back to my car, but decided that I'd gotten up at five that morning to run a race and so, I was going to do one.  Besides, I didn't want to waste all those carbs I'd eaten in the past few days. :-).

At ten o'clock, the rain pretty much stopped.  And then we were off!  By 10:15, the sun came out.  No, really. 
Everyone who's still there seems in good humor :-)
And we're off!


Turned out, there were only about 200 of us who stuck around for the 10k, with another 150 for the 5k (out of around 3000 original participants), but everyone seemed to be having a good time.  I was pretty happy with my race -- I'm not sure it was exactly 10k, but I still did one of my better overall times and paces. At least this century :-).
The view from the Biergarten.  Note the utter absence of rain.
Sunny skies.
On the whole, it was a bit surreal but fun, although in the moment sometimes frustrating.  And, in retrospect, kind of funny.  I think the organizers did a good job under trying conditions and kept us pretty well informed via social media.  So, thanks (And I am really glad I wasn't in charge :-)).  Thanks also to all the volunteers who stuck around to the bitter end.

 Oh, and I actually ended up getting a bit of a tan.

Coveting my neighbor's bike, Part II
Epilogue: Late Monday, the Austin Fire Department closed Lady Bird Lake and Lake Austin to all boat traffic.

And they're giving us a discount for the 2017 race. :-).

Here's what the lake looked like Tuesday morning (Normally, there is no current at all):

















 




of

Eureka? Scientists’ first hints of life on other planets may not be so obvious

Knowing that you've found signs of life beyond Earth may not be as clear-cut and simple as one might think.




of

How EELS could change the future of robotic exploration

The snake-like robot is being designed to autonomously navigate the challenging terrain of Saturn’s moon Enceladus, including descending into fissures in the moon’s icy crust. The skills it needs in order to explore this distant, unfamiliar world may make EELS well equipped to explore even more alien worlds, perhaps including exoplanets.




of

Mars may host oceans’ worth of water deep underground

The tentative discovery hints at an habitat where life could potentially thrive.




of

Hera launches to study the aftermath of an asteroid deflection test

The European Space Agency’s Hera spacecraft launched on Oct. 7, 2024, from Cape Canaveral, Florida. It will travel to the Didymos-Dimorphos asteroid system to study the aftermath of the first-ever field test of an asteroid deflection technique.




of

Best of 2024

Cast your vote for the best of space exploration and science in 2024!




of

Glenmorangie A Tale of Ice Cream Single Malt Scotch Whisky




of

One of my favorite native flowers

Native wine-cups (aka purple poppy mallow) bookends. This is one of my favorite native flower, so particularly pleased with how nicely it shows up in bookends.




of

The Battle of the Five Armies at Partizan.

well...what a bloody marvellous couple of days I've just had. Taking all my figures to a show for the first time was a little nerve racking, but the effort really paid off. I had an excellent time and met some great new friends too in the process.
The board and mountain were excellent too, crafted by the talented James Morris. I had never deployed my whole army in one go before, so what better time to do it than on a set up like this. Everything seemed to come together perfectly.
I started my battle with the orcs at the bottom of the hill advancing across the mountain river. However, due to my terrible dice rolling and the luck of the Elves, I was cut and shot to pieces. It wasn't long before my tattered ranks were tumbling back through the freezing water. The Elves just had time to redress their lines before another dark wave of Goblin folk arrived.
I love the walled up gate that James made, the little gaps in the stonework were perfect. It really reminded me of one of Alan Lees illustrations. Also a very clever use of silver fabric for the river...very effective!

The Eagles arrived later in the game and swept the Goblins from the mountain sides. The game was masterfully run by David Hunter, who made sure the flo was just right.
My Eagles looked very realistic on the slopes of the mountain too, better than I had hoped for. The extra poses were well worth collecting over the last ten years. The flying ones on stands were a very recent purchase and they really stood out.
The game in full flo, it drew a big crowd and it was great to talk figures with people. A lot of people wanted to know where I got my Orcs from and of course the answer was...everywhere. Also the amount of plastic model kits used was very hard to calculate. Just about every Dark age and Fantasy box set all mashed up and mixed together.



I love my Wood elves after lovingly creating each one individually and got a huge buzz from seeing them in action. I didn't really care they were cutting my forces to ribbons, just seeing them used in anger was enough for me.
Hordes of Goblins still plagued the mountainside, their numbers were too much even for the Eagles.
Thorin was cut down by Bolg's bodyguard as he tried to cut through their great shields. Bard of Laketown too, fell to the hacking Goblin blades. Dain of the Iron hills was holding his own but was badly wounded. At last it seemed like the Free people's luck had run out. 
Then with a roar like a thunderclap Beorn burst onto the scene. He tore through the Goblin ranks and made his way to Bolg. Approaching the rear of Bolg's bodyguard he shattered their formation.

It was at this point the Orc's morale was lost and the game was called. The rules were James' new Midguard ones and were perfect for this Dark age epic clash of men and monsters.

I had a great couple of days and it was a real treat to get out and do something exciting like this. To my amazement I didn't suffer any spear snaps of breakages and everything survived intacted. I was expecting a few casualties and had even taken a little repair kit but didn't need it.

Dain's Iron hills Dwarves push the Goblins from the ridge, supported by a group of Lake men.
An earlier shot of the swirl of battle.
The nine Black riders with their Dark lord 
My beloved Silvan elves holding their own against ravaging wolf packs.

I said it once, I'll say it again, what a bloody marvellous couple of days!

 I'm sure I'll have some more pictures soon to post on here of the day...
 




  • lord Of The Rings
  • lord Of The Rings.
  • War games

of

The Battle of the Five Armies



 Here's a shot of James Morris' Lonely mountain with the only missing element...thousands of blood drinking bats. 



  • lord Of The Rings
  • lord Of The Rings.

of

Stones, Shrines and Alters of Erin


Here are the finished standing stones from Alternative Armies amongst others.
I've added a few extra skulls to the base once flocked.
A bear hide has been left as an offering too.

Battle shine, again extra weapons have been added to the base. The rocks form a sacred circle in which to knee and offer your items.

Paper ferns were added to this base. I'm not sure how they will last as they are fragile. Being hard up against the stone will help them.
This place must be strong in fey magic because flowers have sprung up around the stones. Flowers are a nice way to show the affinity of a place. Flowers equal a nice holy area, where dead and dying grass says the opposite.


The great fertility rock with it's comfy furs to lie on. I wanted to give this the impression of a giant lady in a fur cloak. The cloak is made from moss and ivy.
Ancient pillar, I added some ivy to this to weather it in. Also putting flock to act as moss up one side helped to age it. Moss normally grows on the north side of trees, so I added this trick to a lot of the stones.



This is a miniature from Reaper Bones, I think it's called 'Evil tower'. I thought it would make a good Fomorian piece or maybe something older and darker. What ever it is, it has been thrust up from the ground and killed the surrounding grass. It must be cursed...


Ivy again helps weather these small stones. This isn't a bad place as flowers grow around the mound, new life from old.

I've rediscovered Celtos as a figure range that might be useful for this growing project.


 


It's time to dig out the old stones and compare them. A lot of these previous models had a greenstuff carving in them.







The two weapon shrines. One is a place of worship, the other, sacrifices.


The two stones of the dead. These could be used for any undead project.






Recently I thought about making more of an effort to photograph my miniatures. I have seen other people use backdrops and so thought I'd play around with the idea. By using a photo from a book as a background, the results can be quite different. Rather than a cluttered work table full of detail taking away from an image, the background actual enhances and compliments it. This simple book set up has really improved these stone pictures and it's something I'll do again for sure.




of

War of the Roses Basing

The men of Lord Hastings' Retinue struggle through the mud of Tewkesbury.

Vallejo thick mud was the perfect solution for the grim battle conditions of the war. Adding snow to this layer would look fantastic but would rather limit the battles. I think a generic muddy field is a good all rounder for this brutal conflict.



I broke my usual 69x60mm basing after seeing a friend's and decided to copy it. Partly because the cheapness of plastics allows for bigger units. I also have quite a few old Perry miniatures from the old days of Foundry. These old lead figures are great for sprinkling amongst the ranks to add character. The above photo shows the effect of these old sculpts. They have to be mounted on plastic bases etc to bring them up to the height of the newer plastics. The mud is great for covering these and making everyone level.
As the Vallejo mud was drying, I cut up some thin brush bristles and pushed them into the mixture. These make for great arrows and really helps to give the bases a War of the Roses look and feel.
The mud is also great for splashing up the legs and clothes of the soldiers. It's quite subtle but helps to  set them in the scene.


The mud isn't quite dry yet and there are a couple more things to do before they are finished. Layers of 'Rutted field' from Luke's APS should look good over the mud, as well as patches of static grass. Also the arrows will need some white goose fletching on them.


 These new bases are 80x60mm and give a more realistic look to a unit. I got a bit carried away with these bases and they grew to 10 men per base.

The figures In these units are a mix of old Foundry, Perry's plastics and Forlorn Hope metal figures. They all mix together well and make for characterful formations.



  • War of the Roses

of

TurtleWare: Dynamic Vars - Return of the Jedi

Table of Contents

  1. The protocol
  2. Control operators
  3. Synchronized hash tables with weakness
  4. First-class dynamic variables
    1. STANDARD-DYNAMIC-VARIABLE
    2. SURROGATE-DYNAMIC-VARIABLE
  5. Thread-local variables
    1. The protocol
    2. The implementation
  6. Thread-local slots
  7. What can we use it for?

In the previous two posts I've presented an implementation of first-class dynamic variables using PROGV and a surrogate implementation for SBCL.

Now we will double down on this idea and make the protocol extensible. Finally we'll implement a specialized version of dynamic variables where even the top level value of the variable is thread-local.

The protocol

Previously we've defined operators as either macros or functions. Different implementations were protected by the feature flag and symbols collided. Now we will introduce the protocol composed of a common superclass and functions that are specialized by particular implementations.

Most notably we will introduce a new operator CALL-WITH-DYNAMIC-VARIABLE that is responsible for establishing a single binding. Thanks to that it will be possible to mix dynamic variables of different types within a single DLET statement.

(defclass dynamic-variable () ())

(defgeneric dynamic-variable-bindings (dvar))
(defgeneric dynamic-variable-value (dvar))
(defgeneric (setf dynamic-variable-value) (value dvar))
(defgeneric dynamic-variable-bound-p (dvar))
(defgeneric dynamic-variable-makunbound (dvar))
(defgeneric call-with-dynamic-variable (cont dvar &optional value))

Moreover we'll define a constructor that is specializable by a key. This design will allow us to refer to the dynamic variable class by using a shorter name. We will also define the standard class to be used and an matching constructor.

(defparameter *default-dynamic-variable-class*
  #-fake-progv-kludge 'standard-dynamic-variable
  #+fake-progv-kludge 'surrogate-dynamic-variable)

(defgeneric make-dynamic-variable-using-key (key &rest initargs)
  (:method (class &rest initargs)
    (apply #'make-instance class initargs))
  (:method ((class (eql t)) &rest initargs)
    (apply #'make-instance *default-dynamic-variable-class* initargs))
  (:method ((class null) &rest initargs)
    (declare (ignore class initargs))
    (error "Making a dynamic variable that is not, huh?")))

(defun make-dynamic-variable (&rest initargs)
  (apply #'make-dynamic-variable-using-key t initargs))

Control operators

Control operators are the same as previously, that is a set of four macros that consume the protocol specified above. Note that DYNAMIC-VARIABLE-PROGV expands to a recursive call where each binding is processed separately.

(defmacro dlet (bindings &body body)
  (flet ((pred (binding)
           (and (listp binding) (= 2 (length binding)))))
    (unless (every #'pred bindings)
      (error "DLET: bindings must be lists of two values.~%~
              Invalid bindings:~%~{ ~s~%~}" (remove-if #'pred bindings))))
  (loop for (var val) in bindings
        collect var into vars
        collect val into vals
        finally (return `(dynamic-variable-progv (list ,@vars) (list ,@vals)
                           ,@body))))

(defmacro dset (&rest pairs)
  `(setf ,@(loop for (var val) on pairs by #'cddr
                 collect `(dref ,var)
                 collect val)))

(defmacro dref (variable)
  `(dynamic-variable-value ,variable))

(defun call-with-dynamic-variable-progv (cont vars vals)
  (flet ((thunk ()
           (if vals
               (call-with-dynamic-variable cont (car vars) (car vals))
               (call-with-dynamic-variable cont (car vars)))))
    (if vars
        (call-with-dynamic-variable-progv #'thunk (cdr vars) (cdr vals))
        (funcall cont))))

(defmacro dynamic-variable-progv (vars vals &body body)
  (let ((cont (gensym)))
    `(flet ((,cont () ,@body))
       (call-with-dynamic-variable-progv (function ,cont) ,vars ,vals))))

Synchronized hash tables with weakness

Previously we've used SBCL-specific options to define a synchronized hash table with weak keys. This won't do anymore, because we will need a similar object to implement the thread-local storage for top level values.

trivial-garbage is a portability layer that allows to define hash tables with a specified weakness, but it does not provide an argument that would abstract away synchronization. We will ensure thread-safety with locks instead.

(defclass tls-table ()
  ((table :initform (trivial-garbage:make-weak-hash-table
                     :test #'eq :weakness :key))
   (lock :initform (bt:make-lock))))

(defun make-tls-table ()
  (make-instance 'tls-table))

(defmacro with-tls-table ((var self) &body body)
  (let ((obj (gensym)))
    `(let* ((,obj ,self)
            (,var (slot-value ,obj 'table)))
       (bt:with-lock-held ((slot-value ,obj 'lock)) ,@body))))

First-class dynamic variables

STANDARD-DYNAMIC-VARIABLE

Previously in the default implementation we've represented dynamic variables with a symbol. The new implementation is similar except that the symbol is read from a STANDARD-OBJECT that represents the variable. This also enables us to specialize the function CALL-WITH-DYNAMIC-VARIABLE:

(defclass standard-dynamic-variable (dynamic-variable)
  ((symbol :initform (gensym) :accessor dynamic-variable-bindings)))

(defmethod dynamic-variable-value ((dvar standard-dynamic-variable))
  (symbol-value (dynamic-variable-bindings dvar)))

(defmethod (setf dynamic-variable-value) (value (dvar standard-dynamic-variable))
  (setf (symbol-value (dynamic-variable-bindings dvar)) value))

(defmethod dynamic-variable-bound-p ((dvar standard-dynamic-variable))
  (boundp (dynamic-variable-bindings dvar)))

(defmethod dynamic-variable-makunbound ((dvar standard-dynamic-variable))
  (makunbound (dynamic-variable-bindings dvar)))

(defmethod call-with-dynamic-variable (cont (dvar standard-dynamic-variable)
                                       &optional (val nil val-p))
  (progv (list (dynamic-variable-bindings dvar)) (if val-p (list val) ())
    (funcall cont)))

SURROGATE-DYNAMIC-VARIABLE

The implementation of the SURROGATE-DYNAMIC-VARIABLE is almost the same as previously. The only difference is that we use the previously defined indirection to safely work with hash tables. Also note, that we are not add the feature condition - both classes is always created.

(defvar +fake-unbound+ 'unbound)
(defvar +cell-unbound+ '(no-binding))

(defclass surrogate-dynamic-variable (dynamic-variable)
  ((tls-table
    :initform (make-tls-table)
    :reader dynamic-variable-tls-table)
   (top-value
    :initform +fake-unbound+
    :accessor dynamic-variable-top-value)))

(defmethod dynamic-variable-bindings ((dvar surrogate-dynamic-variable))
  (let ((process (bt:current-thread)))
    (with-tls-table (tls-table (dynamic-variable-tls-table dvar))
      (gethash process tls-table +cell-unbound+))))

(defmethod (setf dynamic-variable-bindings) (value (dvar surrogate-dynamic-variable))
  (let ((process (bt:current-thread)))
    (with-tls-table (tls-table (dynamic-variable-tls-table dvar))
      (setf (gethash process tls-table) value))))

(defun %dynamic-variable-value (dvar)
  (let ((tls-binds (dynamic-variable-bindings dvar)))
    (if (eq tls-binds +cell-unbound+)
        (dynamic-variable-top-value dvar)
        (car tls-binds))))

(defmethod dynamic-variable-value ((dvar surrogate-dynamic-variable))
  (let ((tls-value (%dynamic-variable-value dvar)))
    (when (eq tls-value +fake-unbound+)
      (error 'unbound-variable :name "(unnamed)"))
    tls-value))

(defmethod (setf dynamic-variable-value) (value (dvar surrogate-dynamic-variable))
  (let ((tls-binds (dynamic-variable-bindings dvar)))
    (if (eq tls-binds +cell-unbound+)
        (setf (dynamic-variable-top-value dvar) value)
        (setf (car tls-binds) value))))

(defmethod dynamic-variable-bound-p ((dvar surrogate-dynamic-variable))
  (not (eq +fake-unbound+ (%dynamic-variable-value dvar))))

(defmethod dynamic-variable-makunbound ((dvar surrogate-dynamic-variable))
  (setf (dynamic-variable-value dvar) +fake-unbound+))


;;; Apparently CCL likes to drop^Helide some writes and that corrupts bindings
;;; table. Let's ensure that the value is volatile.
#+ccl (defvar *ccl-ensure-volatile* nil)
(defmethod call-with-dynamic-variable (cont (dvar surrogate-dynamic-variable)
                                       &optional (val +fake-unbound+))
  (push val (dynamic-variable-bindings dvar))
  (let (#+ccl (*ccl-ensure-volatile* (dynamic-variable-bindings dvar)))
    (unwind-protect (funcall cont)
      (pop (dynamic-variable-bindings dvar)))))

Thread-local variables

We've refactored the previous code to be extensible. Now we can use metaobjects from the previous post without change. We can also test both implementations in the same process interchangeably by customizing the default class parameter.

It is the time now to have some fun and extend dynamic variables into variables with top value not shared between different threads. This will enable ultimate thread safety. With our new protocol the implementation is trivial!

The protocol

First we will define the protocol class. THREAD-LOCAL-VARIABLE is a variant of a DYNAMIC-VARIABLE with thread-local top values.

We specify initialization arguments :INITVAL and :INITFUN that will be used to assign the top value of a binding. The difference is that INITVAL specifies a single value, while INITFUN can produce an unique object on each invocation. INITARG takes a precedence over INTIFUN, and if neither is supplied, then a variable is unbound.

We include the constructor that builds on MAKE-DYNAMIC-VARIABLE-USING-KEY, and macros corresponding to DEFVAR and DEFPARAMETER. Note that they expand to :INITFUN - this assures that the initialization form is re-evaluated for each new thread where the variable is used.

(defclass thread-local-variable (dynamic-variable) ())

(defmethod initialize-instance :after
    ((self thread-local-variable) &key initfun initval)
  (declare (ignore self initfun initval)))

(defparameter *default-thread-local-variable-class*
  #-fake-progv-kludge 'standard-thread-local-variable
  #+fake-progv-kludge 'surrogate-thread-local-variable)

(defun make-thread-local-variable (&rest initargs)
  (apply #'make-dynamic-variable-using-key
         *default-thread-local-variable-class* initargs))

(defmacro create-tls-variable (&optional (form nil fp) &rest initargs)
  `(make-thread-local-variable 
    ,@(when fp `(:initfun (lambda () ,form)))
    ,@initargs))

(defmacro define-tls-variable (name &rest initform-and-initargs)
  `(defvar ,name (create-tls-variable ,@initform-and-initargs)))

(defmacro define-tls-parameter (name &rest initform-and-initargs)
  `(defparameter ,name (create-tls-variable ,@initform-and-initargs)))

Perhaps it is a good time to introduce a new convention for tls variable names. I think that surrounding names with the minus sign is a nice idea, because it signifies, that it is something less than a global value. For example:

DYNAMIC-VARS> (define-tls-variable -context- 
                  (progn
                    (print "Initializing context!")
                    (list :context)))
-CONTEXT-
DYNAMIC-VARS> -context-
#<a EU.TURTLEWARE.DYNAMIC-VARS::STANDARD-THREAD-LOCAL-VARIABLE 0x7f7636c08640>
DYNAMIC-VARS> (dref -context-)

"Initializing context!" 
(:CONTEXT)
DYNAMIC-VARS> (dref -context-)
(:CONTEXT)
DYNAMIC-VARS> (dset -context- :the-new-value)

:THE-NEW-VALUE
DYNAMIC-VARS> (dref -context-)
:THE-NEW-VALUE
DYNAMIC-VARS> (bt:make-thread
               (lambda ()
                 (print "Let's read it!")
                 (print (dref -context-))))
#<process "Anonymous thread" 0x7f7637a26cc0>

"Let's read it!" 
"Initializing context!" 
(:CONTEXT) 
DYNAMIC-VARS> (dref -context-)
:THE-NEW-VALUE

The implementation

You might have noticed the inconspicuous operator DYNAMIC-VARIABLE-BINDINGS that is part of the protocol. It returns an opaque object that represents values of the dynamic variable in the current context:

  • for STANDARD-DYNAMIC-VARIABLE it is a symbol
  • for SURROGATE-DYNAMIC-VARIABLE it is a thread-local list of bindings

In any case all other operators first take this object and then use it to read, write or bind the value. The gist of the tls variables implementation is to always return an object that is local to the thread. To store these objects we will use the tls-table we've defined earlier.

(defclass thread-local-variable-mixin (dynamic-variable)
  ((tls-table
    :initform (make-tls-table)
    :reader dynamic-variable-tls-table)
   (tls-initfun
    :initarg :initfun
    :initform nil
    :accessor thread-local-variable-initfun)
   (tls-initval
    :initarg :initval
    :initform +fake-unbound+
    :accessor thread-local-variable-initval)))

For the class STANDARD-THREAD-LOCAL-VARIABLE we will simply return a different symbol depending on the thread:

(defclass standard-thread-local-variable (thread-local-variable-mixin
                                         thread-local-variable
                                         standard-dynamic-variable)
  ())

(defmethod dynamic-variable-bindings ((tvar standard-thread-local-variable))
  (flet ((make-new-tls-bindings ()
           (let ((symbol (gensym))
                 (initval (thread-local-variable-initval tvar))
                 (initfun (thread-local-variable-initfun tvar)))
             (cond
               ((not (eq +fake-unbound+ initval))
                (setf (symbol-value symbol) initval))
               ((not (null initfun))
                (setf (symbol-value symbol) (funcall initfun))))
             symbol)))
    (let ((key (bt:current-thread)))
      (with-tls-table (tls-table (dynamic-variable-tls-table tvar))
        (or (gethash key tls-table)
            (setf (gethash key tls-table)
                  (make-new-tls-bindings)))))))

And for the class SURROGATE-THREAD-LOCAL-VARIABLE the only difference from the SURROGATE-DYNAMIC-VARIABLE implementation is to cons a new list as the initial value (even when it is unbound) to ensure it is not EQ to +CELL-UNBOUND+.

(defclass surrogate-thread-local-variable (thread-local-variable-mixin
                                          thread-local-variable
                                          surrogate-dynamic-variable)
  ())

(defmethod dynamic-variable-bindings ((tvar surrogate-thread-local-variable))
  (flet ((make-new-tls-bindings ()
           (let ((initval (thread-local-variable-initval tvar))
                 (initfun (thread-local-variable-initfun tvar)))
             (cond
               ((not (eq +fake-unbound+ initval))
                (list initval))
               ((not (null initfun))
                (list (funcall initfun)))
               (t
                (list +fake-unbound+))))))
    (let ((key (bt:current-thread)))
      (with-tls-table (tls-table (dynamic-variable-tls-table tvar))
        (or (gethash key tls-table)
            (setf (gethash key tls-table)
                  (make-new-tls-bindings)))))))

That's all, now we have two implementations of thread-local variables. Ramifications are similar as with "ordinary" dynamic variables - the standard implementation is not advised for SBCL, because it will crash in LDB.

Thread-local slots

First we are going to allow to defined dynamic variable types with an abbreviated names. This will enable us to specify in the slot definition that type, for example (MY-SLOT :DYNAMIC :TLS :INITFORM 34)

;;; Examples how to add shorthand type names for the dynamic slots:

(defmethod make-dynamic-variable-using-key ((key (eql :tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         *default-thread-local-variable-class* initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :normal-tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'standard-thread-local-variable initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :kludge-tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'surrogate-thread-local-variable initargs))

;;; For *DEFAULT-DYNAMIC-VARIABLE* specify :DYNAMIC T.

(defmethod make-dynamic-variable-using-key ((key (eql :normal-dyn)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'standard-dynamic-variable initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :kludge-dyn)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'surrogate-dynamic-variable initargs))

In order to do that, we need to remember he value of the argument :DYNAMIC. We will read it with DYNAMIC-VARIABLE-TYPE and that value will be available in both direct and the effective slot:

;;; Slot definitions
;;; There is a considerable boilerplate involving customizing slots.
;;;
;;; - direct slot definition: local to a single defclass form
;;;
;;; - effective slot definition: combination of all direct slots with the same
;;;   name in the class and its superclasses
;;;
(defclass dynamic-direct-slot (mop:standard-direct-slot-definition)
  ((dynamic :initform nil :initarg :dynamic :reader dynamic-variable-type)))

;;; The metaobject protocol did not specify an elegant way to communicate
;;; between the direct slot definition and the effective slot definition.
;;; Luckily we have dynamic bindings! :-)
(defvar *kludge/mop-deficiency/dynamic-variable-type* nil)

;;; DYNAMIC-EFFECTIVE-SLOT is implemented to return as slot-value values of the
;;; dynamic variable that is stored with the instance.
;;;
;;; It would be nice if we could specify :ALLOCATION :DYNAMIC for the slot, but
;;; then STANDARD-INSTANCE-ACCESS would go belly up. We could make a clever
;;; workaround, but who cares?
(defclass dynamic-effective-slot (mop:standard-effective-slot-definition)
  ((dynamic :initform *kludge/mop-deficiency/dynamic-variable-type*
            :reader dynamic-variable-type)))

Moreover we specialize the function MAKE-DYNAMIC-VARIABLE-USING-KEY to the effective slot class. The initargs in this method are meant for the instance. When the dynamic variable is created, we check whether it is a thread-local variable and initialize its INITVAL and INITFUN to values derived from INITARGS, MOP:SLOT-DEFINITION-INITARGS and MOP:SLOT-DEFINITION-INITFUN:

(defmethod make-dynamic-variable-using-key
    ((key dynamic-effective-slot) &rest initargs)
  (let* ((dvar-type (dynamic-variable-type key))
         (dvar (make-dynamic-variable-using-key dvar-type)))
    (when (typep dvar 'thread-local-variable)
      (loop with slot-initargs = (mop:slot-definition-initargs key)
            for (key val) on initargs by #'cddr
            when (member key slot-initargs) do
              (setf (thread-local-variable-initval dvar) val))
      (setf (thread-local-variable-initfun dvar)
            (mop:slot-definition-initfunction key)))
    dvar))

The rest of the implementation of DYNAMIC-EFFECTIVE-SLOT is unchanged:

(defmethod mop:slot-value-using-class
    ((class standard-class)
     object
     (slotd dynamic-effective-slot))
  (dref (slot-dvar object slotd)))

(defmethod (setf mop:slot-value-using-class)
    (new-value
     (class standard-class)
     object
     (slotd dynamic-effective-slot))
  (dset (slot-dvar object slotd) new-value))

(defmethod mop:slot-boundp-using-class
  ((class standard-class)
   object
   (slotd dynamic-effective-slot))
  (dynamic-variable-bound-p (slot-dvar object slotd)))

(defmethod mop:slot-makunbound-using-class
  ((class standard-class)
   object
   (slotd dynamic-effective-slot))
  (dynamic-variable-makunbound (slot-dvar object slotd)))

The implementation of CLASS-WITH-DYNAMIC-SLOTS is also very similar. The first difference in that ALLOCATE-INSTANCE calls MAKE-DYNAMIC-VARIABLE-USING-KEY instead of MAKE-DYNAMIC-VARIABLE and supplies the effective slot definition as the key, and the instance initargs as the remaining arguments. Note that at this point initargs are already validated by MAKE-INSTANCE. The second difference is that MOP:COMPUTE-EFFECTIVE-SLOT-DEFINITION binds the flag *KLUDGE/MOP-DEFICIENCY/DYNAMIC-VARIABLE-TYPE* to DYNAMIC-VARIABLE-TYPE.

;;; This is a metaclass that allows defining dynamic slots that are bound with
;;; the operator SLOT-DLET, and, depending on the type, may have thread-local
;;; top value.
;;;
;;; The metaclass CLASS-WITH-DYNAMIC-SLOTS specifies alternative effective slot
;;; definitions for slots with an initarg :dynamic.
(defclass class-with-dynamic-slots (standard-class) ())

;;; Class with dynamic slots may be subclasses of the standard class.
(defmethod mop:validate-superclass ((class class-with-dynamic-slots)
                                    (super standard-class))
  t)

;;; When allocating the instance we initialize all slots to a fresh symbol that
;;; represents the dynamic variable.
(defmethod allocate-instance ((class class-with-dynamic-slots) &rest initargs)
  (let ((object (call-next-method)))
    (loop for slotd in (mop:class-slots class)
          when (typep slotd 'dynamic-effective-slot) do
            (setf (mop:standard-instance-access
                   object
                   (mop:slot-definition-location slotd))
                  (apply #'make-dynamic-variable-using-key slotd initargs)))
    object))

;;; To improve potential composability of CLASS-WITH-DYNAMIC-SLOTS with other
;;; metaclasses we treat specially only slots that has :DYNAMIC in initargs,
;;; otherwise we call the next method.
(defmethod mop:direct-slot-definition-class
    ((class class-with-dynamic-slots) &rest initargs)
  (loop for (key) on initargs by #'cddr
        when (eq key :dynamic)
          do (return-from mop:direct-slot-definition-class
               (find-class 'dynamic-direct-slot)))
  (call-next-method))

(defmethod mop:compute-effective-slot-definition
    ((class class-with-dynamic-slots)
     name
     direct-slotds)
  (declare (ignore name))
  (let ((latest-slotd (first direct-slotds)))
    (if (typep latest-slotd 'dynamic-direct-slot)
        (let ((*kludge/mop-deficiency/dynamic-variable-type*
                (dynamic-variable-type latest-slotd)))
          (call-next-method))
        (call-next-method))))

(defmethod mop:effective-slot-definition-class
    ((class class-with-dynamic-slots) &rest initargs)
  (declare (ignore initargs))
  (if *kludge/mop-deficiency/dynamic-variable-type*
      (find-class 'dynamic-effective-slot)
      (call-next-method)))

Finally the implementation of SLOT-DLET does not change:

;;; Accessing and binding symbols behind the slot. We don't use SLOT-VALUE,
;;; because it will return the _value_ of the dynamic variable, and not the
;;; variable itself.
(defun slot-dvar (object slotd)
  (check-type slotd dynamic-effective-slot)
  (mop:standard-instance-access
   object (mop:slot-definition-location slotd)))

(defun slot-dvar* (object slot-name)
  (let* ((class (class-of object))
         (slotd (find slot-name (mop:class-slots class)
                      :key #'mop:slot-definition-name)))
    (slot-dvar object slotd)))

(defmacro slot-dlet (bindings &body body)
  `(dlet ,(loop for ((object slot-name) val) in bindings
                collect `((slot-dvar* ,object ,slot-name) ,val))
     ,@body))

Finally we can define a class with slots that do not share the top value:

DYNAMIC-VARS> (defclass c1 ()
                  ((slot1 :initarg :slot1 :dynamic nil :accessor slot1)
                   (slot2 :initarg :slot2 :dynamic t   :accessor slot2)
                   (slot3 :initarg :slot3 :dynamic :tls :accessor slot3))
                  (:metaclass class-with-dynamic-slots))
#<The EU.TURTLEWARE.DYNAMIC-VARS::CLASS-WITH-DYNAMIC-SLOTS EU.TURTLEWARE.DYNAMIC-VARS::C1>
DYNAMIC-VARS> (with-slots (slot1 slot2 slot3) *object*
                (setf slot1 :x slot2 :y slot3 :z)
                (list slot1 slot2 slot3))
(:X :Y :Z)
DYNAMIC-VARS> (bt:make-thread
               (lambda ()
                 (with-slots (slot1 slot2 slot3) *object*
                   (setf slot1 :i slot2 :j slot3 :k)
                   (print (list slot1 slot2 slot3)))))

#<process "Anonymous thread" 0x7f76424c0240>

(:I :J :K) 
DYNAMIC-VARS> (with-slots (slot1 slot2 slot3) *object*
                (list slot1 slot2 slot3))
(:I :J :Z)

What can we use it for?

Now that we know how to define thread-local variables, we are left with a question what can we use it for. Consider having a line-buffering stream. One possible implementation could be sketched as:

(defclass line-buffering-stream (fancy-stream)
  ((current-line :initform (make-adjustable-string)
                 :accessor current-line)
   (current-ink :initform +black+
                :accessor current-ink)))

(defmethod stream-write-char ((stream line-buffering-stream) char)
  (if (char= char #
ewline)
      (terpri stream)
      (vector-push-extend char (current-line stream))))

(defmethod stream-terpri ((stream line-buffering-stream))
  (%put-line-on-screen (current-line stream) (current-ink stream))
  (setf (fill-pointer (current-line stream)) 0))

If this stream is shared between multiple threads, then even if individual operations and %PUT-LINE-ON-SCREEN are thread-safe , we have a problem. For example FORMAT writes are not usually atomic and individual lines are easily corrupted. If we use custom colors, these are also a subject of race conditions. The solution is as easy as making both slots thread-local. In that case the buffered line is private to each thread and it is put on the screen atomically:

(defclass line-buffering-stream (fancy-stream)
  ((current-line
    :initform (make-adjustable-string)
    :accessor current-line
    :dynamic :tls)
   (current-ink
    :initform +black+
    :accessor current-ink
    :dynamic :tls))
  (:metaclass class-with-dynamic-slots))

Technique is not limited to streams. It may benefit thread-safe drawing, request processing, resource management and more. By subclassing DYNAMIC-VARIABLE we could create also variables that are local to different objects than processes.

I hope that you've enjoyed reading this post as much as I had writing it. If you are interested in a full standalone implementation, with tests and system definitions, you may get it here. Cheers!




of

All Souls Night (Part 15 of 31)

.


 




 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*




of

One Week E-Book Sale of Vacuum Flowers!!! One Full Week!!!

.

 


Open Road Media, which publishes several of my e-books, has announced a one-week reduction in price of Vacuum Flowers. Starting this October 18 and running through October 25, 2024, it will be available for $1.99. That's in the US only.

So if you're an e-book reader and have been curious about my novel... well, there you are.


And if you don't already know . . .

Vacuum Flowers is what used to be called a Grand Tour of the Solar System. Rebel Elizabeth Mudlark is operating off of stolen wetware and on the run from very dangerous people. She arrives in the inner system on a cometary orbit, which takes her through a great variety of human and post-human societies, including the most dangerous one of all--Earth.

That bit about the cometary orbit is not incidental. Comets enter the Inner System on either a hyperbolic or a parabolic orbit. The one is open, the other closed. I knew that the book would end with Rebel Elizabeth Mudlark standing in the stardocks with a coffin at her feet. But I didn't know if the person within the coffin would be alive or dead or if REM would someday return to the Inner System or was leaving it forever. I only decided that when I came to write the last page.


*




of

All Souls Night (Part 16 of 31)

 .

 


 


 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*




of

All Souls Night (Part 17 of 31)

.


 

 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*




of

All Souls Night (Part 18 of 31)

.


 


 

 

 

 

 

 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where. I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

 




of

"Dragons of Paris" and the Role of Time in the Mongolian Wizard Series

 .

The kind people at Reactor Magazine have posted my two latest Mongolian Wizard stories, one yesterday and the other today. Thursday's "Halcyon Afternoon" took place during a rare moment of peace for Franz-Karl Ritter. But in today's "Dragons of Paris," it's warfare as usual. 

Time has always been a little tricky in this series. The first story was clearly set in the Nineteenth Century but, though only a few years have passed, the series has now reached what is recognizably World War I. Mostly this occurred for reasons explained in "The Phantom in the Maze" and "Murder in the Spook House." (And which I anticipate giving me increasing difficulties in writing the next ten stories.) But also, in a more literary background sense, I wanted to cover the transition from a way of life now alien to us to something more modern, if not contemporary. 

So time may get a bit more slippery in the future. That's if, of course, the stories go in the direction I intend. Sometimes the fiction has its own ideas where it wants to go and the author can only follow along meekly in its wake.

You can read the story here. Or just go to the ezine and poke around. It's a good place to poke around.


Above: The illustration is by Dave Palumbo. I'm grateful for that.


*




of

All Souls Night (Part 19 of 31)

.


 


 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 

 




of

All Souls Night (Part 20 of 31)

.



 

 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 




of

All Souls Night (Part 21 of 31)

 

.


 


 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 




of

All Souls Night (Part 22 of 31)

.


  


 



 

 

CONTINUED TOMORROW.

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 




of

All Souls Night (Part 23 of 31)

.

 




 





 


 

 

CONTINUED TOMORROW. (For those who came in late: The first sentence was posted here on October 1 and a new sentence was posted every day thereafter.)

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 




of

All Souls Night (Part 24 of 31)

.


 



 

CONTINUED TOMORROW. (For those who came in late: The first sentence was posted here on October 1 and a new sentence was posted every day thereafter.)

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 




of

All Souls Night (Part 25 of 31)






 



 

CONTINUED TOMORROW. (For those who came in late: The first sentence was posted here on October 1 and a new sentence was posted every day thereafter.)

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*

 

 




of

All Souls Night (Part 26 of 31)

.


 



 

 

CONTINUED TOMORROW. (For those who came in late: The first sentence was posted here on October 1 and a new sentence was posted every day thereafter.)

 

Above: Every Autumn, I write a Halloween story, write it out on leaves (one word per leaf), photograph the leaves, and then leave them where.I found them. The story is then serialized, starting on October 1 and concluding on the 31st--All Souls Day.

 

*