I7 syntax with more than 9 indents

I use Inform 7 (5Z71), and write using the “pythonesque” style of tab-indented code (because unfortunately I find the “begin if/end if” style utterly incomprehensible and confusing).

I’ve been having a lot of trouble for some time now because Inform seems to refuse to compile any individual line of code that contains more than nine indentations when using the “pythonesque” writing style. Until now, I’ve always found a way to break my code down into logically “simpler” units to avoid using more than nine indentations (even though the result is often so verbose that it’s nearly impossible for me to read afterwards, and the time required to write or copy/paste line after line of mostly redundant code is very annoying). However, I am currently working on some things that are just too complex to write without going beyond nine indentations.

Since the problem I’ve been having with Inform is in my opinion a very serious limitation to its practical functionality, I would have expected to see at least a few discussions of this issue in threads here (or at the ‘raif’ newsgroup). However despite several searches, I haven’t found any mention at all of the problem. Since I can hardly be the first person who has ever written a semi-complex code-rule in Inform 7, I’m therefore thinking that perhaps I’m doing something wrong in my style of writing. I’m hoping some of the experts here might either point out an error in my method, know of an undocumented feature or setting that will enable Inform to read lines of code that contain more than nine tabs, or otherwise have some suggestions for generally dealing with the problems I’ve been having.

In simple terms, my difficulties arise from Inform refusing to compile code in the format of the following example–in fact, the compile process stops almost instantly after pressing “Go” in a way which indicates to me the program isn’t even trying to work things out.

The testarea is a room.

Condition1 is a kind of value.  The condition1s are aa, bb, cc, dd, ee, ff, gg, hh, ii, and jj.

A thing has condition1.  A thing is usually aa.

To say status for (item - a thing):
	if item is aa:
		say "A.";
	otherwise:
		if item is bb:
			say "B.";
		otherwise:
			if item is cc:
				say "C.";
			otherwise:
				if item is dd:
					say "D.";
				otherwise:
					if item is ee:
						say "E.";
					otherwise:
						if item is ff:
							say "F.";
						otherwise:
							if item is gg:
								say "G.";
							otherwise:
								if item is hh:
									say "H.";
								otherwise:
									if item is ii:
										say "I.";
									otherwise if item is jj:
										say "J.".

The description of the player is "[status for the player]".

While attempting to compile the above code, Inform quickly spits out the following rather senseless error message:

“Problem: The phrase or rule definition 'To say status for (item - a thing) is written using the ‘colon and indentation’ syntax for its 'if’s, 'repeat’s and ‘while’s’, but that’s only allowed if each phrase in the definition occurs on its own line. So phrases like ‘say "I.’”, which follow directly on from the previous phrase, aren’t allowed.".

The real problem, however, is that Inform 7 isn’t able to read any particular line that has more than nine tabs (or at least it won’t read such lines for me). If the last lines of the above are changed to:

if item is hh:
	say "H.";
otherwise:
	say "Groovy.".

there is no problem, since the lines “say ‘H.’” and “say ‘Groovy.’” are only indented by nine tabs.

Of course the above example is much ado about nothing, since it can be easily rewritten using only a few tabs:

To say status for (item - a thing):
 	if item is aa:
		say "A.";
	otherwise if item is bb:
		say "B.";
	otherwise if item is cc:
		say "C.";
	otherwise if item is dd:
		say "D.";
	otherwise if item is ee:
		say "E.";
	otherwise if item is ff:
		say "F.";
	otherwise if item is gg:
		say "G.";
	otherwise if item is hh:
		say "H.";
	otherwise if item is ii:
		say "I.";
	otherwise:
		say "J.".

Or even in several other various ways that allow me to compile it to full functionality.

However I am frequently attempting to write code which is not nearly as simple as the above example. If a given rule needs to consider a large enough number of factors, it either simply isn’t possible to express with less than ten indentations, or becomes so artificially complex that it’s nearly impossible to comprehend and debug after being written using less than ten indentations.

