Powershell: Sugestão: implementar coalescência nula, acesso condicional nulo (imersão de nulo), atribuição condicional nula

Criado em 2 mar. 2017  ·  71Comentários  ·  Fonte: PowerShell/PowerShell

A coalescência nula e o acesso condicional nulo (imersão de nulos) seriam adições úteis à linguagem.

_Update_: @BrucePay sugere adicionalmente null-conditional _assignment_ , com ?= - veja o comentário abaixo .

Por exemplo, em vez de escrever:

if ($null -ne $varThatMayBeNull) { $varThatMayBeNull } else { $fallbackValue }
#
if ($null -ne $varThatMayBeNull) { $varThatMayBeNull.Name } else { $null }

alguém pode ser capaz de escrever:

$varThatMayBeNull ?? $fallbackValue  # null-coalescing
# 
$varThatMayBeNull?.Name   # null-conditional access, for Set-StrictMode -Version 2+
$varThatMayBeNull?[42]  # ditto, with indexing

Re acesso condicional nulo: Com Set-StrictMode desativado (o padrão), você pode apenas usar $varThatMayBeNull.Name - nenhuma sintaxe especial necessária; no entanto, se Set-StrictMode -Version 2 ou superior estiver em vigor, $varThatMayBeNull.Name _break_ e é aí que o operador nulo-condicional ( ?. ) é útil, para sinalizar a intenção explícita de ignorar o $null forma concisa.


Pergunta aberta :

$varThatMayBeNull?[42] trata do caso em que a variável é $null , mas se não for, um elemento de matriz com o índice especificado _deve existir_.

Portanto, também seria útil tornar _indexing_ nulo-condicional - algo que o C # _não_ suporta, incidentalmente (você deve usar .ElementAtOrDefault() ).

As duas opções básicas são:

  • Crie uma sintaxe adicional que sinalize explicitamente a intenção de ignorar um _index_ inexistente:

    • A questão é qual sintaxe escolher para isso, dado que ?[...] e [...]? não são uma opção devido à ambigüidade.
    • Talvez [...?] , mas isso parece estranho.
  • Confie no comportamento existente em relação ao acesso a índices não existentes, conforme implícito na configuração Set-StrictMode - consulte a tabela abaixo .


Relacionado: implementar condicionais ternários

Issue-Enhancement Resolution-Fixed WG-Language

Comentários muito úteis

@rjmholt eu fiz uma análise aqui . Uma análise rápida: de quase 400.000 scripts com mais de 22.000.000 de nomes de variáveis ​​(não exclusivos), havia apenas ~ 70 variáveis ​​exclusivas que terminavam com ? .

Todos 71 comentários

@ mklement0 Parece que o segundo exemplo funciona por design, sem "?".
Talvez mude para name() ?

@iSazonov : Bom ponto, mas só funciona sem ? menos que Set-StrictMode -Version 2 ou superior _não_ esteja em vigor - atualizei a postagem inicial para deixar isso claro.

Acho que o açúcar sintático é algo que o PowerShell pode usar e que outras linguagens gostam. +1 na proposta.

Considere os seguintes exemplos de outros idiomas:

a="${b:-$c}"
a = b || c;
a = b or c
a := b ? c
a = b ? b : c;

até

a = b if b else c

é melhor que

if (b) { a = b } else { a = c }

e muitas vezes você precisa esclarecer com o detalhamento adicional

a = (if (b -ne $ null) {b} else {c})

Faz a pessoa se sentir suja e destruída.

Encontrei-me fazendo isso hoje como uma solução alternativa.

