Rust: Argumentos padrão e argumentos de palavra-chave

Criado em 6 jun. 2013  ·  70Comentários  ·  Fonte: rust-lang/rust

Não vi problemas com argumentos de palavras-chave. Há planos para isso no futuro?


NOTA: O bug tracker não é o lugar para se ter uma discussão sobre design. Direcione todas as discussões sobre design para o etherpad ( https://pad.riseup.net/p/hvbg6dQQnEe7 ) ou crie uma página de casamento de bicicletas no wiki.

Etherpads:

Comentários muito úteis

Triagem 05/08/2013: nenhum progresso (que eu saiba), embora ainda seja legal; talvez a sintaxe da declaração possa parecer

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

onde o RHS é qualquer expr constante (isto é, coisas que são válidas em uma declaração static ).

Todos 70 comentários

Não houve planos para isso, embora ninguém tenha ainda expressado qualquer oposição. Tenho pensado sobre esse problema há muito tempo e basicamente se resume a um pedido de argumentos padrão. Suponho que seria algo como:

fn foo(bar: int, qux=2: int, ham=0: uint) { ... }

Que poderia então ser chamado de foo(1) , foo(1, 3) , foo(1, ham=4) , foo(6, 7, 8) , etc.

EDITAR: Por favor, veja a sintaxe proposta de huonw abaixo, que parece muito melhor e mais uniforme com a sintaxe de declaração atual.

Triagem 05/08/2013: nenhum progresso (que eu saiba), embora ainda seja legal; talvez a sintaxe da declaração possa parecer

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

onde o RHS é qualquer expr constante (isto é, coisas que são válidas em uma declaração static ).

IMO, eles são realmente úteis, mesmo o sistema C ++ de _ argumentos padrão_, onde você só pode voltar aos padrões para argumentos não especificados, seria ótimo ... reduzir o número de variantes de funções nomeadas alternativas, embora não seja tão complexo quanto sobrecarregar .. ser capaz de declarar aqueles em structs e variantes de enum também seria ótimo.
Talvez este subconjunto mais simples do comportamento necessário possa ser implementado antes que haja consenso sobre como / se chamar os parâmetros de palavras-chave nomeadas ... isso deixaria os internos prontos?

seria viável / útil implementar como expressões ... em termos de argumentos especificados anteriormente e parâmetros de tipo genérico (por exemplo, capacidade de pesquisar um zero ::ou escreva coisas como slice (& self, start: uint , end: uint = self.len ()) / * "foobarbaz" .slice (6) == "baz" .. ou create_window (parent, x, y, width : uint = parent.width () / 4, height: uint = (width_161) / 100) / _ o padrão é janelas em formato de proporção dourada e 1/4 da largura da tela se você não especificar nenhum tamanho .. * / .. .. ou isso soa apenas como uma receita para inchaço de código ..

Os padrões do estilo c ++ impediriam o aplicativo de função parcial semelhante ao haskell, mas ouvi dizer que é improvável que isso aconteça

Aqui está outra ideia para argumentos padrão. Em vez de permitir que os argumentos sejam completamente eliminados, permitimos que os argumentos para os quais exista um padrão sejam invocados com _ .

Então, dado o adorável exemplo de @huonw de:

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

... poderíamos invocar isso como foo(1, 2, None) , foo(1, _, Some(1)) , foo(1, _, _) , etc.

No entanto, essa ideia não traria nenhum benefício em adotar argumentos de palavra-chave, visto que ainda é estritamente posicional.

Alterando o título para "Argumentos padrão e argumentos de palavra-chave".

não implementado, mas tentei adicionar ao ast & parser; https://github.com/dobkeratops/rust/compare/default_args , é a maneira certa de fazer isso? (Opção @expr .. vai precisar de restrições sobre o que a expr pode ser?)
mergulhar os dedos dos pés na água para experimentar esse recurso.
Eu estaria interessado nisso para aumentar o subconjunto de APIs C ++ que poderiam ser traduzidas, e elas são definitivamente algo que sinto falta do C ++, e o recurso de palavra-chave nomeada seria incrível além do que c ++ faz.
Parecia simples e lógico seguir a segunda sintaxe de declaração sugerida

@dobkeratops que parece bom (embora type Default = Option<@expr> seja provavelmente desnecessário).

As restrições em expr vão em rustc::middle::check_const ; você terá que adicionar uma função como check_fn que verifica cada arg. (Além disso, presumivelmente, deve haver uma verificação de que os argumentos padrão só aparecem no final; e eu acho que deve haver algo no verificador de tipo rustc::middle::typeck e em trans também. )

Ok, o campo é nomeado, então isso é uma anotação desnecessária.
Obrigado pelas dicas sobre onde procurar, eu estava procurando em rustc :: middle :: typeck :: .. mod.rs (check_fn something).

em nome da 'integração contínua', vale a pena apresentar o trabalho básico trivial como um pr? Se eu fracassar nisso, talvez alguém que conheça melhor a base de código possa fazer as outras partes muito mais rápido outra vez.
Estou sempre paranóico com mudanças divergentes

Só é destrutivo se você definitivamente decidir _contra eles?

Alguém sugeriu que esse tipo de coisa pode entrar com uma opção -Z (..Session :: debug_opts ... mas eu não encontrei estes swere atualmente propagados em todos os lugares (por exemplo, no analisador).

Posso ver que você pode se opor ao compilador que analisa algo que ainda não usa, eu poderia alterá-lo de um aviso para um erro, talvez ...

@dobkeratops Observe que nenhum desenvolvedor oficial comentou sobre isso ainda, então é muito improvável que qualquer RP seja aceito. Há muita discussão a ser feita aqui a respeito de 1) se queremos argumentos padrão, 2) em caso afirmativo, qual deve ser a sintaxe e 3) se queremos argumentos de palavra-chave (porque a semântica que escolhemos para argumentos padrão pode impedir isso )