For example, here is the first section of something I was working on today (for a liquid/fluid system) that shows the trouble I’m having in more practical terms; it’s definitely a bit cryptic without an explanation of all the variables and activities used, but generally demonstrates what I’m talking about:

Carry out an actor pouring:
	let currentcarrycapacity be the carrying capacity of the actor;
	now the carrying capacity of the actor is 100;
	if the noun is a notsource liquid and the second noun is a notsource liquid:
		if the liqsim of the noun is the liqsim of the second noun:
			if the noun is in a fluidworthy container (called pouredholder) and the second noun is in a fluidworthy container (called poureeholder):
				let noun2containercapacity be the maxvolume of the poureeholder minus the currentvolume of the poureeholder;
				if the liquidvolume of the noun is greater than noun2containercapacity or the liquidvolume of the noun is noun2containercapacity:
					decrease the liquidvolume of the noun by noun2containercapacity;
					increase the liquidvolume of the second noun by noun2containercapacity;
					if the liquidvolume of the noun is less than 1:
						fixdensity for the second noun;
						repeat with newhome running through things in the noun:
							move newhome to the actor;
							silently try the actor inserting newhome into the second noun;
							if newhome is not in the second noun:
								move newhome to the pouredholder;
								now newhome is wet;
								change the dampstoragevalue of newhome to the passonliquid of the noun;
								repeat with updatewetness running through things enclosed by newhome:
									unless updatewetness is in a closed fluidworthy container or updatewetness is incorporated by something in a closed fluidworthy container or updatewetness is incorporated by something that is incorporated by something in a closed fluidworthy container or updatewetness is incorporated by something that is incorporated by something that is incorporated by something in a closed fluidworthy container:
										now updatewetness is wet;
										change the dampstoragevalue of updatewetness to the passonliquid of the noun;
						remove the noun from play;
					fluidadjust pouredholder;
					fluidadjust poureeholder;
				if the liquidvolume of the noun is less than noun2containercapacity:

				[and so forth for dozens of lines]

I don’t especially want to discuss or debate the details of the above example (not because I’m particularly taciturn but rather because my point is I’m generally having problems with all kinds of code rather than just this one instance) other than to say that it won’t compile for me only because the lines:

now updatewetness is wet;
change the dampstoragevalue of updatewetness to the passonliquid of the noun;

are at the tenth level of indentation.

As I mentioned earlier, since I can hardly be the first person to ever write semi-complex code like the above in Inform 7, I suppose I must be doing something wrong–however, I don’t understand what that may be, so hopefully someone will have some suggestions.

I certainly don’t need dozens of indents, but I frequently find myself writing code that either would require indents in the ten to fifteen range, or would be much easier to conceptualize, write, and subsequently read if I could use ten to fifteen indents. I’m not particularly enthusiastic about trying to learn the “begin if/end if” system, but if that’s what it would take to make parts of my code compilable I’ll try it. However if I were to guess it seems sensible to assume that since the “begin if/end if” style also uses indentations, it would suffer from the same limitation as my current style. On the other hand, since again however I can hardly be the first person to ever write code like the above, the fact must be that I am doing something wrong.

In short, I’m confused and not really able to continue my Inform work anymore without either a significant revision of my expectations about the complexity of my simulated world-model or a solution to this problem. I would appreciate any guidance, and thanks in advance for any suggestions.

p.s.: Sorry, I just tried copying/pasting some of the boxed text above and it seems I haven’t used the board’s “code posting” function properly as my tabs weren’t preserved within the boxed sections. In any case, it should be clear from the way the text is displayed where the tabs are supposed to be.