`` `Powershell
$ word = ($ null, "two", "three"). Onde ({$ _ -ne $ null}, "First")
`` ``

Eu uso um padrão semelhante:

$word = ($null, "two", "three" -ne $null)[0]

@kfsone Há um pequeno erro em seu exemplo $a=(if ($b -ne $null) { $b } else { $c }) . Deve ser $a=$(if ($b -ne $null) { $b } else { $c }) porém a única versão do PowerShell em que $( ) era necessária era a versão 1. A partir da v2, você pode simplesmente fazer:

$a = if ($b) { $b } else { $c }

@bgshacklett Você não quer dizer $word=($null,'two')[$null -ne 'three'] ?

É um pouco lamentável que tenha passado de 6.1 para 6.2 para "Futuro".

@ TheIncorrigible1 Não, se você copiar e colar o que adicionei acima, verá que o valor de $word está definido como "dois". Há mais detalhes nesta resposta do Stack Overflow, onde vi o padrão pela primeira vez:
https://stackoverflow.com/a/17647824/180813

Enquanto hack-arounds perlescos atraem o eu que escrevi um shebang que registrou e / ou consultou um usuário RIPE-DB ou registro de organização, o que espero do PowerShell é algo que incentive meus colegas a usar a linguagem ao invés de instilar medo neles.

Meu teste decisivo é este: Será que eu gostaria de ler isso no meu tablet às 3h da manhã de Ano Novo, enquanto estava de ressaca com o CEO no telefone chorando comigo quantos milhões de dólares estamos perdendo por segundo?

(Aparte: isso foi apenas um exagero de uma experiência real até que eu trabalhei no Facebook e voltei de um vazamento rápido e urgente para ser informado de que, nos 2 minutos que estive fora, mais pessoas do que a população da Holanda tinham um vazio feed de notícias. Não era meu código e o erro foi uma mudança de minuto na semântica de return x vs return (x) no padrão c ++ para casos de modelo muito específicos, e "você gostaria de ler este código com 2 minutos prazo, bexiga cheia e o destino de cada tamanco com fotos de gatos holandeses no ombro ??? "não soou tão legal)

Sim. Existem alguns truques interessantes possíveis no PS que são ótimos em caso de necessidade ou se você precisar de uma abreviatura única.

Para código sustentável, idealmente devemos ter operadores de coalescência nula explícitos como C #. O principal problema para mim é - o que usamos para eles? ? já tem o alias de Where-Object (por mais que eu adorasse que fosse apagado, é um uso muito comum). Lembre-se, % tem o alias de ForEach-Object mas isso não impede as operações de módulo, então, em teoria, pelo menos ? sendo um operador de coalescência nula também seria potencialmente bem; ele só seria interpretado como tal em expressões, onde Where-Object não é realmente válido de qualquer maneira.

@ vexx32 :

Pelo menos _sintaticamente_ ?? deve servir, porque estamos falando sobre o modo _expressão_, enquanto ? , como um comando [alias], só é reconhecido no modo _argumento_.
Não tenho certeza se a reutilização do símbolo causa confusão, mas não seria a primeira vez que um símbolo tem dupla função em diferentes contextos.

Surpreendentemente, usar ?. e ?[] para saturação de nulos seria tecnicamente uma alteração significativa, porque o PowerShell atualmente permite ? como um caractere não inicial em um nome de variável.

PS> $foo? = @{ bar = 1 }; $foo?.bar   # !! $foo? is a legal variable name
1

No entanto, espero que isso seja considerado um Balde 3: alteração

Não posso dizer que vi alguém usar? em um nome de variável ... Nem eu, porque é provável que seja mal interpretado. Mas sim, espero que seja perfeitamente utilizável.

Suspeito que seja um pouco tarde para trazer isso à tona, mas o Bash tratou disso (e de alguns outros casos) com seus recursos de substituição de parâmetro: https://www.tldp.org/LDP/abs/html/parameter-substitution.html.

Embora possa ser difícil aprender devido ao grande número de coisas que podem ser feitas com ele, é incrivelmente poderoso. Eu entendo que não seria possível usar essa notação exata devido à maneira como o PowerShell usa colchetes com variáveis, nem necessariamente se encaixaria com a sensação geral da linguagem, mas parece ser um ponto de dados útil.

@bgshacklett :

Sim, a substituição de parâmetros é poderosa, mas, infelizmente, não é apenas um fardo para _aprender_, mas também para _ lembrar_.

Portanto, embora os _recursos_ de Bash sejam frequentemente interessantes, sua _forma sintática_ costuma ser misteriosa, difícil de lembrar e não se encaixa bem no PowerShell.

_Brace expansion_ (por exemplo, a{1,2,3} em bash expandindo para a1 a2 a3 ) é um exemplo de um recurso interessante cuja expressividade eu adoraria ver no PowerShell, mas com PowerShell apropriado sintaxe - consulte # 4286

Eu concordo completamente. Eu mencionei isso mais como um exemplo de como esse problema foi resolvido em outro lugar do que como uma solução exata.

Há um outro operador a considerar:

$x ?= 12

que definiria $x se não estiver definido (não existe). Isso faz parte do "padrão inicializador" que não é comum em linguagens convencionais, mas para linguagens (com escopo dinâmico) como shells, ferramentas de criação, etc., é muito comum ter um script que define um padrão se o usuário não o tiver especificado . (Embora na verdade os inicializadores de parâmetro sejam bastante difundidos.)

Estendendo isso para propriedades:

$obj.SomeProperty ?= 13

adicionaria e inicializaria uma propriedade de nota SomeProperty no objeto se ele não existisse.

E - por diversão - mais uma variação na inicialização de uma variável usando -or :

$x -or ($x = 3.14) > $null

$x ?= 12 parece uma ótima ideia.

Ocorreu-me que provavelmente deveríamos aplicar todos esses operadores não apenas se o LHS não _existir_, mas também se _existisse, mas contivesse_ $null (com [System.Management.Automation.Internal.AutomationNull]::Value tratado como $null neste contexto).

adicionar e inicializar uma propriedade de nota SomeProperty

Nesse sentido, $obj.SomeProperty ?= 13 faz sentido para mim _somente_ se .SomeProperty existe e contém $null , dado que você não pode criar propriedades implicitamente mesmo com atribuições regulares (por outro lado, para _hasthables_ o a criação de entrada implícita faz sentido).

Todos os operadores discutidos precisarão isentar o LHS da verificação de existência de modo estrito.

Estou me perguntando sobre a intenção / raciocínio por trás do qual strictmode impede você de acessar uma propriedade que não existe. A intenção é detectar erros de digitação como $x.pretnd ? A intenção é verificar suposições como assert(x != null) ? A intenção é bloquear a propagação acidental de $null mais adiante em seu código?

a imersão de nulos (?.) é útil, para sinalizar a intenção explícita de ignorar $ null de maneira concisa.

Não é isso que . já sinaliza no modo não estrito? Se você quisesse ser estrito, poderia verificar se .Name existia primeiro; ao não verificar primeiro, você já sinalizou a intenção explícita de que a verificação não era importante para você.

Se StrictMode tiver um propósito em que acessar uma propriedade inexistente e retornar $null seja proibido, o mesmo raciocínio não se aplicará a ?. , e isso deve lançar a mesma exceção pelo mesmo motivo . Se não, porque não?

Com coalescência nula, você pode escrever:

$result = Invoke-RestMethod -Uri 'https://example.org/api/test'
$test = $result?.Name

Será rapidamente recomendado ou idiomático usar sempre ?. para cada referência de propriedade, porque "se comporta como . e não lançará exceções para pessoas que usam o modo strict"?

O desejo real é ter um modo restrito configurável com configurações variáveis, de modo que, por exemplo, você possa ter uma declaração no topo de seu script # StrictMode SkipMemberExistenceCheck , ou um atributo de saída de emergência para indicar um bloco de unstrict code geralmente? [Suspeito que o desejo seja uma sintaxe concisa no estilo C #, mas você sabe o que quero dizer]

Obrigado pelos pensamentos, @HumanEquivalentUnit.

A intenção é detectar erros de digitação como $x.pretnd ?

Não posso falar sobre a intenção do projeto, mas é isso que faz sentido para mim - análogo ao que Set-StrictMode -Version 1 faz para _variáveis_ (apenas).

Com as variáveis, trata-se de sua _existência_, não se o valor é $null , e a verificação da existência de propriedade segue o mesmo padrão.

Eu penso nisso como o mais próximo que uma linguagem de script dinâmica pode chegar de encontrar erros que uma linguagem compilada com tipagem estática relataria em _tempo de compilação_.


Não é isso. sinais já em modo não estrito? Se você quisesse ser rígido, poderia verificar se .Name existe primeiro

Sim, mas no modo não estrito, você renuncia às verificações de existência mencionadas, e testar manualmente é obviamente muito complicado - e invariavelmente mais lento do que um recurso integrado.

por não verificar primeiro, você já sinalizou a intenção explícita de que verificar não era importante para você.

A questão é que, com Set-StrictMode -Version 2 ou superior, você obtém:

  • o benefício da verificação de existência de membro _por padrão_
  • a opção de cancelar, _ sob demanda, concisamente_ com .?

você pode ter uma declaração no topo de seu script # StrictMode SkipMemberExistenceCheck , ou um atributo de saída de emergência para indicar um bloco de unstrict code geralmente

Relacionado: O escopo dinâmico atual de Set-StrictMode é problemático; veja @lzybkr 's projecto de RFC para implementar um modo estrito _lexical_: https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0003-Lexical-Strict-Mode.md

o benefício da verificação de existência de membro por padrão

PS contém código que verifica o membro e retorna $ null quando eles não existem. Esse é um benefício padrão, que você também pode evitar escrevendo seu próprio teste de existência de membro com $x.psobject.properties... se assim desejar. StrictMode tira esse benefício e deixa o obstáculo de ter que escrever testes de existência. Então, em vez de fornecer uma boa maneira de escrever exatamente um teste de existência de membro, ?. daria a você uma solução alternativa concisa para obter o que . sempre fez. E se for importante que o membro exista, você ainda terá que codificá-lo separadamente.

Mas isso me faz perguntar o que acontece com as chamadas de método:

$x.pretend     -> $null
$x.pretend()   -> Exception MethodNotFound
$x?.pretend  -> $null
$x?.pretend()   -> ____ what goes here? $null?

set-strictmode -version 2
$x.pretend -> Exception PropertyNotFoundStrict
$x.pretend()  -> Exception MethodNotFound
$x?.pretend  -> $null
$x?.pretend()   -> ____  ?

ou seja, ?. se comportaria da mesma forma que . para pesquisas de propriedade em modo não restrito, mas se comportaria de maneira diferente de . para chamadas de método em modo não restrito, tornando-o nuançado e não um substituto imediato. Isso significa que, ao usar ?. você não teria como saber se um método foi invocado ou se nada aconteceu? Propriedades podem ser apoiadas por métodos getter, mas acho que é uma convenção no DotNet que os getters não alterem o estado interno, mas é normal que as chamadas de método alterem o estado. Com ?. você não teria idéia se alterou o estado de um objeto?

Gostaria de saber se você pode obter os benefícios desejados e, melhor, tornando ?? capaz de lidar com exceções PropertyNotFoundException e MethodNotFound diretamente da expressão à esquerda (mas não outras exceções levantadas dentro de métodos), bem como lidar com $ nulos, e não têm ?. todo. por exemplo

$varThatMayBeNull ?? $fallbackValue # null-coalescing
#
$varThatMayBeNull.Name ?? $fallbackvalue    # catching PropertyNotFoundStrict exception
$varThatMayBeNull.Name ??  # default fallback is $null
$varThatMayBeNull.Method() ??  # catching MethodNotFound Exception

# and potentially addressing the index case too
$varThatMayBeNull.first.second[3].Method() ??  # catching MethodNotFound Exception, or index not found Exception

E então, em vez de ?. chamar um membro ou acessar uma propriedade, poderia ser uma forma concisa de perguntar se um membro existe, retornando um [bool].

Com a mesma adição de sintaxe de ?? e ?. você poderia lidar com mais situações, com menos sobreposição confusa.

StrictMode tira esse benefício e deixa o obstáculo de ter que escrever testes de existência

O obstáculo de uma pessoa é o benefício de outra:
Tudo depende do que você deseja que aconteça _por padrão_:

  • Se você tiver certeza de que não há _typos_ em seu código - por suposto, aproveite o comportamento _default_ (modo estrito OFF) - e você não precisará de .? (para acesso a _property_ - consulte próximo comentário re _chamadas de método_ e _indexação_).

    • Em uma nota _pessoal_: eu tendo a usar Set-StrictMode -Version 1 para evitar erros de digitação em nomes de _variáveis_, mas acho -Version 2 ou superior muito irritante, principalmente devido a # 2798 (que é um bug independente de esta proposta).
  • Se você quiser se certificar de que (a) não digitou nomes de propriedade incorretamente e / ou (b) seu código não opera acidentalmente em tipos de dados diferentes dos que você pretendia operar, defina o modo estrito para a versão 2 ou superior .

    • Na verdade, isso _atualmente_ torna complicado _desativar_ essas verificações _ sob demanda_,
    • que é precisamente porque a introdução de acesso condicional nulo está sendo proposta aqui: para tornar _fácil_ cancelar sob demanda.

Testar explicitamente a presença de um membro é realmente um caso de uso separado que pode ser uma necessidade, independentemente do modo estrito em vigor.

Quanto às _chamadas de método_, que o OP não cobre:

_Chamadas de método_ (por exemplo, $varThatMayBeNull.Method() ) e _indexação_ (por exemplo, $varThatMayBeNull[$index] ) são dois casos em que mesmo o modo estrito desativado ou na versão 1 pode se beneficiar, dado que ambos os casos atualmente _invariavelmente_ causam um erro se o valor acessado é $null .

A sintaxe seria a mesma das propriedades dos métodos - $varThatMayBeNull?.Method() - e análoga à indexação - $varThatMayBeNull?[$index] - como em C #.

_Update_: há um segundo aspecto da indexação, ou seja, se a variável não é nula, mas o elemento no índice fornecido não existe (por exemplo, $arr = 0,1; $arr[2] ). Com o modo estrito versão 2 ou inferior, isso é avaliado como $null , mas na versão 3 ou superior isso causa um erro e seria bom poder cancelar esse erro também. No entanto, a sintaxe para este caso apresenta um desafio - consulte o OP atualizado, que atualmente propõe o talvez estranho [...?] .

E, sim, eu tornaria esse acesso padrão em $null também, mesmo para métodos - novamente, você está _explicitamente_ sinalizando que não há problema em não fazer nada se o objeto acessado for $null ou se nenhum elemento da matriz existir.

Observe que C # não possui indexação condicional nula no segundo sentido.

Ele tem indexação condicional nula, mas não exatamente como você está propondo. C # permite (tenho quase certeza, pelo menos ...) array?[0] que ignora o problema usual de "índice em matriz / coleção nula" e apenas retorna nulo.

Mas sim, concordo que é uma boa ideia. Isso nos poupa do tratamento de exceções e mantém o código bem claro e conciso.

@ vexx32 , que é _um_ aspecto da indexação condicional nula: se array em array?[0] for null , a operação de indexação é ignorada - C # também tem isso, como você afirma.

Eu estava (atualização: _também_) falando sobre o _outro_ aspecto da indexação condicional nula: o caso em que array em array?[0] é _não_ null mas é _o elemento no índice 0 _ que não existe.

É o último que não pode ser ignorado em C #, que eu saiba, mas também acho que seria útil.

Você pode pensar em uma terminologia para dar nomes distintos a esses dois aspectos?

O código de outro problema de JavaScript iria de $_.Description[0].Text para $_?.Description[0?]?.Text que não acho bonito; e minha outra sugestão de como ?? poderia se comportar levaria a $_.Description[0].Text ?? que é subjetivamente melhor e - se possível de implementar - trataria de falhas em qualquer ponto de pesquisa da cadeia de uma só vez , e ainda permite que você escolha o que deseja que aconteça por padrão e tenha um fallback diferente à vontade, em vez de estar limitado a apenas $null .

À parte, essa sintaxe ?. teria um análogo para propriedades e métodos estáticos, ou seja, $t = [int]; $t::MaxValu; $t::Pars("1") , isso se tornaria ?: ou ?:: ?

Qual seria o comportamento desejado para a indexação de array com mais de um índice especificado? $array[0,4?,1,2] onde cada um pode ter permissão para falhar, ou $array[0,4,1,2?] com apenas o lote inteiro permitido para ter sucesso ou falhar?

Primeiro tenho que dar um passo para trás, porque percebi que confundi aspectos que valem a pena separar, pelo menos conceitualmente:

  • _Valor nulo_-membro condicional ou acesso de índice, como em C #:

    • Se um valor sendo acessado for $null , ignore as tentativas de acessar seus membros ou indexe nele e avalie como $null .
  • Membro _Member-existing_-condicional ou acesso de índice, específico para PowerShell:

    • Se um valor sendo acessado é _non- $null _, ignore as tentativas de acessar _members_ não existentes (propriedades, chamadas de método, indexação) que não existem.

Nota: Para resumir, estou usando _member_ um tanto vagamente aqui para incluir também _elementos_ de objetos que por acaso são coleções, que são acessados ​​com _indexação_ em vez de notação de ponto.

Se você tiver certeza de que não há erros de digitação em seu código - por suposto, aproveite o comportamento padrão (modo estrito OFF) - e você não precisará fazer isso. (para acesso à propriedade

@ mklement0 Acho que você está discutindo do ponto de vista " uma pessoa usando o modo estrito para melhorar seu próprio código " e eu, do ponto de vista " outra pessoa pode executar meu código no modo estrito, quais alterações fariam meu código funcionar para eles também? "e isso está me levando a um raciocínio diferente. Com ?. , seria tão conveniente usá-lo em vez de . e fazer o código funcionar em modo restrito que eu também poderia usá-lo em qualquer lugar habitualmente; Não perco nada e ganho compatibilidade. Temo que todo o código do PowerShell ficará feio dessa forma porque "funciona" em mais casos.

Mas não é uma substituição imediata, porque silencia a pesquisa de chamada de método com falha no modo não strict e silencia as pesquisas de propriedade com falha no modo strict - adiciona uma nuance complicada à pesquisa de membro, uma operação incrivelmente comum. Isso não ajuda ninguém que deseja se beneficiar das verificações de modo estrito, tornando esse texto padrão mais fácil. Não ajuda pessoas suficientes em situações suficientes, pela complexidade que adiciona. E é muito conveniente tornar o código mais compatível, eu não acho que "ignore se você não quiser" é forte o suficiente para mantê-lo sob controle.

O PowerShell pode fazer coisas que o C # não faz, como definir variáveis ​​automáticas ou ter um precedente para que exceções sejam lançadas e silenciadas nos bastidores. Deve haver uma abordagem melhor que não complique a pesquisa de membros de forma alguma, mas simplifique o clichê de verificação de segurança do strictmode para que as pessoas que querem usar os cheques tenham uma vida mais fácil e as pessoas que ficaram incomodadas com os cheques também tenham uma vida mais fácil.

@HumanEquivalentUnit Como silencia essas coisas? Você teria que escrever uma carga de tratamento de exceção e verificações de nulos anteriormente, em vez disso:

$prop = $item?.Method().PropIwant ?? 'PropActionFailed'

v.

$prop = if ($null -ne $item) {
    $propIwant= $item.Method().PropIwant
    if ($null -eq $propIwant) {
        'PropActionFailed'
    }
    else {
        $propIwant
    }
}

E este caso não é incomum. Ou você apenas joga $ErrorActionPreference = 'Stop' e envolve tudo em try/catch para simplificar a verificação de nulos, que é um padrão ruim (lógica orientada por exceção).

Além disso, este é o futuro da linguagem, não é como se estivéssemos voltando para 5.1 e mudando as coisas. Se você quiser mais adotantes, precisará facilitar a leitura do código, o que acho que esta sugestão faz.

@ mklement0 Acho que você está discutindo do ponto de vista "_uma pessoa usando o modo estrito para melhorar seu próprio código_" e eu do ponto de vista "_alguém pode executar meu código no modo estrito, quais alterações fariam meu código funcionar para eles também? _ "

Estamos falando de equivalente a "set -euo pipefail" (modo estrito bash)?

a- Limite para pesquisas de variáveis ​​(embora eu não veja por que você acha que uma simples verificação de nulo em pesquisas de membros seria uma coisa ruim, a pesquisa de membro real é muito mais cara do que if (result is null) )
b- Use [Strict] em scopes / codeblocks para dizer ao analisador para tratá-los como tal,
c- A rigidez é baseada no escopo: o código que não está marcado como estrito não é estrito.

Se eu escrevi um código estrito que falha quando você o usa, então um de nós cometeu um erro. Ou fiz referência a uma variável ausente em um código que nunca testei ou você entendeu mal minha API mal projetada que depende da existência de variáveis ​​que não são passadas para minha função e você falhou em preencher uma variável que espero que seja preenchida. .

# Their code
Function Log($Message) { Write-Host $Massege }  # not strict

# My code
[Strict]
Function DebugMsg
{
  Param([String] $Message)
  if ($DEBUG) { Log Host $Message }
}

# Your code
DebugMsg "Hello"  # you didn't define DEBUG, and by saying strict I expressed a contract whereby the variable MUST be defined.

A função "Log" não foi marcada como estrita, por isso não reclama do erro de digitação

🤔 Agora que você mencionou, um atributo [Strict()] seria uma maneira _descrita_ de implementar o modo estrito de uma forma restrita ao escopo. Isso precisa ser mencionado naquele outro tópico de discussão sobre questões de modo estrito, vou desenterrar isso e vincular de volta aqui ...

@ TheIncorrigible1 " Como ele silencia essas coisas? " No modo não estrito $null.x é silencioso, $null.x() é uma exceção. Com ?. como discutido, $null?.x é silencioso, $null?.x() é silencioso.

Portanto, você teria um . que funciona como ?. , exceto no modo estrito, em que lança exceções. Então você teria um ?. que funciona no modo estrito como ?. e como . do PowerShell funciona no modo não estrito, mas funciona de maneira diferente do . do PowerShell ?. é claramente melhor e mais útil em mais desses cenários).

Você teria que escrever uma carga de tratamento de exceção e verificações de nulos anteriormente, em vez disso: $prop = $item?.Method().PropIwant ?? 'PropActionFailed'

Sua chamada de método pode lançar, ou retornar $ null e PropIWant pode estar faltando, você ainda terá que escrever:

$prop = try {
    $item?.Method()?.PropIwant ?? 'PropActionFailed'  
} catch [someMethodException] {
    'PropActionFailed'
}

vs. minha proposta de ?? (se fosse possível construir), e sem necessidade de ?. , e cobre possíveis casos de exceções de Método caso você só se preocupe com "sucesso ou não" e nenhum tratamento de erros detalhado.

$prop = $item.Method().PropIWant ?? 'AnythingFailed'

Se você quiser mais adotantes, precisará facilitar a leitura do código, o que acho que esta sugestão faz.

Com que frequência você digita . no PowerShell? Com que frequência você deseja explicar aos novos usuários qual é a diferença entre . e ?. e dizer a eles que eles devem levar em consideração a diferença sempre que digitarem . para sempre ?

"Oh, como isso aconteceu?" "Bem, em C # . lançou uma exceção para membros ausentes, então eles inventaram ?. que não o fez. Isso foi ótimo, então eles fizeram isso no PowerShell por padrão, o . não joga. Então eles inventaram o modo estrito que faz . se comportar como C # e jogar, mas apenas nesse contexto, mas a maneira de verificar os membros manualmente era muito prolixo e incômodo e ninguém queria digitá-lo; em vez de abordar esse problema de frente e torná-lo mais conveniente para se beneficiar do modo estrito, eles se esquivaram e imitaram ?. C # para obter uma maneira furtiva de sair do rigor que você pediu, o que é moderadamente conveniente para você, mas é péssimo para todos os outros. E você ainda não tem nenhum teste de existência de membro, e se beneficiar do modo estrito ainda é prolixo e incômodo, mas pelo menos você não precisa sofrer isso porque 'modo estrito' tem uma saída de emergência 'modo normal' agora". Ick.

Se eu escrevi um código estrito que falha quando você o usa, então um de nós cometeu um erro.

embora eu não veja por que você acha que uma simples verificação de nulo em pesquisas de membro seria uma coisa ruim, a pesquisa de membro real é muito mais cara do que se (o resultado é nulo)

Não acho que seria ruim, o modo não estrito de PS já faz isso, e acho que seria uma alternativa desejável ao ?. proposto - muito diferente, útil em mais situações.

Mas observe que no PS você pode fazer algo que C # não pode:

(1..3)[1kb..100kb]

e sem exceções no modo não estrito. Mude os números e veja quanto tempo leva; a julgar pelo desempenho e como acho que deve funcionar nos bastidores para indexar em objetos arbitrários, parece que na verdade existem cerca de 100.000 exceções sendo levantadas e silenciadas, para nossa conveniência. PS nem sempre faz a escolha "deve ser o mais rápido possível, empurrar o inconveniente para o usuário", e gosto disso.

@HumanEquivalentUnit Existem muitos recursos em todos os idiomas que você nunca usará em sua carreira e se você não gosta do estilo, tudo bem, você não precisa usar esses recursos. Não vejo por que você está desativando o modo estrito; é uma boa prática em scripts, para que você esteja lidando conscientemente com os problemas, em vez de permitir que a linguagem os engula (e, em essência, ter blocos catch vazios implícitos em todos os lugares). Novamente, você ainda pode não usar os recursos (por exemplo, nunca usei Out-Default ).

Além disso, o operador é ? , não ?. ; também funcionaria com acesso de índice.

Para continuar a estabelecer as bases para uma discussão mais aprofundada, deixe-me resumir como os modos estritos existentes afetam o acesso condicional de valor nulo e de existência de membro condicional (os termos introduzidos acima ):

As colunas abaixo representam as configurações Set-StrictMode e os valores da coluna indicam:

  • 👍 ... permitido
  • 🚫 ... proibido (causa erro de encerramento de instrução)

À parte: o fato de os erros serem apenas _statement-_, não _script_-terminating, está relacionado à discussão maior sobre o tratamento de erros do PowerShell - consulte https://github.com/PowerShell/PowerShell-Docs/issues/1583.

  • Acesso _Null-value_-condicional - $obj _itself_ is $null :

Construir | -Off | -Versão 1 | -Versão 2 | -Versão 3+
---------- | ---- | ---------- | ---------- | ------------
$ obj | 👍 | 🚫 | 🚫 | 🚫
$ obj.Prop | 👍 | 👍 | 🚫 | 🚫
$ obj [42] | 🚫 | 🚫 | 🚫 | 🚫
$ obj.Method () | 🚫 | 🚫 | 🚫 | 🚫

É curioso que com -Off e -Version 1 , $obj.Prop é permitido, enquanto $obj[42] não é.

  • _ Acesso condicional de existência de membro - $obj não é $null , mas o membro / elemento acessado não existe:

Construir | -Off | -Versão 1 | -Versão 2 | -Versão 3+
---------- | ---- | ---------- | ---------- | ------------
$ obj.Prop | 👍 | 👍 | 🚫 | 🚫
$ obj [42] (indexável) | 👍 | 👍 | 👍 | 🚫
$ obj [42] (não indexável) | 👍 | 👍 | 🚫 | 🚫
$ obj.Method () | 🚫 | 🚫 | 🚫 | 🚫

É curioso que objetos inerentemente indexáveis ​​(por exemplo, arrays) permitem acesso a elementos inexistentes mesmo com -Version 2 , enquanto os escalares - onde o PowerShell fornece recursos de indexação para fins de manipulação unificada com coleções - não.

O operador de saturação de nulos (

function Get-OptionalPropertyValue($object, [string] $propertyName) {
    if (-not ([bool] (Get-Member -InputObject $object -MemberType Properties -Name $propertyName))) {
        return $null
    }

    $object.$propertyName
}

$value = Get-OptionalPropertyValue $foo "bar" # if $foo.bar exists, $value will contain its data; else $value will be $null

quando você _realmente_ deveria apenas ser capaz de escrever:

$value = $foo?.bar # if $foo.bar exists, $value will contain its data; else $value will be $null

Por favor, pessoal, se você pudesse obter apenas nulo-condicional desta proposta implementada e apenas para propriedades, isso pouparia muitas pessoas.

Comecei a dar uma olhada nisso e encontrei alguns problemas.

Vamos considerar ?? e ?=

$x ?= 1 e $x ?? 1 parecem ser diretos. Mas, como eles são operadores, precisamos oferecer suporte a $x?=1 e $x??1 .

O problema com isso é $x? e $x?? ambos validam nomes de variáveis ​​no PowerShell. A única maneira de eliminar a ambigüidade seria ${x}?=1 e ${x}??1 .

E mais para o acesso de membro condicional ?. e ?[] , teríamos que ter ${x}?.name e ${x}?[0] .
Para estes, $x ?.name e $x ?[0] não serão suportados.

A outra opção é introduzir uma alteração significativa e não permitir ? no nome da variável.

O que você acha? @ mklement0

/ cc @rjmholt @ daxian-dbw @JamesWTruher

Nunca _gosto_ nem entendi por que ? é um nome de variável válido. Ele abre a porta para nomes como $Safe? que geralmente é mais claro e menos estranho quando escrito como $IsSafe qualquer maneira.

Acho que essa mudança provavelmente demorou muito para acontecer.

Isso abre a porta para nomes como $ Safe? que geralmente é mais claro e menos estranho quando escrito como $ IsSafe de qualquer maneira

Curiosamente, expressei o sentimento oposto. É uma tradição clássica do Scheme / Ruby .

if ($questionMark?)
{
    Write-Host "That's silly!"
}

O operador ternário não tinha o mesmo problema, onde a resolução era exigir espaços ao redor do operador ou chaves ao redor do nome da variável para que funcionasse adequadamente com variáveis ​​que terminam com ? ?

Eu esperaria que, com a introdução desses operadores, o PowerShell priorizasse o reconhecimento de ? como parte do operador na sintaxe, como $x?[0] , de modo que os usuários que usam nomes de variáveis ​​terminados em ? para armazenar uma coleção teria que usar esta sintaxe (já que os espaços seriam inválidos neste caso): ${x?}?[0] . Idem para $x?.Name e ${x?}?.Name .

A outra opção é introduzir uma alteração significativa e não permitir? no nome da variável.

👍 Visto que esta é uma alteração importante da versão (6 a 7), acho que vale a pena fazer uma alteração significativa aqui. Gostaria que o GitHub tivesse uma pesquisa de código melhor para ver quantas instâncias existem de variáveis ​​PS que terminam com ? . Eu suspeito que isso não seria tão impactante, mas seria bom verificar isso no GitHub. Em 14 anos de uso do PowerShell, nunca usei ? em um nome de variável, mas talvez seja só eu. :-)

Esse navio navegou há muito tempo, e eu não ficaria surpreso em ver variáveis ​​terminando com ? em uso nos scripts hoje.

Neste ponto, se simplesmente priorizarmos a análise de ? como parte de um operador, em vez de como parte de um nome de variável quando se trata desses novos operadores, as pessoas que usam nomes de variáveis ​​terminando em ? precisariam para usar {} ou espaços (onde espaços são permitidos) para usar essas variáveis ​​com esses operadores.

É estranho ouvir a frase "aquele navio partiu" neste contexto. Esta é uma _maior versão_ mudança. Não é irracional que isso mude aqui, eu acho.

@rkeithhill Eu usei isso em coisas pessoais, mas pensei que não seria claro para o trabalho colaborativo, uma vez que é uma coisa anti-intuitiva para os programadores ter símbolos como parte de nomes de variáveis ​​(semelhante a usar emojis como variáveis)

@KirkMunro tendo "análise priorizada" soa como uma porta aberta para bugs.

@ vexx32 : Não é irracional para uma alteração significativa, pois esta é uma alteração importante da versão; no entanto, a barreira para tais mudanças significativas deve permanecer muito alta, e não acho que isso chegue perto de passar dessa barreira, uma vez que os usuários poderiam usar variáveis ​​que terminam em ? muito bem, desde que usassem {} inclui para identificar o nome da variável.

Observe que você também pode ter um nome de propriedade que termina em ? . Atualmente, se você tentar exibir essa propriedade no PowerShell sem colocar o nome entre aspas, receberá um erro do analisador.

Por exemplo:

PS C:\> $o = [pscustomobject]@{
    'DoesItBlend?' = $true
}
PS C:\> $o.DoesItBlend?
At line:1 char:15
+ $o.DoesItBlend?
+               ~
Unexpected token '?' in expression or statement.
+ CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken

PS C:\> $o.'DoesItBlend?'
True

Estou um pouco surpreso que não analise hoje (por que não analisa?), Mas independentemente, para tais propriedades, você precisa colocar seus nomes entre aspas, caso em que você poderia seguir a citação com um ternário, nulo -coalescing, etc. operador sem espaços e funcionaria muito bem. Acho essa sintaxe muito semelhante a ${x?}?.name e estou de acordo com a posição de que você pode usar esses nomes de variáveis ​​/ propriedades se quiser, mas esses nomes podem exigir sintaxe extra ou espaçamento para funcionar com ternário ou nulo - * operadores.

@KirkMunro Nada impede as pessoas de usarem limites de variáveis ​​no futuro se quiserem nomes de variáveis ​​esotéricos. Eu concordo com os outros no sentido de que me pergunto qual é o uso atual desse comportamento.

Pessoas no PowerShell 7 provavelmente já são entusiastas e estarão cientes das mudanças significativas. Pessoas que não usam, ainda estão usando <= v5.1 e continuarão usando por muito tempo; provavelmente até que o msft o remova do Windows 10 (nunca).

@KirkMunro Claro, mas removê-lo dos caracteres de variável permitidos padrão não impede que os usuários façam ${Valid?} como o nome da variável de qualquer maneira. Já que eles teriam que fazer isso com esses operadores independentemente, acho que seria melhor apenas ter consistência, em vez de ? se tornar um caractere que _somente às vezes_ é considerado parte de um nome de variável.

Isso já _is_ será uma mudança significativa, e acho melhor pelo menos ser consistente sobre isso e ir até o fim, em vez de introduzir outro nível de ambigüidade. 🙂

@adityapatwardhan Acho que seria melhor para o idioma remover ? como um caractere de variável válido. Ele habilitaria facilmente os operadores null-soak / -coalesce e ternário em uma sintaxe familiar que adiciona muita ergonomia ao processo de autoria do script.

@KirkMunro ter "análise priorizada" soa como uma porta aberta para bugs.

@ TheIncorrigible1 : Qualquer código pode abrir a porta para bugs se não for implementado corretamente. Estou apenas falando sobre um lookahead simples de caractere único para identificar se o PowerShell é executado em um operador ternário ou null- * quando analisa um nome de variável que não está incluído em limites de variável e encontra um caractere ? . Isso não é complicado e não abre a porta para mais bugs do que qualquer outra alteração de código.

Pessoas no PowerShell 7 provavelmente já são entusiastas e estarão cientes das mudanças significativas. Pessoas que não usam, ainda estão usando <= v5.1 e continuarão usando por muito tempo; provavelmente até que o msft o remova do Windows 10 (nunca).

@ TheIncorrigible1 : Que base / evidência você tem dessa afirmação? PowerShell 7 está em versão prévia, então hoje ele é usado apenas por entusiastas. Isso é certo. Mas, além da visualização, se o PowerShell 7 ou posterior oferecer recursos atraentes que as empresas precisam, ao mesmo tempo que oferece suporte à funcionalidade de que precisam, eles usarão essas versões. Isso é especialmente verdadeiro se o PowerShell 7 for instalado com o sistema operacional. O único ponto em que os entusiastas entram em jogo são as organizações que não têm uma necessidade comercial do que o PowerShell 7 traz para a mesa.

Isso já vai ser uma mudança significativa, e acho melhor pelo menos ser consistente com isso e ir até o fim, em vez de introduzir outro nível de ambigüidade. 🙂

@ vexx32 Isso é ?? em um nome de variável, mas a probabilidade disso é muito mais remota do que ter uma variável cujo nome termina em um único ? . Fora isso, como seria a introdução de operadores null- * enquanto ainda dá suporte a ? como scripts de quebra de caracteres de variável permissível padrão hoje?

Na minha experiência, as alterações de interrupção para quem gosta de ter (que é o que parece ser) são com muito mais frequência rejeitadas, e a discussão / argumentos em torno delas servem apenas para desacelerar o processo de fazer as coisas dramaticamente o ponto em que as coisas simplesmente param ou perdem a liberação, etc. A desaceleração geralmente é necessária, porque se você está propondo uma alteração significativa, as evidências são necessárias para poder avaliar o impacto e justificar tal alteração. É difícil reunir essa evidência hoje. Só estou dizendo isso porque vou escolher terminar os recursos agora, em vez de discutir sobre uma mudança importante a qualquer dia.

Eu nunca uso ? em nomes de variáveis, nem usaria. No entanto, espero que algumas pessoas usem, porque ele pode ler muito bem em um script e colocar barreiras desnecessárias à entrada do PowerShell 7 apenas retarda a adoção, especialmente quando muitas pessoas que trabalham com o PowerShell não são desenvolvedores que estão mais acostumados a trabalhar com mudanças significativas.

De qualquer forma, não é minha intenção retardar esse processo - muito pelo contrário. Mas eu compartilhei meus pensamentos e experiência, então não vou comentar se devemos ou não pressionar por uma mudança significativa aqui.

Considere este exemplo inventado:

$ValueIsValid? = @( $true, $false, $false, $true )

$ValueIsValid?[0]
# old behaviour? gets `$true`
# new behaviour? gets nothing, because the `?[0]` is interpreted as a null-conditional access.

Este comportamento _já_ romperia com as mudanças propostas. Eu preferiria uma quebra consistente e limpa do que uma quebra confusa que precisa de uma explicação de meia página para listar todas as exceções possíveis e quando e onde ? repente não é válido, e onde ainda é.

@KirkMunro se o PowerShell 7 ou posterior oferecer recursos atraentes de que as empresas precisam, ao mesmo tempo que oferece suporte à funcionalidade de que precisam, elas usarão essas versões. Isso é especialmente verdadeiro se o PowerShell 7 for instalado com o sistema operacional.

Tendo trabalhado em algumas Fortune 50s com algumas bases de funcionários na casa das centenas de milhares, até mesmo ficar longe do que era padrão no sistema operacional foi um desafio (ou seja, mudar para a v5.x). Ainda não vi nenhum lugar adotar o Core ; eles preferem apenas mudar para Python para compatibilidade cruzada. Habilitar recursos opcionais do Windows também era uma tarefa muito rara.

Acho que as empresas trabalham com o que têm e com a base de conhecimento de seus funcionários, em vez de buscar novas tecnologias ou versões de idiomas para resolver seus problemas. Alguns ficariam perfeitamente satisfeitos em permanecer na v2 para sempre e nunca tocar nos cmdlets Win10 / Server16 que tornam a vida muito mais fácil.

Meu ponto com tudo isso, é que esses recursos não estão no vácuo. Se você tornar uma linguagem mais ergonômica, haverá maior adoção por pessoas interessadas na ferramenta para resolver os problemas que têm mais rapidamente. (Veja: C # e o crescimento da popularidade com mais / melhores recursos de linguagem)

Com relação às variáveis ​​que terminam em ? - isso poderia ser uma alteração significativa por causa da variável automática $? .

@lzybkr Eu suspeito que o melhor caso para lidar com isso é um caso especial como o que existe para $^ e $$ .

@lzybkr @ TheIncorrigible1 Pelo que me lembro, _todas_ essas variáveis ​​são explicitamente com letras especiais no tokenizer. $^ _already_ não é um nome de variável válido. O tokenizer tem casos especiais para todos eles antes de começar a procurar por nomes de variáveis ​​padrão.

A única solução aqui é usar ¿ :

$ bobo? ¿= 1

Eu não sei Steve, estou firmemente no meio da multidão interrobang neste.

$silly?‽=1

@lzybkr - que eu saiba $? $$ e $^ são tratados de uma maneira especial.

https://github.com/PowerShell/PowerShell/blob/8b9f4124cea30cfcd52693cb21bcd8100d39a796/src/System.Management.Automation/engine/parser/tokenizer.cs#L3001 -L3004

Para resumir, temos essas quatro opções

  • Mudanças importantes - não permitir ? nos nomes das variáveis.
  • Prefira ?. como operador. Isso significa que os nomes de variáveis ​​que terminam com ? devem usar a sintaxe ${variablename} . Ainda é uma mudança significativa.
  • Prefira comportamentos antigos. Isso significa que usar ?. e ?[] , ${variablename}?.property deve ser usado. Sem alterações significativas, mas torna o uso dos novos operadores desajeitado.
  • Não implemente os novos operadores.

Eu, pessoalmente, não prefiro o primeiro e o último.

Esta é uma alteração importante da versão. Não é irracional que isso mude aqui, eu acho.

Acho que um ponto importante a ser feito é que a versão principal está sendo incrementada para sinalizar a prontidão para substituir o Windows PowerShell, o que significa que indica compatibilidade, não quebra. Do anúncio original :

Observe que a versão principal não significa que faremos alterações significativas.

As opções 1 e 2 não são iguais? Ou as variáveis ​​ainda teriam permissão para usar ? no caso de não haver nenhum operador de coalescência nula ou ternário seguindo-as?

Nesse caso, acho que podemos ter muitos problemas para lidar com a lógica de tokenização / análise sem muito retrocesso. Isso pode levar a degradações de desempenho quando as variáveis ​​terminam com ? .

Considerando que, pelo que posso ver, a opção 2 parece ser a preferência geral, não tenho certeza se entendo a relutância em fazer a alteração significativa aqui. Fazer dessa forma desencorajará ativamente o uso de ? em nomes de variáveis, simplesmente apresentando cenários em que eles não podem ser usados ​​sem incluir o nome da variável.

Eu acho que esta é uma mudança bastante pequena no que diz respeito às mudanças significativas, e ter uma quebra na consistência do que pode e não pode ser usado em um nome de variável é provavelmente a pior opção, na minha opinião.

Essas coisas devem ter regras claras que se aplicam a quase todas as situações. Atualmente, é esse o caso. Estamos propondo turvar as águas aqui e tornar o comportamento _in menos_ claro. Não consigo ver nada de bom vindo dele, exceto talvez que os nomes de variáveis ​​contendo ? se tornem menos usados ​​simplesmente porque são mais difíceis de usar em alguns cenários - o que (efetivamente) nos leva de volta à opção 1 por padrão, quase ... então não vejo nenhum motivo particular para não fazer uma pausa agora e apenas evitar o comportamento ambíguo.

@ vexx32 Existe uma ligeira diferença entre 1 e 2.

Para # 1, não permitimos que ? seja usado em nomes de variáveis ​​todos juntos. Isso significa que $x? = 1 , $x?? = 1 $x?x?x = 1 não analisará.

Para # 2, $x? = 1 ainda é válido, mas $x?.name é equivalente a ${x}?.name . Isso apenas quebra os nomes das variáveis ​​com ? no final, que estão acessando membros. Portanto, $x? = 1 , $x?? = 1 $x?x?x = 1 ainda seria válido.

Sim, eu ficaria um pouco preocupado com a sobrecarga de tokenização lá. Pode valer a pena implementar ambas as possibilidades e fazer alguns benchmarks em um script que usa nomes de variáveis ​​semelhantes de forma razoavelmente forte.

Minha preferência é definitivamente a opção 1 ... uma alteração de interrupção única é, pelo menos para mim, muito mais preferível do que ter que lidar com as inconsistências em como isso pode ser analisado no futuro.

Nesse caso, acho que podemos ter muitos problemas para lidar com a lógica de tokenização / análise sem muito retrocesso. Isso pode levar à degradação do desempenho quando as variáveis ​​terminam com?.

Eu compartilho essa preocupação sobre essa possibilidade. Ter ? como um separador de tokens às vezes parece uma linha irregular na linguagem para mim.

Nesse caso, acho que podemos ter muitos problemas para lidar com a lógica de tokenização / análise sem muito retrocesso. Isso pode levar à degradação do desempenho quando as variáveis ​​terminam com?.

Depende de como você o implementa. Contanto que você adote uma abordagem antecipada em vez de uma abordagem de tokenização e backup, você estará bem. Você pode ver quais caracteres são os próximos quando encontrar um ? em um nome de variável e tomar uma decisão sobre como deseja "embrulhar" o token de variável com base no que vem a seguir. Isso não é um grande problema e não deve exigir retrocesso ou resultar em degradações de desempenho perceptíveis.

@ PowerShell / powershell-Committee analisou este hoje, temos algumas ideias:

  • Não importa o que façamos, vamos fazer algumas análises do nosso corpus de scripts para ver com que frequência as pessoas usam ? em nomes de variáveis
  • Alguns de nós têm a hipótese (que outros gostariam de validar) de que os usuários que estão usando ? em seus nomes de variáveis ​​podem ser usuários menos avançados (como concordamos nesta sala, ficaríamos longe disso porque dos problemas potenciais que podem surgir). Por outro lado, qualquer pessoa que use a funcionalidade descrita aqui será capaz de entender uma sintaxe um pouco mais complicada (como ${foo}?.bar ). Portanto, preferimos a opção 3 porque ela evita alterações significativas nesses usuários menos experientes. (Mas, novamente, vamos validá-lo de acordo com o primeiro marcador.)
  • @ daxian-dbw levantou um bom ponto sobre tornar mais difícil encontrar todas as referências de variáveis ​​quando os criadores de scripts estão combinando o uso de $foo e ${foo} . No entanto, concordamos que isso pode ser corrigido nas ferramentas (como a extensão VS Code PowerShell), e usuários como @JamesWTruher que usam editores como o Vim podem combinar facilmente em ambos com sua sintaxe de pesquisa.

mais difícil encontrar todas as referências de variáveis ​​quando os criadores de scripts estão misturando o uso de $ foo e $ {foo}

Isso depende se o AST diferencia o nome da variável com base no fato de ter visto as chaves. Suspeito que qualquer ferramenta que use o PowerShell AST não terá problemas com chaves aqui.

Mas para regex, certamente significa que você deve estar mais atento: varname -> \{?varname\}?

@rjmholt eu fiz uma análise aqui . Uma análise rápida: de quase 400.000 scripts com mais de 22.000.000 de nomes de variáveis ​​(não exclusivos), havia apenas ~ 70 variáveis ​​exclusivas que terminavam com ? .

Obrigado, @mburszley - para mim, isso o torna um segmento 3: mudança {...} é altamente lamentável, paralelamente à infeliz necessidade de $(...) torno de exit / return / throw declarações no contexto de && e || .

Para pegar emprestado o raciocínio desse problema:

Se forçarmos o uso de {...} torno dos nomes de variáveis ​​apenas para que você possa usar ?. :

  • Novos usuários não esperarão a necessidade de {...} e não saberão necessariamente que _it_ é o que é necessário quando o seguinte falhar (possivelmente _não detectado_, a menos que Set-StrictMode -Version 2 ou superior esteja em vigor): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • Mesmo quando os usuários _do_ sabem sobre a necessidade de {...} :

    • Eles se esquecerão de usá-lo ocasionalmente, por causa da necessidade contraintuitiva dele.
    • Quando eles se lembrarem, esse requisito aparentemente artificial será uma fonte contínua de frustração, especialmente porque {...} é difícil de digitar.

Este problema foi marcado como corrigido e não teve nenhuma atividade por 1 dia . Foi fechado para fins de limpeza.

@rjmholt , citando você de https://github.com/PowerShell/PowerShell/issues/10967#issuecomment -561843650:

não quebrar a forma como analisamos a sintaxe válida existente era o caminho certo a seguir. Novamente, uma mudança não é apenas um caso em que tomamos a decisão de apoiar um pequeno número de programas selvagens por aí - fizemos várias mudanças importantes em casos em que pensamos que os prós superavam os contras (não que eu pessoalmente concorde com todos eles, ou que justificam ou não os outros). O problema é que alterar algum aspecto do tokenizer ou analisador significa que duas versões do PowerShell não gerarão mais o mesmo AST para alguns scripts, portanto, dois PowerShells "verão" o mesmo script de maneira diferente. Isso significa que não podemos nem mesmo ler a sintaxe da mesma maneira, então não há regra PSScriptAnalyzer que você possa escrever para detectar possíveis problemas em torno dela; PSScriptAnalyzer verá uma sintaxe diferente de uma versão do PowerShell para a próxima.

Ao contrário do C #, o PowerShell não pode pré-compilar uma sintaxe diferente em um formato compatível para execução posterior, o que significa que uma alteração sintática é vinculada ao tempo de execução. Depois que o PowerShell faz uma quebra sintática, reduzimos o número de scripts que podem funcionar em diferentes versões, o que significa que os usuários são forçados a escrever dois scripts, o que é um problema sério para um shell e uma linguagem de script. O PowerShell deve ser dinâmico o suficiente para superar todas as diferenças com a lógica dentro de um script, mas a sintaxe é a única coisa estática não negociável que temos. E fazer uma mudança de sintaxe onde antes e depois são válidos é especialmente pernicioso, pois não há uma maneira simples de detectá-lo, não há nenhum aviso para isso e mesmo após executá-lo em ambos os ambientes, os usuários podem não saber que ocorreu um comportamento diferente.

Em geral, posso perceber que essas mudanças podem ser muito problemáticas e devem ser feitas apenas se os prós superam os contras, um fator importante que é quanto código existente quebra.

reduzimos o número de scripts que podem funcionar em diferentes versões
o que significa que os usuários são forçados a escrever dois scripts

Estamos falando sobre um _novo_ recurso sintático aqui ( ?. ), então, por definição, os scripts que o usam _não podem ser executados em versões mais antigas - a menos que você especificamente adicione condicionais que forneçam caminhos de código compatíveis com a versão legada, mas isso parece dificilmente valer a pena - você apenas manteria os recursos legados.

Sim, a interpretação de $var?.foo em scripts antigos que usavam $var? como nome de variável seria interrompida, mas:

  • ? nunca deveria ter sido permitido como parte de um identificador _sem incluí-lo em {...} _.

  • Já que ser capaz de fazer isso é inesperado e provavelmente _desconhecido_ para muitos, tais nomes de variáveis ​​são extremamente raros na natureza, falando por experiência pessoal, mas a análise de @mburszley fornece evidências mais tangíveis .

    • Mesmo Ruby permite apenas ? (e ! ) no _end_ dos identificadores, e lá apenas de identificadores de _metodo_, e eu suspeito que a maioria dos usuários Ruby estão cientes de que não devem _não_ assumir que outras linguagens suportam a mesma coisa.

Então, pragmaticamente falando:

  • A grande maioria do código existente não será afetada - um token como $var?.foo simplesmente não será encontrado.

  • Se você escrever $var?.foo com a nova semântica, então sim, executá-la em versões anteriores pode resultar em um comportamento diferente (em vez de interromper de maneira óbvia), dependendo de qual modo estrito está em vigor - mas _você deve sempre imponha a versão mínima necessária para executar seu código conforme pretendido de qualquer maneira_ ( #requires -Version , chaves de manifesto do módulo).

Resumindo, para mim este é um caso claro de mudança no balde 3: uma mudança tecnicamente inovadora que quebra muito pouco código existente enquanto oferece benefícios reais (ou, inversamente, evita dores de cabeça perenes devido ao comportamento inesperado).

@ mklement0 estamos mantendo o acesso nulo-condicional experimental para PS7.0. Talvez você possa abrir uma nova edição para continuar esta discussão para 7.1?

Parece bom, @ SteveL-MSFT - consulte # 11379.

Eu encorajo todos aqui a mostrarem novamente apoio (ou, suspiro, não apoio) lá.

Esta página foi útil?
0 / 5 - 0 avaliações