Ok, bem, eu acho que eles seriam uma prioridade muito baixa por um longo tempo ainda, já que eles não bloqueiam nada (eles certamente não estão no topo da minha lista de desejos).
Achei que poderia ser uma tarefa fácil e outra familiaridade para o pessoal de C ++.
Suponho que as macros às vezes podem fazer um trabalho semelhante (e mais) ... e posso obter melhores hábitos de nomenclatura para variações comuns / incomuns

Eu posso ver que os argumentos nomeados não são críticos, mas os args opcionais da IMO meio que são. Porque em alguns lugares eu acho que vi alguns métodos com assinaturas e nomes semelhantes apenas para oferecer interfaces mais fáceis quando um determinado argumento não é necessário. Seria péssimo se acabarmos com funções legadas como essa só porque um recurso estava faltando.

Existem muitas partes sutis aqui. Qualquer argumento padrão é uma forma implícita de sobrecarga, então (estou supondo) provavelmente será integrado ao algoritmo de resolução de característica e / ou inferência de tipo. Pode não ser muito difícil, mas vale a pena ter um design cuidadoso e, neste ponto, não tenho certeza se temos orçamento para isso. Como um recurso de _idioma_, imagino que seja compatível com versões anteriores; como um recurso de API, provavelmente informaria alguns projetos de API, portanto, representa o risco de bc.

@Seldaek Concordo que, se quisermos argumentos padrão, é importante implementá-los antes de nos comprometermos com a compatibilidade com versões anteriores do stdlib. No entanto, conheço pelo menos uma linguagem (Go) que filosoficamente rejeita argumentos padrão em favor da abordagem de funções com nomes e assinaturas ligeiramente diferentes.

No entanto, ao contrário do Rust, Go tem alguns recursos que tornam a ausência de argumentos padrão menos dolorosa. Por um lado, eles têm funções variáveis. [1] Em segundo lugar, você pode omitir campos ao criar structs, o que significa que é fácil passar uma estrutura de configuração para uma função (os campos omitidos parecem estar configurados para valores padrão especificados pelo compilador (?), Em vez de ser algo que um usuário pode personalizar). [2]

Para o primeiro, talvez possamos nos safar com macro-hackeamentos para obter o mesmo resultado (embora a um custo de implementação trabalhoso). Na verdade, a próxima substituição de fmt!() faz o seguinte:

ifmt!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3)

Este é um código que funciona hoje.

Quanto ao último, o melhor análogo que temos seria usar a atualização de estrutura funcional de acordo com o seguinte:

struct Foo { x: int, y: int, z: int }

impl Foo {
    fn default() -> Foo {
        Foo{x: 0, y: 0, z: 0}
    }
}

fn bar(f: Foo) {
    printf!(f);
}

fn main() {
    bar(Foo{y: 5, ..Foo::default()});
}

Já vi esse método sugerido de maneira improvisada no passado como uma solução alternativa para a falta de argumentos padrão, mas é bastante desajeitado na prática (compare bar(Foo{y: 5, ..Foo::default()}) com bar(y=5) , ou minha proposta mais conservadora de bar(_, 5, _) ) e, claro, força todos os chamadores a criar uma estrutura, mesmo se eles _do_ quiserem passar todos os argumentos.

Para reiterar, como alguém que vem do Python e Javascript, eu obviamente receberia argumentos padrão. No entanto, tenho certa empatia com a filosofia de Go de tornar as APIs explícitas (em troca de (potencialmente muito) aumentar o número de funções na própria API).

[1] https://groups.google.com/d/msg/golang-nuts/NWMReL1HueQ/X9mdYduCOB8J

[2] http://stackoverflow.com/questions/2032149/optional-parameters

Devo também salientar que ele interage com fechamentos e tendo referências de primeira classe para funções.

@bstrie, obrigado pela visão geral do Go. Eu concordo com o ponto de que a sobrecarga provavelmente é uma má ideia porque cria APIs confusas. Da mesma forma, os métodos "sobrecarregados" do jQuery, que podem receber um retorno de chamada em praticamente qualquer argumento que você queira, são muito estranhos e difíceis de implementar.