This may in fact be a bug, but I would suggest that you reevaluate your approach. Obviously I can’t tell you what programming practices are best for you to “conceptualize, write, and subsequently read,” but I can say that your posted code does appear to have several places where indentation can be reduced. Unfortunately, it’s hard for me to give you a concrete example since you cut off the code block before any of the “otherwise” portions of the early conditionals. For example:... if the noun is in a fluidworthy container (called pouredholder) and the second noun is in a fluidworthy container (called poureeholder): ...What happens if both nouns aren’t fluidworthy containers? If the action fails in some way this probably should have been in an initial check rule. If it partially succeeds in a way that requires a completely different set of code – for example situations where you pour a gallon of water into a leaky half-gallon container and you want the leaky container to leak, but the original container to remain half full – you could put that in a separate carry out rule, as in:[code]Carry out pouring (this is the pouring one fluidcontainer to another rule):
if the noun is in a fluidworthy container (called pouredholder) and the second noun is in a fluidworthy container (called poureeholder):

Carry out pouring (this is the pouring one fluidcontainer to a nonfluidcontainer rule):

[/code]If you need to do different calculations for determining the pouredholder and poureeholder which vary depending on the source or nature of the liquid, you could do that up front as action variables of the pouring action:[code]The pouring action has an object called the pouredholder.

Setting action variables for pouring:
if the noun is in a fluidworthy container, now the pouredholder is the holder of the noun;

[/code]
In short, there are usually lots of ways of breaking it up that don’t involve redundant code (other than repeating the name of the rule in some cases) and in fact will reduce redundancy. I looked through an unpublished extension of mine which contains some pretty complex rules and conditions (over 50 rules and around 45 new phrases) and the the greatest indent depth I used was five (in two rules). The vast majority were in the 2 - 4 range. It may seem easier at first to have one huge nested block, but in my experience it’s much harder to debug or alter later. Think of it this way, is it easier to have a nested conditional where the first branch is a dozen lines above the second branch, or to break it down? :slight_smile:

If you like, you can send me your complete code and I can give you an example of how I would reorganize it and a more detailed explanation of why. Then you can decide for yourself if that’s a better approach for you. I’ll pm you w/ my email info.

You should probably email Graham about this, as it appears to be a hard-coded limitation in the compiler. My guess is, nobody has yet tried using more than 9 levels of indentation, and for the reason Mike alluded to: Organizing the code is easier if it’s done in a different manner, by calling separate functions for the various cases you’re working through.

–JA

@Skinny Mike:

Thanks for your suggestions and your kind offer. I actually had never considered constructing such highly-specific Carry Out rules for different conditions that did not involve discrete identifiable objects; in fact for some reason, I wouldn’t have thought that would work (which I suppose is why I never tried it). In other words, while I often in fact make rules such as:

Carry out pouring:
do boring stuff.

Carry out pouring the bleach into the ammonia:
say “A noxious cloud fills the room.”;
end the game in death.

I never considered an intermediate step such as:

Carry out pouring:
if the noun is viscous and the second noun is a person:
now the second noun is sticky;
now the second noun is upset with the player.

as a discrete “Carry Out” rule of its own. Your suggestion will be very helpful in allowing me to free up some additional tabs, since apparently the whole tab problem isn’t just in my imagination.

After reading your response as well as Jim Aikin’s, I guess my work-style is different from veteran practices. For example, the “pouring” action described in my original post is in fact a generalized “pouring/dumping/emptying into” action. When I sat down to write the rule, I first started off by making a long list of conditions to consider, such as:

[liquid: a very specific kind of container, that has a variable called 'liqsim' which provides things such as its description and printed name, as well as indicating how mixing it with other liquids will play out; Issource or notsource liquid: separates things like a lake and a stream of tap-water from a water puddle or a cupfull of coffee; Fluidworthy or not container: a general characterization of all containers; fluidworthy containers can be hasfluid if they contain a liquid, or otherwise are not hasfluid]

Carry out pouring:
	[if the noun is a liquid that is not a liquid source and the second noun is a liquid that is not a liquid source]
		[if the liquids of the objects of are the same]
			[if both liquids are in a container]
			[if only the first liquid is in a container]
			[if only the second liquid is in a container]
			[if neither liquid is in a container]
		[if the liquids are different, we will apply the generic mixing rules]
			[if both liquids are in a container]
			[if only the first liquid is in a container]
			[if only the second liquid is in a container]
			[if neither liquid is in a container]

