Everything is iterable?

The help page describing the foreach statement: https://technet.microsoft.com/en-us/library/hh847816.aspx

On this page you can see that the foreach is “a language construct for stepping through (iterating) a series of values in a collection of items.”. This is false. This is possible:

foreach ($Element in 1) {
    Write-Host $Element
}

Why is this possible?

Is there no distinction between a iterable item and a non-iterable item in powershell?

The about_ForEach is also a very confusing read since it mixes the foreach statement/keyword with the foreach-object cmdlet (which is aliased “foreach”). These aren’t the same. Is this some kind mixup of legacy docs?

PowerShell’s pretty aggressive about doing type conversions to help you out. If you pass something to a foreach that isn’t iterable, it’s probably just converting it to a single-element array behind the scenes. The main reason for this is probably to support commands that may return 0, 1 or more elements, such as this:

foreach ($item in Get-ChildItem)
{

}

On the other hand, there are some types that are iterable in .NET that won’t automatically trigger PowerShell’s foreach (strings and dictionaries). These get treated as single objects in PowerShell unless you explicitly call their GetEnumerator() methods.

And with regard to the documents, the scripting construct “foreach,” the “ForEach-Object” cmdlet, and the “foreach” alias have been around since v1.0, so it isn’t a mixup in the docs. It’s just the way it is. PowerShell figures out the difference between the alias and the construct based on context. I agree that it’s confusing to have all those identical keywords doing different things, but that’s what it is. I believe it’s even an article in “The Big Book of PowerShell Gotchas,” on our eBooks menu, because it stumps a lot of newcomers.

And to reinforce what Dave wrote, the documentation is not false. But, in your example, because ForEach expects a collection/array in the second position, PowerShell converts “1” to a single-object collection, which is enumerable. This happens, as Dave points out, a lot. For example, if you run:

Get-Service -ComputerName One

The -ComputerName parameter only accepts a string, which is a collection. Knowing that tossing an error for only providing one computer name would be annoying, the shell converts “One” into an array of one item. You can see this if you use Trace-Command to see what’s happening in the pipeline as the command is processed by the shell. As a programming language, PowerShell is very loosely typed and loosely structured, which is common for scripting languages. It doesn’t exhibit the same strict behaviors as a language like C#.

About foreach keyword vs foreach-object:
Yes, they are clearly separated because of the foreach keyword expecting parentheses after the keyword.

But shouldn’t the about_ForEach doc be only about the keyword? The ForEach-Object is documented in it’s own help pages. They are not the same thing (event though they are very similar).

About the input and converting scalars to single item collections:
I find the documentation to be false or at least misleading since it clearly says that the foreach keyword iterates collections. When some kind of conversion magic happens it should at least be mentioned.

The argument that this is i nice thing since e.g. Get-ChildItem might return a single item is valid. It would be awkward to handle this possibility if the foreach keyword couldn’t iterate single items. But: My personal opinion is that the fix is wrong. IMO the foreach keyword shouldn’t be able to loop single items. The Get-ChildItem should rather always return a collection. In stead of returning null, a single item or a collection of multiple items it should’ve return an empty collection, an collection of one item or a collection of multiple items.

The think the bottom line is that I find the language to be too loose. It should be much, much stricter. There is too much implicit stuff going on under the hood and in many cases this leads to unwanted and/or unpredictable behavior in scripts.

Ohwell. I guess I’m done ranting now :slight_smile: How these cmdlets return their values will probably never change now and I’ll just have to accept the behavior.

I don’t tend to worry about “should” something be the way it is or not. I’m not on the product team, so I just deal with what they produce ;).

You’re arguing against some of the fundamental ways that PowerShell works. I think it’s probably a little late for Microsoft to go back and change it. Most users seem pretty satisfied with it. I appreciate your personal opinion, but it’s outside my ability to address it. I can’t implement the changes you’re suggesting, I can only explain why things are the way they are. For me, engaging in a debate over them isn’t a productive use of my time, since regardless of the debate’s outcome, I can’t change any of it.

The language is loose by design. There are stricter languages, like C#, which might be more appropriate for you if that’s your preference. In fact, it is the existence of C# that helped make PowerShell more loosely typed - people who want a more formal programming language already had one.

PowerShell’s looseness is both a strength and a weakness - just as C#'s tightness is a strength and a weakness. Everything is a tradeoff, and so you have to look at why the tradeoffs were made, and what audience and purpose they were intended to serve. There’s no One True Answer that’s always right in every situation. Perhaps your situation would be better served by C# or Visual Basic, both of which can access the PowerShell engine and commands.

I think it’s worth remembering the roots of the language, too. PowerShell’s intent from day 1 was to make an easily interchangeable set of cmdlets for system configuration/scripting (monad manifesto and all that). Being overly strict/literal basically just “gets in the way” of the original intent of use: IT Admins who need to get “something” and then easily apply “something else” to it… essentially scripting on steroids. It has evolved to the point where people are writing games and programs as well as with Dev-Ops practices starting to hit infrastructure things continue to blur, but it’s roots are fundamentally different than some more “traditional” languages that come from a programming root.

This former “click-next guru” certainly appreciates the leniency :slight_smile:

Justin, you bring up a really good point. Paal, you may want to read GitBook - Where software teams break knowledge silos. for some of the original history on the shell. It’s interesting to me, at least.

Thanks for the manifesto link. I’ll check it out :slight_smile:

Yes, I agree that debating “I think this and that should’ve been different” when you can’t even do anything about it isn’t productive. What could be a productive discussion on the other hand is how to deal with these “flaws”, “inconveniences” or .

I think an example with Get-ChildItem illustrates the issue quite well. E.g. how would you get the number of objects returned from Get-ChildItem when the return type isn’t a single, strictly defined type?