E eu meio que concordo que as funções de wrapper para preencher os argumentos padrão não são tão ruins em alguns casos. Mas o problema é que, ao mesmo tempo que o salva de alguns casos confusos, ele introduz muitos clichês em outros. Um exemplo é *::to_str_radix(num, radix) . Se tivéssemos argumentos opcionais, isso poderia muito bem ser dobrado em *::to_str(num, radix = 10) . Acho que, em alguns casos, a explosão de variantes de funções realmente torna as coisas menos intuitivas e mais difíceis de lembrar. Só meus dois centavos, obviamente.

Eu posso ver que é uma boa hora para adiá-los;
mas vai _realmente_ omite padrões filosoficamente, ou são apenas eles justificando as compensações de orçamento de tempo de implementação que fizeram? Eu ficaria surpreso se alguém pensasse que percorrer long_function_names_with_lots_of_trailing_qualifiers é um passo à frente :), mas se eles dissessem "isso nos permite alavancar ferramentas simples, ainda não desenvolvemos um IDE poderoso ..." isso é outra questão.

Eu definitivamente sinto falta da sobrecarga e dos padrões do C ++ .. mas estou feliz em trocá-los por outras conveniências de Rusts e a promessa de uma alternativa C ++ (depois de ficar preso a uma linguagem principal por tanto tempo ..)

Para um experimento, pensei em tentar fazê-los como um 'hack do analisador' ... trabalhando no nível de macros? inlining o expr padrão nos sites de chamada onde os argumentos estão faltando?
Percebo que é improvável que seja um design aceitável - mas me faz acreditar que não é _impossível_ ter args padrão que funcionam com recursos de ferrugem.

Uma coisa a que ainda estou me acostumando é que os métodos são, em teoria, muito mais utilizáveis ​​na ferrugem.
De volta ao C ++, tenho medo deles por causa dos problemas de cabeçalho / dependência (esse é o principal motivo de eu estar aqui), então geralmente procuro primeiro as funções instintivamente.
Talvez eu sinta menos falta dos padrões quando estiver usando melhor a ferrugem.

Só para que a ideia não desapareça: o aspecto tagarela de bar(Foo{y: 5, ..Foo::default()}) me parece ser a parte ..Foo::default() .

Se tornássemos mais fácil omitir campos em uma construção de estrutura, deixando-os cair para um padrão, então talvez essa abordagem fosse mais palatável.

Por exemplo, uma nova forma, provavelmente definida apenas para estruturas que implementam alguma característica apropriada ( Zero ou Default ou qualquer outro), que se parecia com isto:

Foo{y: 5, *) onde * toma o lugar do anterior ..Foo::default() .

Então, o exemplo é "apenas" bar(Foo{y: 5, *}) . Talvez outros prefiram que Foo não esteja lá, mas isso parece muito claro para mim.

Suponho que enums também forneçam algumas outras maneiras de passar grupos de parâmetros opcionais de maneira conveniente, diferentemente do que um programador C ++ está acostumado ... e uma maneira de intercalar anotações e valores com uma chamada.
Isso parece se adequar a casos como criar algo com muitos parâmetros de configuração também

@pnkfelix Lembre-se de que a Foo struct nesse exemplo provavelmente teria um nome muito mais autodescritivo e, mesmo com seu açúcar proposto, seria algo assim na prática:

html::start_server(html::ServerOpts{port: 10088, *});

Parece um pouco melhor. Ainda não tenho certeza se isso ajuda o suficiente para garantir uma nova sintaxe, ou se é suficiente para satisfazer todos os casos de uso de argumentos padrão.

Na verdade, para usar meu próprio exemplo, ao inicializar algo com várias opções, como um servidor HTML, provavelmente ficaria tão feliz configurando uma estrutura server_opts antecedência e, em seguida, apenas chamando start_server(server_opts) . E eu realmente não me importo de adicionar uma linha com ..html::ServerOpts::default se minha inicialização de struct já abrange várias linhas.

Acho que talvez precisemos repensar onde os argumentos padrão seriam mais úteis. Para mim, não é onde há muitos argumentos potenciais; estes devem ser bastante raros, e algumas linhas para iniciar uma estrutura de configuração são suficientes. Em vez disso, sinto falta de argumentos padrão quando 1) a operação é bastante comum e 2) a operação tem, no máximo, um ou dois argumentos que quase sempre são supérfluos, mas necessários para a integridade. Por exemplo:

let foo = Some('a');
foo.unwrap();  // same as today
foo.unwrap('z');  // imagine that this replaced unwrap_or_default

int::from_str("4");  // same as today
int::from_str("4", 16);  // imagine that this replaced from_str_radix

No entanto, agora que os digitei, na verdade prefiro a explicitação dos métodos separados. :) Talvez eu realmente não saiba o que quero afinal!