[skipping ahead a few paragraphs to avoid listing all the boring iterations of noun is a liquid and second noun is a liquid]

	[if the noun is a hasfluid container and the second noun is not a liquid source]
		[if the liquid in the noun is the same as the liquid of the second noun]
			[if the container's liquid will be emptied into the liquid]
				[if anything in the first container can be moved to the liquid]
				[otherwise the item stays in the container but is now dripping wet]
			[otherwise if the first container retains some liquid]
		[etc]

	[if the noun is a hasfluid container and the second noun is a hasfluid container]
		[if the liquids in both are the same]
			[if the first container's liquid will be emptied into the second container]
				[if anything is in the first liquid and the item will fit into the second container]
				[otherwise the item stays in the original container, but is now dripping wet]
			[if the first container retains some liquid]
		[if the liquids are different and need to be generically mixed]
			[if the first container's liquid will be emptied into the second container]
				[if anything is in the first liquid and the item will fit into the second container]
				[otherwise the item stays in the original container, but is now dripping wet]
			[if the first container retains some liquid]
				[moving everything in the first container to the liquid in the second container]
				[if anything from the first container didn't fit or couldn't be moved]
		[etc]

[skipping ahead a few more paragraphs]

	[if the noun is a container that is not a liquid and isn't hasfluid and the second noun is a supporter]
	[if the noun is a container that is not a liquid and isn't hasfluid and the second noun is the location]
	[if the noun is a supporter and the second noun is a liquid]
	[if the noun is a supporter and the second noun is a hasfluid container]
	[if the noun is a supporter and the second noun is a container that isn't involved with liquids]

[and so forth, but I think you get the idea by this point]

I then just started at the top and went through line by line, filling in what I wanted to happen for each set of conditions. I find this method clarifies my thoughts about what I’m trying to accomplish (and often I just copy/paste when one side of a condition is unchanged from previous either/or checks), but I can see where others would find this process and its result completely unwieldly.

In any case, I found your suggestions to be very helpful. Separating the whole process into much more specific carry-out checks will definitely free up some apparently limited/valuable indentations–and I usually only need one or two extra indentations to work things out.

@Jim Aiken

Thanks for your response. I’m a little reluctant to e-mail the Inform creators about the issue, since if the tab-limit is indeed hard-coded increasing it would be more of a feature request than a bugfix, and apparently other than myself it wouldn’t be a feature anyone would use. Perhaps however this thread will come to their attention.

In any case, I would like to take this opportunity to say thank you very much for the wonderful Inform Handbook you wrote and made freely available. The target audience of the book may be school-kids, but speaking as a middle-aged person I found your reference book to be invaluable in deciphering the complexities of Inform 7. I read every page of it (twice, in fact) after I had spent considerable time studying the I7 documentation but was still mystified by the simplest principles of the program; had it not been for your fine book I may have given up on Inform rather continue to muddle on in frustration. For anyone trying to learn Inform 7, I would strongly recommend reading your book first before even looking at the documentation that comes with the program–your book is that good.

Thanks again, guys, for the fast responses.

Graham is certainly open to feature requests. Doesn’t mean he’ll implement them, but I’m sure he’s very interested in learning more about how people are using (or desiring to use) Inform. The compiler error message you encountered is certainly wrong, and therefore a bug.

The functionality you’re trying to implement (mixing liquids, it looks like) is certainly among the more complex challenges in IF. (The cocoa mug in “Lydia’s Heart” was a nightmare to code, and it had only four ingredients.) I’m pretty sure there are ways to encapsulate portions of your code so that you’d have fewer indents, but I wouldn’t presume to try to guess what would work best for your particular needs.

Thanks. I’m glad people are finding it useful – and yes, the emphasis on younger authors has become somewhat dated or less relevant. I’m working on an updated, expanded version of the Handbook. It’s mostly finished, but it won’t be released until after the next version of Inform, because I’ll want to bring it into conformity with whatever code changes may appear.

–JA