5

Something I have noticed and never seen any explanation for is this odd little "fact". Most (all?) class methods I can call from Powershell use Pascal Case. For example

[System.IO.FileInfo].FullName
$xmlWriter.WriteStartDocument()
[System.GC]::Collect()

and many more. But the one exception I see all the time is new. New is (almost?) always lower case. Type [xml.xmlReaderSettings]:: and the helpful dropdown list provided is Equals, new & ReferenceEquals. This happens consistently, and I suspect it isn't a coincidence, so I am hoping someone can explain WHY. My thinking is that since the naming convention is for Public Methods and Parameters to use Pascal Case (while Private Methods and Parameters use Camel Case) and []::new is neither a property nor a parameter, it's indirectly a call to a Constructor, it gets Camel Case? Or not even Camel Case, just Lower Case, which in this situation looks the same.

I know that in PowerShell it doesn't matter, just curious what the underlying logic is.

Gordon
  • 6,257
  • 6
  • 36
  • 89
  • 3
    Your reasoning is sound, but the primary reason is much simpler: it's a replica of the `new` keyword in C# – Mathias R. Jessen Aug 29 '20 at 13:40
  • Ah, it's really a key word, which are also all lower case, so makes perfect sense. – Gordon Aug 29 '20 at 13:55
  • 1
    It's _not_ a keyword in PowerShell, but the decision to reuse the C#-keyword as-is comes down to exactly the reasons you pondered: it's _not_ a public static member (unlike anything else `::` exposes), keeping it lowercase communicates "this is a special method" – Mathias R. Jessen Aug 29 '20 at 13:57
  • So, "functions AS/LIKE a keyword" is probably how I will think of it. – Gordon Aug 29 '20 at 14:08

1 Answers1

6

new is lower-case because new is special

This answer is based in part on conversation(s) I've had with some of the involved parties (ie. PowerShell (ex-)team members at Microsoft), so it's more anecdotal fabulation than it is hard technical facts, but such is the nature of some "why did they do X instead of Y" question

TL;DR:
Your reasoning is sound, but it's not like a committee inside Microsoft sat down, looked at the .NET specs and decided "we MUST break with these naming conventions if we ever introduce a synthetic constructor invocation method, in the spirit of the defined standard" - as with most things in real life the decision was influenced by a confluence of factors, some of which I will recount below.


As you've noted in the question, new is not like the others - the static member invocation operator (::) only expose public static members, and .NET naming conventions dictate PascalCase'd names for public members.

But new breaks with that convention, and to appreciate why you need to look at the context of its origin and introduction in PowerShell 5.0

Origin of the new keyword for object allocation

Use of the name or keyword new in the context of PowerShell is directly lifted from C#!

new is baked into the C# language grammar as a keyword, and it's most well-known application is for instantiating new object instances by invoking the appropriate constructor:

var instance = new TypeName();

C# in turn took new from C++, a language in which it serves a similar purpose, automatic memory allocation for typed objects:

T instance = new TypeName;

C++ in turn took new from SmallTalk, the oldest language I know of that positively used it for object allocation:

instance := TypeName new

The authors of SmallTalk might have used new simply because they thought it fit for purpose, but one could also imagine they took inspiration from ALGOL68 in which I'm given to understand new was a common operator name for array-generating procedures (this part is 100% anecdotal speculation, I've never written or ran any ALGOL).

In any case, using lowercase new to name procedures that allocation objects in memory carries a 50 year legacy.

Constructor invocation in PowerShell <5.0

Before the introduction of this new quasi-method in PowerShell 5.0, the idiomatic way to invoke .NET constructors was to let New-Object handle it:

$instance = New-Object -TypeName 'Target.Type.Name' -ArgumentList @($ctorParamArgs,$go,$here)

New-Object had positional bindings for those two parameters from the initial release in 2.0, in hopes that this new-fangled and shiny, but very verbose, scripting language would appeal to existing users of the .NET ecosystem, particularly developers and sysadmins already familiar with C#:

# Lot's of typing on the keyboard in PowerShell ...
New-Object -TypeName 'TypeName' -ArgumentList @($arg)

# ... compared to C#
new TypeName(arg);

# The compromise - New-Object invoked with positional binding:
New-Object TypeName $arg

But some developers still felt PowerShell's command expression syntax was too verbose, and before long, script sharing repositories around the internet was littered with scripts that abused the Alias facility to mimic C#:

Set-Alias new New-Object
new TypeName($arg) # doesn't get much closer and this

So new, as a (fake) "constructor keyword", already existed in the community eco system around PowerShell, long before PowerShell 3.0 was even released, and a subset of users clearly yearned for a more concise way to allocate new objects.

PowerShell 5.0 and the advent of class definitions

A major feature in PowerShell 5.0 was the introduction of the enum and class keywords, with which PowerShell users all of a sudden could define their own CTS-compliant data type!

Being able to define new types at runtime is great, but of course you need to be able to use them - so what options are available?

  • New-Object - can instantiate PowerShell classes just fine, but it feels awkward to have the language itself be able to define types but have no built-in facility for instantiating them.
  • Introduce new as a new keyword in the grammar - except, what about compatibility with existing scripts that had abused the alias feature as described in the previous section - should the new new expressions support @() for the argument list?
  • Introduce new as a synthetic static member name on type literals - this turned out to be better for compatibility than you might think - New as a member name is an extremely rare occurrence in the .NET ecosystem (Create() being the canonical preference for user-defined constructor wrappers), so the change could be made without altering the language at all - just add special handling in the :: invoke member operator's implementation for when the member name is new.

The PowerShell team ended up choosing the latter, and decided to keep it lowercase in the vein of the ancestral languages discussed above, which in turn sends the message you've picked up on - this one is not like the others :)


Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • @mathias-r-jessen Well that was a fun little read. Wasn't expecting my idle curiosity to lead to quite so much history. Good times. – Gordon Aug 29 '20 at 16:20
  • `Algol68` defaults to taking stack memory eg. `REF INT ptr := LOC INT`. Or a request can get some global space, eg. `REF INT ptr := HEAP INT` U can't define you own allocator *keyword* (eg `REF REAL ptr := XTASKMEM REAL`) as an alternative to LOC & HEAP. However: There are some nice (maybe annoying) safeguards. eg. the compiler might pick up cases where you assigned a stack address into a global pointer. For some more examples consult: [Semantic differences in Algol and C in casting](https://stackoverflow.com/questions/41993990/semantic-differences-in-algol-and-c-in-casting/54032884) – NevilleDNZ Oct 31 '20 at 04:06