@dobkeratops , de sua experiência com C ++, você pode nos dar alguns exemplos de APIs C ++ legais que são ativadas por argumentos padrão?

Comentários em https://github.com/mozilla/rust/wiki/Meeting-weekly-2013-08-13 parecem indicar que os desenvolvedores veem isso como um recurso de um futuro distante. Nomeando.

OCaml faz argumento opcional / padrão sem sobrecarregar, eu acredito. Argumentos opcionais do tipo T sem um valor padrão são convertidos implicitamente para a opção T e a função deve verificar se um valor foi fornecido. Também existem restrições na declaração e uso de argumentos opcionais para fazer o compilador saber quando um argumento foi omitido. Todos os argumentos opcionais devem ter rótulos (devem ser argumentos de palavra-chave para serem opcionais). Isso complica o sistema de tipos do OCaml (os rótulos se tornam parte do tipo da função), mas acho que isso é um artefato de ter que interoperar com o resto do sistema de tipos Hindley-Milner.

Vindo de C ++, eu realmente sentiria falta dos valores de argumento padrão. Costumo usar funções com grande número de parâmetros, onde a maioria deles quase sempre tem valores padrão, exceto em alguns casos raros de uso ou de teste. Adicionar derivados de nome de função puramente combinatórios em vez de valores padrão criaria uma grande confusão de nomes realmente longos.

Eu concordo que os argumentos padrão e / ou argumentos de palavra-chave seriam extremamente úteis. Acho que devemos trabalhar em uma proposta pré-1.0 que descreve o que precisa ser feito em termos de sobrecarga e talvez tenha uma discussão sobre a sintaxe.

É bom ver mais pessoas comentando a favor :) Outra razão pela qual eu queria era aumentar a quantidade de APIs C ++ que poderiam ser traduzidas perfeitamente. Usar bibliotecas C ++ é um grande motivo pelo qual estamos presos ao C ++.

Suspeito que a única maneira de conseguir isso se a comunidade pudesse _implementá-lo_ sem nenhum custo para os desenvolvedores centrais.
Eu concordo que eles têm coisas muito mais importantes a fazer, e enums recuperam o polimorfismo perdido com possibilidades não imediatamente aparentes para cérebros condicionados em C ++.

É por isso que eu queria apresentar as bases para analisá-lo na estrutura de dados AST. Estaria pronto para outra pessoa pegá-lo e tentar encontrar uma solução. o feedback inicial me desencorajou de fazer um PR.

do c ++, vejo o que scala e python têm sobre isso com inveja. Uma linguagem nativa não GC com essa elegância seria ótimo.
..sua mais funcionalidade em um lugar, menos navegação para descobrir o que está acontecendo, menos profundidade de aninhamento, menos ruído no código-fonte. Não apenas uma digitação trivialmente menor.
talvez você possa até alegar que é mais autodocumentável, a assinatura da função informa mais sobre como ela é usada.

a parte difícil é o verificador de tipo, evitando possibilidades sutis de bugs e erros confusos se fosse feito apenas como um hack do analisador, ele pensa.

imagine se você pudesse escrever coisas assim ..

fn substr(a:&str, start:int, end:int=a.len())->~str

apenas hackear esse tipo de coisa no AST sem verificações de erro, tenho certeza que muito pode dar errado .. mas imagine se pudéssemos consertar isso para fazer o que era esperado :)

Apenas um bug. Podemos debater os méritos, mas não bloquearemos o lançamento disso.

Gosto do comportamento que @tautologico descreve em seu comentário.

Aplicado ao Rust, acredito que isso se traduziria no seguinte:

fn ga(bu: int, zo: Option<int>, meu: Option<int>) {
  let zo = zo.get_or_default(42);
  let meu = meu.get_or_default(99);
  ...
}

E todas essas chamadas seriam válidas:

ga(10, 20, 30); // 20 and 30 are automagically
                // converted to Some(20) and Some(30)
ga(10, 20);         // ga(10, 20, 99) 
ga(10);             // ga(10, 42, 99)
ga(10, None, None); // ga(10, 42, 99)
ga(10, 20, None);   // ga(10, 20, 99)
ga(10, None, 30);   // ga(10, 42, 30)

A regra é que os parâmetros Option<T> finais podem ser omitidos. Aqui, o tipo Option é reutilizado, mas se isso causar alguns problemas, outro tipo OptParam mais específico pode ser criado.

Esta proposta permite ao chamador escolher precisamente os parâmetros que deseja fornecer e aqueles que deseja manter padrão, independentemente de sua posição. Além disso, acho bom que o valor padrão não apareça na lista de parâmetros. Dessa forma, o valor padrão não se limita a uma expressão constante, pode depender do estado do objeto ou de outros parâmetros.

Exemplo da vida real de uma função que mostra uma caixa de diálogo GUI:

fn showDialog(message: ~str,
              parent: Option<Widget>,
              title: Option<~str>,
              type: Option<DialogType>,
              icon: Option<Icon>) { ... }

// Display an info box in the middle of the screen.
// Set the title to "information", translated in the current locale
// Set the icon to an "info" icon
showDialog(~"hello, world!");

// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a "warning" icon
showDialog(~"sick, sad world!", None, None, WarningDialog);

// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a custom icon
showDialog(~"sick, sad world!", None, None, WarningDialog, bugIcon);

Neste exemplo:

  • o valor padrão de parent é None (sem pai)
  • o valor padrão de type é InfoDialog
  • o valor padrão de icon depende do valor de type
  • o valor padrão de title depende do valor de type e do local atual

Agora você também está propondo adicionar Option como um novo tipo primitivo. É inteiramente um recurso de biblioteca agora, já que é um simples enum .

Gostaria de acrescentar que os argumentos padrão da IMO são um bilhão de vezes melhores com argumentos de palavras-chave. Eu não gosto da ideia de args padrão, mas nenhum args de palavra-chave.

um é um trampolim para o outro e os padrões ainda são úteis por si só, você tem alguns sinalizadores extras .. e pode deixá-los fora se você quiser apenas os padrões ..
concordou que combinam bem se você tiver os dois.
O ponto principal é implementá-los :)
como adaptá-lo para o typechecker / type inferer .. ou como encaixá-lo.

Os valores padrão para argumentos posicionais resultam em código enigmático. Por que não ter padrões apenas para argumentos de palavras-chave? (Se forem implementados).

IMO ... não é "ou ou".
Ambos são úteis. Um é mais simples de implementar, com menos opções a serem feitas na sintaxe;
então faz sentido para nós quanto a isso, ou tentar implementar isso primeiro, como um trampolim ...

Como a omissão de valores padronizados é mais enigmática do que as chamadas de função normais? Os programadores C ++ já estão acostumados a classificar os parâmetros deles. Normalmente algum tipo de palavra de sinalizadores de controle com um padrão sensato no final .. apenas omita o valor em vez de escrever (.., BLAH_DEFAULT) qualquer coisa .. esclarece listando apenas informações significativas. Pessoas que precisam do Rust provavelmente já lidaram com C ++ e, portanto, estão acostumadas com ele.

Percebi hoje cedo que minha proposta acima (que tentava aproveitar a sintaxe de extensão de estrutura existente para fornecer algo próximo aos argumentos padrão, visto que considero structs já fornecendo os ingredientes para argumentos de palavra-chave), ignorou uma questão importante: a sintaxe que eu estava propondo assumiu que alguém seria capaz de fornecer um padrão para _cada_ argumento (e então todos eles seriam elevados para o único parâmetro de estrutura).

Obviamente, isso não é verdade em geral: muitas vezes, alguns argumentos são obrigatórios e outros opcionais.

Eu ainda gostaria de descobrir uma maneira de aproveitar os recursos que já temos, em vez de bagunçar ainda mais a sintaxe de declaração de função, principalmente porque as declarações de função _já são complicadas_ graças às várias maneiras de escrever funções / encerramentos / métodos.


Meu brainstorming atual se concentrou em se poderíamos alavancar a sintaxe de estrutura existente, talvez adicionando expressões padrão para campos na definição de um item de estrutura. Isso permitiria que alguém tivesse alguns campos que poderiam ser eliminados e outros que seriam obrigatórios. Algo como:

struct Foo { x: int = 0, y:int = 0, z:int = 0 }

fn bar(f: Foo) {
    printf!(f);
}

struct Baz { x: int, y: int, z:int = 0 }

fn quux(g: Baz} {
    printf!(g);
}

fn main() {
    bar(Foo{y: 5});
    quux(Baz{y: 5}); // ~ERROR: required field, `x`
}

@pnkfelix Eu gosto dessa sugestão, talvez não seja muito difícil alavancar os mesmos internos, mas gerar uma estrutura anônima. Portanto, tomando seu exemplo, este seria efetivamente o mesmo código com um pouco da mágica do compilador para inferir a estrutura:

fn bar({ x: int = 0, y:int = 0, z:int = 0 }) {
    printf!(f);
}

fn quux({ x: int, y: int, z:int = 0 }) {
    printf!(g);
}

fn main() {
    bar({ y: 5 });
    quux({ y: 5 }); // ~ERROR: required field, `x`
}

@visionmedia @bstrie Precisamos realmente nos preocupar em nos livrar do nome da estrutura? Uma declaração de uso de religação não resolve o problema de nomes longos, reduzindo o número mínimo de caracteres adicionais no site de chamada para 3 (para o nome + colchetes esquerdo e direito)?

Ou seja, com o exemplo ServerOpts que bstrie postulou para mim:

fn main() {
  use O = html::StartServerOptions;
  ...
  html::start_server(O{port: 10088});
}

@pnkfelix eu gosto do

struct Foo {
   x: int = 10,
   y: float = 1.0
}

sintaxe, mas não acho que precisamos dela para obter argumentos obrigatórios sensatos (não nomeados, infelizmente), apenas passe os obrigatórios separadamente:

struct FooOptions { ... }
static default: FooOptions = FooOptions { ... };
fn foo(compulsory: int, required: uint, necessary: float, optionals: FooOptions) { ... }

FWIW, também gosto da ideia de estrutura com valores padrão. Acho que seria um atalho para:

struct Test {
    m: int,
    y: int
}

static DEFAULT: Test = Test{m: 10, y: 15};

Test{y: 5, ..DEFAULT}

Eu gosto dos padrões de estrutura dentro da definição também

Eu também tenho essas análises com essa sintaxe, mas na verdade não as usei. Ainda não sei como lidar com a fonte do typechecker, etc ...

Talvez o fato de já haver um mecanismo padrão possa torná-lo um pouco mais fácil de implementar.

ré. Acho que originalmente as estruturas anônimas as tinham, mas tiveram que removê-las 'por causa de interações estranhas'?
eu preferiria ter os dois mecanismos disponíveis. talvez estruturas de parâmetros aliviassem a necessidade de argumentos _palavra-chave_, mas padrões finais como em c ++ ainda seriam úteis

para referência -
http://www.parashift.com/c++-faq-lite/named-parameter-idiom.html
lol - argumentos de palavra-chave interessantes, mas verdadeiros, evitariam a necessidade de hacks pesados ​​padronizados como este

ré. Acho que originalmente as estruturas anônimas as tinham, mas tiveram que removê-las 'por causa de interações estranhas'?

Originalmente, todos os tipos de estrutura eram estruturais. Eles foram removidos (substituídos por tipos de estruturas nominais) por causa de interações estranhas com características, eu acredito.

Eu gosto dos valores padrão na ideia de definições de estrutura. Isso parece fácil de entender e pelo menos evita ter que enumerar todos os campos ao construir uma estrutura - isso é um problema para estruturas do tipo "opções" porque significa que quando um novo campo "opcional" é adicionado, todo site de chamada deve ser modificado para inicialize-o para 0 / Nenhum / etc.

Vindo de Perl (onde os padrões são um padrão ad-hoc), Python 2 (onde você pode obter foo() takes at least 2 arguments (2 given) ) e Python 3 (onde você pode ter argumentos nomeados apenas _sem_ padrões), eu proponho humildemente: adicione um bit _teeny_ de açúcar para permitir a declaração de uma estrutura como o argumento final para uma função, mas deixe o chamador _inline_ os campos de estrutura.

por exemplo

impl int {
    struct FromStrOptions {
        radix: uint = 10
    }
    fn from_str(s: str, opts: FromStrOptions) -> int {
        // ...
    }
}

int::from_str("4", radix: 10);

Vantagens:

  • Nenhuma nova sintaxe para a definição da função, que na verdade não precisa se preocupar em como o chamador a chama. Isso é semelhante a como funciona a passagem de blocos com do : é puramente a preocupação de quem liga. (É necessário haver uma maneira de ativar / desativar, no caso de ter um último argumento que é uma estrutura, mas não pretendendo que seja usado para kwargs? Alguém se importaria? Não acho que alguém se importe com do .)
  • Layout binário e semântica de chamada C e etc. ainda estão muito bem definidos.
  • Sobrecarga por tipo ou aridade não é mais difícil de implementar algum dia, exceto que o tipo do argumento final não pode contribuir para a sobrecarga de tipo. Também não deve interferir com argumentos variáveis, se isso acontecer.
  • O mesmo conjunto de padrões pode ser reutilizado em várias funções.
  • Algo como **kwargs de graça: basta criar a estrutura e passá-la como um argumento posicional.
  • Argumentos de palavra-chave necessários caem naturalmente disso: apenas não forneça um padrão na estrutura, e você é forçado a passar _algo_ pelo nome.
  • Nenhuma confusão maluca ao passar argumentos posicionais por nome; você simplesmente não pode fazer isso.

Desvantagens:

  • Um pouco mágico. Mas não mais do que passar 5 e ter o tipo inferido do argspec da função, eu acho.
  • Não está claro como ele interagiria com o do idiom existente. Pode ser fácil dizer que tem que ser o último argumento _escrito_, ou seja, funciona como penúltimo argumento apenas quando usado com do .

FWIW int::from_str("4", radix: 10) entraria em conflito com o, anteriormente proposto, uso de : como operador de atribuição de tipo.

Maldições, frustradas por ASCII.

int::from_str("4", radix☃ 10)

Eu votaria pelo uso de = para a palavra-chave args, apenas desautoriza a ideia de C de atribuições dentro de subexpressões .. (o que talvez não se encaixe tanto com rusts 'ideias principalmente imutáveis ​​de qualquer maneira) .. acho que Scala tem isso, para um possível exemplo de como ele se encaixa em uma sintaxe semelhante a c?

Há algum lugar para pensar em como implementar alguma dessas coisas. Eu me perguntei se o estilo do argumento da variável poderia ser implementado internamente como uma sobrecarga de função com base na contagem de argumentos (quase como se o número de argumentos fosse pós-fixado no nome da função ..)

Falamos sobre isso na reunião semanal

Acho que todos concordaram que outras complicações para os formulários de declaração / invocação de função não são boas, e que estender as declarações de struct para permitir padrões de campo (associados a expressões const), como sugerido em meu comentário anterior , seria uma maneira razoável de obter a maioria do que é desejado aqui.

A equipe também decidiu que resolver isso de qualquer maneira não é uma prioridade para a versão 1.0. O líder do projeto @brson sugeriu que deixássemos a prototipagem de qualquer coisa nesta tarefa para o pós-1.0 (por exemplo, para uma versão 1.1).

Portanto, essa é uma dica forte para todos os contribuidores: hackear o quanto quiser, mas por favor não envie solicitações pull ou r + nada para este bug até o lançamento posterior ao 1.0.

Sem dúvida, Rust é uma linguagem incrível. Ele faz tantas coisas certas que nem chega a ser engraçado. Mas acho que adiar esse recurso para o post 1.0 é um erro.

Deixe-me explicar.

Os argumentos de função padrão são _críticos_ para um bom design de API; a falta deles complica o que, de outra forma, seriam APIs mais simples. Exemplos da biblioteca std:

E outros.

Ter todos esses nomes de funções extras que podem ser removidos com argumentos padrão aumenta desnecessariamente a carga cognitiva. O usuário agora precisa saber dois (ou mais) nomes em vez de um.

Se os argumentos padrão forem adiados para depois de 1.0, essas funções na biblioteca std não podem ser removidas devido à compatibilidade com versões anteriores. Isso significa que mesmo se os argumentos padrão forem adicionados no futuro e a versão "vanilla" das funções agora puderem fazer o trabalho das outras variantes, as variantes antigas permanecerão por aí e os desenvolvedores terão que saber sobre elas porque alguém certamente usará eles, mesmo que apenas por engano.

Para que a carga cognitiva extra permaneça _para sempre_.

[NB O mesmo argumento pode ser feito para sobrecarga de função baseada em tipo de parâmetro (a solução alternativa baseada em característica atual é muito complicada e as funções da biblioteca std não a usam), mas este problema não é o lugar para essa discussão.]

Rust devs, obrigado por trabalhar no Rust e torná-lo incrível!

Estou adicionando o marco do "futuro distante" para enfatizar o extremo desânimo que nós, da equipe principal, desejamos comunicar a qualquer pessoa que dedique um segundo de seu tempo ao assunto. Se você deseja contribuir com o Rust agora, resolva um dos 41 bugs abertos no marco 1 (bem-definição):

https://github.com/mozilla/rust/issues?milestone=12&state=open

ou um dos 104 bugs abertos no marco 2 (compatibilidade com versões anteriores):

https://github.com/mozilla/rust/issues?milestone=13&state=open

ou um dos 68 bugs abertos no marco 3 (recursos que concordamos em um ponto são cruciais para o lançamento do Rust 1.0):

https://github.com/mozilla/rust/issues?milestone=14&state=open

ou mesmo apenas comente sobre um desses bugs para pedir esclarecimentos ou sugerir formas de progredir. São 213 bugs para escolher; progredir em qualquer um deles neste ponto seria muito mais valioso para Rust do que esta questão. E quem conseguir fechar um desses bugs terá nossa maior gratidão.

Posso ver que a equipe principal tem coisas muito mais importantes a fazer, mas parece uma pena 'comunicar um desânimo extremo' :(
+1 para comentários vallóricos. nomear é difícil, vasculhar a documentação e aprender mais nomes é desconcertante ..; args / overloarding padrão tornam isso mais fácil; com argumentos de palavras-chave, você teria a oportunidade de ir além do C ++ nisso. (Eu gostaria que o github tivesse votação, eu poderia simplesmente clicar em +1 em vez de reclamar um pouco mais aqui lol)

@Valloric Concordo inteiramente (como disse antes) e realmente espero que a equipe do Rust Core reconsidere ou pelo menos discuta novamente os argumentos opcionais porque isso impacta o design da API. Args nomeados podem definitivamente esperar, já que eles afetam principalmente o site de chamada (é claro que o nome de args torna-se parte do design da API assim que nomeamos args, então ele tem um leve impacto, mas não quebra o BC se os nomes de args são limpos quando args nomeados são introduzidos).

Não há consenso de que esse seria um bom recurso de linguagem. Também não há uma proposta concreta com todos os detalhes resolvidos para parâmetros nomeados / padrão. Isso não vai acontecer no 1.0, porque há um conjunto existente de recursos de linguagens centrais para consertar ou remover antes de criar um açúcar sintático não essencial.

'sonhar' faz com que pareça algo fantasioso, mas esses recursos foram comprovados em outras línguas. Não é apenas o açúcar da sintaxe - está reduzindo a quantidade de código que você precisa navegar, reduzindo o número de símbolos que você precisa aprender

Isso não vai acontecer no 1.0, porque há um conjunto existente de recursos de linguagens centrais para consertar ou remover antes de criar um açúcar sintático não essencial.

Não estou dizendo que os outros recursos da linguagem que precisam ser corrigidos / implementados não sejam mais importantes; eles provavelmente são. Mas não é ou / ou. Tudo o que estou dizendo é que esse recurso deve ser fortemente considerado para 1.0 (além dos outros recursos) porque não tê-lo impacta a qualidade das APIs na biblioteca std _para sempre_.

Esta é provavelmente uma opinião controversa, mas as linguagens de programação da IMO vivem e morrem mais pelas APIs que fornecem do que por seus recursos básicos. A biblioteca std "baterias incluídas" do Python vendeu toda a linguagem. CPAN mantém Perl vivo. .Net torna a escrita de código C # incrível, e LINQ é a melhor coisa desde o pão fatiado. Uma das maiores falhas do C ++ é a falta de APIs boas e padronizadas. Etc.

As APIs são essenciais para o sucesso de uma linguagem, portanto, os recursos que permitem a criação de _good_ APIs não devem ser descartados como "açúcar sintático não essencial".

@dobkeratops : ao dizer _digindo_, estou tentando enfatizar que nenhuma proposta completa foi feita, então não está em uma etapa de tomada de decisão

Essa discussão é improdutiva. Para evitar que isso esgote o tempo de ninguém, encerrarei o problema. Se alguém quiser reabri-lo (ou começar uma nova edição) daqui a um ano, ou depois disso, tudo bem.

Se alguém vier com uma proposta com quase todos os detalhes resolvidos (gramática, semântica), sugiro postá-la na lista de discussão. Se for alcançado um consenso sobre uma certa maneira de fazer isso ser a _ maneira certa_, então faz sentido abrir um problema e implementá-lo como um recurso experimental por trás de um sinalizador como once funções.

Em segundo lugar, @thestinger - se alguém (ou um pequeno grupo de pessoas) tem uma proposta completa, ou uma proposta com alguns espaços em branco claramente soletrados que estão em discussão, seria apropriado para essa pessoa apresentá-la pela lista de discussão. Não há promessa de que implementaremos essa proposta, mas fazer o trabalho de formalizar a ideia e explicar como ela interage com outros recursos de linguagem aumentaria muito o valor da sugestão.

@thestinger @catamorphism Obrigado a ambos por manterem a mente aberta! Quando eu tiver algum tempo, prepararei uma proposta e a enviarei para rust-dev.

Eu criei um pad para debater sobre esse recurso e escrever uma especificação clara sobre ele: https://pad.riseup.net/p/hvbg6dQQnEe7

Estou reabrindo isso. Não é uma prioridade - já há muito a ser feito - mas é um recurso que as pessoas desejam e que não está completamente fora de questão. Não desejo encerrar nenhuma conversa sobre o assunto.

Obrigado @brson !

Eu editei o problema original com um link para o etherpad.

Oi.
Desde a criação do pad, ele foi completado por muitas proposições de design, perguntas, problemas, etc ...
Portanto, criei um segundo painel para "resumir" o painel de discussão, este painel será usado para descrever a solicitação de recurso exatamente.
URL do teclado aqui: https://pad.riseup.net/p/Ca5PBeDjUGxW

@KokaKiwi todos os blocos estão vazios agora. Para onde foi o conteúdo?

@cmr , eu acho:

AVISO: Este pad será EXCLUÍDO se passarem 30 dias sem edições. NÃO HÁ NENHUMA MANEIRA de recuperar a almofada depois que isso acontecer, então tome cuidado!

: carrancudo:

Na verdade, estou pesquisando o pad que criei na instância do Mozilla Etherpad (para evitar esse caso), mas não consigo encontrá-lo em meu histórico e esqueci de publicar o link aqui :(

@cmr @huonw @KokaKiwi , aqui estão dois links no histórico do meu navegador:

https://etherpad.mozilla.org/CQEDa85jLX
https://etherpad.mozilla.org/78FA1bozLd

@dram Era isso que eu estava procurando! Obrigado: smiley:
Talvez a edição deva ser editada com os novos links, eu acho.

(Editado.)

Não tenho nada significativo a acrescentar, a não ser isso, é uma das coisas que estou observando de perto antes de investir mais tempo com o idioma. Vindo de linguagens (Objective-C e Python) que têm esse recurso, acredito que os parâmetros nomeados são um grande impulso indireto para a produtividade por causa de como eles forçam o código de outras pessoas a ser mais legível.

Uma proposta concreta deve ser feita por meio do novo processo RFC: https://github.com/rust-lang/rfcs

Existem muitas idéias conflitantes e tópicos de conversa divergentes aqui para que seja uma área de discussão útil.

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