Rust: Argumentos predeterminados y argumentos de palabras clave

Creado en 6 jun. 2013  ·  70Comentarios  ·  Fuente: rust-lang/rust

No vi ningún problema para los argumentos de palabras clave, ¿algún plan para esto en el futuro?


NOTA: El rastreador de errores no es el lugar para tener una discusión de diseño. Dirija toda la discusión de diseño al etherpad ( https://pad.riseup.net/p/hvbg6dQQnEe7 ) o cree una página de bicis en el wiki.

Etherpad:

Comentario más útil

Triage 2013-08-05: sin progreso (que yo sepa), aunque todavía estaría ordenado; tal vez la sintaxis de la declaración podría verse así

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

donde RHS es cualquier expr constante (es decir, cosas que son válidas en una declaración static ).

Todos 70 comentarios

No ha habido planes para ello, aunque nadie se ha opuesto todavía. He estado pensando en este problema durante mucho tiempo y básicamente se reduce a una solicitud de argumentos predeterminados. Supongo que se vería así:

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

Que luego podría llamarse foo(1) , foo(1, 3) , foo(1, ham=4) , foo(6, 7, 8) , etc.

EDITAR: Consulte la sintaxis propuesta de huonw a continuación, que se ve mucho mejor y más uniforme con la sintaxis de declaración actual.

Triage 2013-08-05: sin progreso (que yo sepa), aunque todavía estaría ordenado; tal vez la sintaxis de la declaración podría verse así

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

donde RHS es cualquier expr constante (es decir, cosas que son válidas en una declaración static ).

En mi opinión, estos son realmente útiles, incluso el sistema C ++ de _argumentos predeterminados_ donde solo puede recurrir a los valores predeterminados para rastrear argumentos no especificados sería genial ... reduce el número de variantes de funciones con nombre alternativas sin ser tan complejo como la sobrecarga ... ser capaz de declararlos en estructuras y variantes de enumeración también sería genial.
Quizás este subconjunto más simple del comportamiento requerido podría implementarse antes de que haya consenso sobre cómo / si llamar a los parámetros de palabras clave nombradas ... ¿prepararía los componentes internos?

sería viable / útil implementar como expresiones ... en términos de argumentos especificados anteriormente y parámetros de tipo genérico (por ejemplo, capacidad de buscar un cero:, o escribir cosas como slice (& self, start: uint , end: uint = self.len ()) / * "foobarbaz" .slice (6) == "baz" .. o create_window (parent, x, y, width : uint = parent.width () / 4, height: uint = (width_161) / 100) / _ el valor predeterminado es ventanas con forma de proporción áurea y 1/4 de ancho de pantalla si no especifica ningún tamaño .. * / .. .. o eso suena como una receta para la hinchazón de código ..

Los valores predeterminados de estilo c ++ excluirían la aplicación de función parcial tipo haskell, pero he oído que es poco probable que se produzca.

Aquí hay otra idea para los argumentos predeterminados. En lugar de permitir que los argumentos se eliminen por completo, permitimos que los argumentos para los que existe un valor predeterminado se invoquen con _ .

Así que dado el hermoso ejemplo de @huonw de:

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

... podríamos invocar esto como foo(1, 2, None) , foo(1, _, Some(1)) , foo(1, _, _) , etc.

Sin embargo, esta idea realmente no proporcionaría ningún beneficio al adoptar argumentos de palabras clave, dado que sigue siendo estrictamente posicional.

Cambio de título a "Argumentos predeterminados y argumentos de palabra clave".

no implementado, pero intenté agregar al ast & parser; https://github.com/dobkeratops/rust/compare/default_args , ¿es esta la forma correcta de hacerlo? (Opción @expr ... ¿necesitará restricciones sobre lo que puede ser expr?)
sumergir los dedos de los pies en el agua para experimentar con esta función.
Estaría interesado en esto para aumentar el subconjunto de api de C ++ que podrían traducirse, y definitivamente son algo que extraño de C ++, y la función de palabra clave nombrada sería increíble más allá de lo que hace C ++.
Parecía simple y lógico ir con la segunda sintaxis de declaración sugerida

@dobkeratops que se ve bien (aunque el type Default = Option<@expr> probablemente sea innecesario).

Las restricciones sobre expr van en rustc::middle::check_const ; tendrá que agregar una función como check_fn que verifique cada argumento. (Además, presumiblemente debería haber una verificación de que los argumentos predeterminados solo aparecen al final; y supongo que tendrá que haber algo en el verificador de tipo rustc::middle::typeck , y en trans también. )

Ok, el campo se nombra para que sea una anotación no necesaria.
Gracias por los consejos sobre dónde buscar, estaba buscando en rustc :: middle :: typeck :: .. mod.rs (check_fn algo).

en el nombre de la 'integración continua', ¿vale la pena que envíe el trabajo preliminar trivial como un PR? Si me equivoco en esto, tal vez alguien que conozca mejor el código base podría hacer las otras partes mucho más rápido en otro momento.
Siempre estoy paranoico acerca de los cambios divergentes

¿Solo es destructivo si definitivamente decides _en_contra_ ellos?

Alguien sugirió que este tipo de cosas pueden ir con una opción -Z (..Session :: debug_opts ... pero no encontré estos swere actualmente propagados en todas partes (por ejemplo, en el analizador).

Puedo ver que puede oponerse a que el compilador analice algo que aún no usa, podría cambiarlo de una advertencia a un error tal vez ...

@dobkeratops Tenga en cuenta que ningún desarrollador oficial ha comentado sobre esto todavía, por lo que es muy poco probable que se acepte ningún RP. Hay mucha discusión aquí con respecto a 1) si queremos argumentos predeterminados, 2) si es así, cuál debería ser la sintaxis y 3) si queremos argumentos de palabras clave (porque la semántica que elegimos para los argumentos predeterminados podría excluir esto ).

Bien, supongo que serían una prioridad muy baja durante mucho tiempo, ya que no bloquean nada (ciertamente no están entre los primeros de mi propia lista de deseos).
Solo pensé que podría ser fruta madura y otra familiaridad para la gente de C ++ ...
Supongo que las macros a veces pueden hacer un trabajo similar (y más) ... y puedo adoptar mejores hábitos de nomenclatura para variaciones comunes / poco comunes

Puedo ver que los argumentos con nombre no son críticos, pero los argumentos opcionales de la OMI sí lo son. Porque en algunos lugares creo que vi algunos métodos con firmas similares y nombres similares solo para ofrecer interfaces más fáciles cuando no se necesita un argumento dado. Sería una mierda si terminamos con funciones heredadas como esa solo porque falta una característica.

Hay muchas partes sutiles aquí. Cualquier argumento predeterminado es una forma implícita de sobrecarga, por lo que (supongo) probablemente se integre con el algoritmo de resolución de rasgos y / o la inferencia de tipos. Puede que no sea muy difícil, pero vale la pena diseñarlo con cuidado, y en este punto no estoy seguro de que tengamos el presupuesto de tiempo para ello. Como característica de _language_, imagino que es compatible con versiones anteriores; como una característica de la API, probablemente informaría al diseño de la API, por lo que representa un riesgo bc.

@Seldaek Estoy de acuerdo en que si queremos argumentos predeterminados, entonces es importante implementarlos antes de comprometernos con la compatibilidad con stdlib con versiones anteriores. Sin embargo, conozco al menos un lenguaje (Go) que rechaza filosóficamente los argumentos predeterminados a favor del enfoque de funciones con nombres y firmas ligeramente diferentes.

Sin embargo, a diferencia de Rust, Go tiene algunas características que hacen que la ausencia de argumentos predeterminados sea menos dolorosa. Por un lado, tienen funciones variadas. [1] En segundo lugar, puede omitir campos al crear estructuras, lo que significa que es fácil pasar una estructura de configuración a una función (los campos omitidos parecen estar configurados en valores predeterminados especificados por el compilador (?), En lugar de ser algo que un usuario pueda personalizar). [2]

Para el primero, podríamos salirse con la suya con la macro-piratería para lograr el mismo resultado (aunque con un laborioso costo de implementación). De hecho, el próximo reemplazo de fmt!() hace esto:

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

Este es un código que funciona hoy.

En cuanto a este último, el mejor análogo que tenemos sería usar la actualización de estructura funcional de la siguiente manera:

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()});
}

He visto este método sugerido sin pensarlo en el pasado como una solución para la falta de argumentos predeterminados, pero es bastante desgarbado en la práctica (compare bar(Foo{y: 5, ..Foo::default()}) con bar(y=5) , o mi propuesta más conservadora de bar(_, 5, _) ) y, por supuesto, obliga a todas las personas que llaman a crear una estructura incluso si _es_ quieren pasar todos los argumentos.

Para reiterar, como alguien que proviene de Python y Javascript, obviamente agradecería los argumentos predeterminados. Sin embargo, simpatizo un poco con la filosofía de Go de hacer que las API sean explícitas (a cambio de (potencialmente) inflar la cantidad de funciones en la propia API).

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

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

También debo señalar que interactúa con cierres y toma referencias de primera clase a funciones.

@bstrie gracias por la descripción general de Go. Estoy de acuerdo con el punto de que la sobrecarga es probablemente una mala idea porque crea API confusas. De manera similar, los métodos "sobrecargados" de jQuery que pueden recibir una devolución de llamada en prácticamente cualquier argumento que desee son muy extraños y difíciles de implementar.

Y estoy de acuerdo en que las funciones de contenedor para completar los argumentos predeterminados no son tan malas en algunos casos. Pero el problema es que, si bien lo salva de algunos casos confusos, introduce una gran cantidad de repetición en otros. Un ejemplo es *::to_str_radix(num, radix) . Si tuviéramos argumentos opcionales, esto podría doblarse en *::to_str(num, radix = 10) . Creo que, en algunos casos, la explosión de variantes de funciones realmente hace que las cosas sean menos intuitivas y más difíciles de recordar. Solo mis dos centavos, aunque obviamente.

Veo que es un buen momento para aplazarlos;
pero, ¿va _realmente_ omite los valores predeterminados filosóficamente, o son solo ellos los que justifican las concesiones de tiempo de implementación y presupuesto que hicieron? Me sorprendería si alguien pensara que recorrer long_function_names_with_lots_of_trailing_qualifiers es un paso adelante :), pero si dijera "nos permite aprovechar herramientas simples, aún no hemos desarrollado un IDE potente ...", eso es otro asunto.

Definitivamente extraño tanto la sobrecarga como los valores predeterminados de C ++ ... pero estoy feliz de cambiarlos por otras comodidades de Rusts y la promesa de una alternativa de C ++ (después de estar atrapado con 1 lenguaje principal durante tanto tiempo ...)

Para un experimento, pensé en intentar hacerlos como un 'hack de analizador' ... ¿Trabajando a nivel de macros? ¿Incluyendo los expr predeterminados en los sitios de llamada donde faltan argumentos?
Me doy cuenta de que es poco probable que sea un diseño aceptable, pero me hace creer que no es _imposible_ tener argumentos predeterminados que funcionen con las características de los rust.

Una cosa a la que todavía me estoy acostumbrando es que, en teoría, los métodos son mucho más útiles en la oxidación.
De vuelta en C ++, les tengo miedo debido a problemas de encabezado / dependencia (esta es la razón principal por la que estoy aquí), por lo que a menudo, instintivamente, busco funciones primero.
Quizás extrañaré menos los valores predeterminados cuando esté usando mejor rust.

Para que la idea no desaparezca: el aspecto hablador de bar(Foo{y: 5, ..Foo::default()}) me parece que es la parte ..Foo::default() .

Si facilitáramos la omisión de campos en la construcción de una estructura, dejándolos caer a un valor predeterminado, entonces tal vez este enfoque sería más aceptable.

Por ejemplo, una nueva forma, probablemente definida únicamente para estructuras que implementan algún rasgo apropiado ( Zero o Default o lo que sea), que lucía así:

Foo{y: 5, *) donde * toma el lugar del anterior ..Foo::default() .

Entonces el ejemplo es "solo" bar(Foo{y: 5, *}) . Tal vez otros prefieran que el Foo tampoco esté allí, pero esto me parece bastante limpio.

Supongo que las enumeraciones también le brindan otras formas de pasar grupos de parámetros opcionales de manera conveniente de manera diferente a lo que un programador de C ++ está acostumbrado ... y una forma de intercalar anotaciones y valores con una llamada.
Eso parecería adaptarse a los casos, como crear algo con muchos parámetros de configuración.

@pnkfelix Recuerde que la estructura Foo en ese ejemplo probablemente tendría un nombre mucho más autodescriptivo, e incluso con su azúcar propuesto se vería así en la práctica:

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

Se ve un poco mejor. Aún no estoy seguro de si ayuda lo suficiente para garantizar una nueva sintaxis, o si es suficiente para satisfacer todos los casos de uso de los argumentos predeterminados.

De hecho, para usar mi propio ejemplo, al inicializar algo con muchas opciones, como un servidor HTML, probablemente estaría igual de feliz configurando una estructura server_opts antemano y luego simplemente llamando a start_server(server_opts) . Y realmente no me importa agregar una línea con ..html::ServerOpts::default si la inicialización de mi estructura ya abarca varias líneas.

Creo que tal vez debamos repensar dónde serían más útiles los argumentos predeterminados. Para mí, no es donde hay muchos argumentos potenciales; estos deberían ser bastante raros, y unas pocas líneas para iniciar una estructura de configuración están bien. Más bien, echo de menos los argumentos predeterminados cuando 1) la operación es bastante común y 2) la operación tiene, como máximo, uno o dos argumentos que casi siempre son superfluos pero necesarios para que estén completos. Por ejemplo:

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

Sin embargo, ahora que los he escrito, prefiero que los métodos separados sean explícitos. :) ¡Quizás no sé realmente lo que quiero después de todo!

@dobkeratops , a partir de su experiencia con C ++, ¿puede darnos algunos ejemplos de buenas API de C ++ que están habilitadas por argumentos predeterminados?

Los comentarios en https://github.com/mozilla/rust/wiki/Meeting-weekly-2013-08-13 parecen indicar que los desarrolladores ven esto como una característica del futuro lejano. Nominación.

OCaml hace un argumento opcional / predeterminado sin sobrecargar, creo. Los argumentos opcionales de tipo T sin un valor predeterminado se convierten implícitamente en la opción T y la función debe verificar si se proporcionó un valor. También existen restricciones sobre la declaración y el uso de argumentos opcionales para que el compilador sepa cuándo se ha omitido un argumento. Todos los argumentos opcionales deben tener etiquetas (deben ser argumentos de palabra clave para que sean opcionales). Esto complica el sistema de tipos de OCaml (las etiquetas se vuelven parte del tipo de la función) pero creo que esto es un artefacto de tener que interoperar con el resto del sistema de tipos Hindley-Milner.

Viniendo de C ++, realmente extrañaría los valores de argumento predeterminados. A menudo uso funciones con una gran cantidad de parámetros, donde la mayoría de ellos casi siempre tienen valores predeterminados, excepto en algunos casos de prueba o de uso poco común. Agregar derivados de nombre de función puramente combinatorios en lugar de valores predeterminados, crearía un gran lío de nombres realmente largos.

Estoy de acuerdo en que los argumentos predeterminados y / o los argumentos de palabras clave serían extremadamente útiles. Creo que deberíamos trabajar hacia una propuesta anterior a 1.0 que describa lo que se debe hacer en términos de sobrecarga y tal vez tener una discusión sobre la sintaxis.

Es bueno ver más personas comentando a favor :) Otra razón por la que quería era aumentar la cantidad de API de C ++ que se podían traducir sin problemas. El uso de bibliotecas de C ++ es una de las principales razones por las que estamos atascados con C ++.

Sospecho que la única forma en que obtendríamos esto si la comunidad pudiera _ implementarlo_ sin ningún costo para los desarrolladores principales.
Estoy de acuerdo en que tienen cosas mucho más importantes que hacer, y las enumeraciones recuperan el polimorfismo perdido con posibilidades que no son evidentes de inmediato para los cerebros condicionados por C ++.

Es por eso que quería enviar las bases para analizarlo en la estructura de datos de AST. Estaría listo para que alguien más lo recogiera y tratara de encontrar una solución. La retroalimentación inicial me desanimó de hacer un PR.

desde c ++ miro lo que scala y python tienen sobre esto con envidia. Un idioma nativo que no sea de GC con esa elegancia sería genial.
... tiene más funcionalidad en un solo lugar, menos navegación para averiguar qué está pasando, menos profundidad de anidamiento, menos ruido en el código fuente. No solo mecanografiar trivialmente menos.
tal vez incluso podría afirmar que es más autodocumentado, la firma de la función le dice más sobre cómo se usa.

la parte difícil es el verificador de tipos, que previene la posibilidad de errores sutiles y confunde errores si solo se hizo como un truco del analizador sintáctico.

imagina si pudieras escribir cosas como esta ...

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

simplemente hackea ese tipo de cosas en el AST sin verificaciones de errores, estoy seguro de que muchas cosas pueden salir mal ... pero imagina si pudiéramos arreglar eso para hacer lo que se esperaba :)

Solo un error. Podemos debatir los méritos, pero no bloquearemos su publicación.

Me gusta el comportamiento que describe @tautologico en su comentario.

Aplicado a Rust, creo que esto se traduciría en lo siguiente:

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

Y todas estas serían llamadas 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)

La regla es que se pueden omitir los parámetros Option<T> finales. Aquí, el tipo Option se reutiliza, pero si esto causa algunos problemas, se podría crear otro tipo OptParam más específico.

Esta propuesta permite al llamante elegir con precisión los parámetros que quiere proporcionar y los que quiere mantener por defecto, independientemente de su posición. Además, creo que es bueno que el valor predeterminado no aparezca en la lista de parámetros. De esta manera, el valor predeterminado no se limita a una expresión constante, puede depender del estado del objeto o puede depender de otros parámetros.

Ejemplo de la vida real de una función que muestra un cuadro 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);

En este ejemplo:

  • el valor predeterminado de parent es None (sin padre)
  • el valor predeterminado de type es InfoDialog
  • el valor predeterminado de icon depende del valor de type
  • el valor predeterminado de title depende del valor de type y de la configuración regional actual

Ahora también está proponiendo agregar Option como un nuevo tipo primitivo. Es completamente una característica de la biblioteca en este momento, ya que es un viejo enum .

Me gustaría agregar que los argumentos predeterminados de la OMI son mil millones de veces mejores con los argumentos de palabras clave. No me gusta la idea de argumentos predeterminados pero sin argumentos de palabras clave.

uno es un trampolín hacia el otro y los valores predeterminados siguen siendo útiles por sí mismos, tiene algunas banderas adicionales ... y puede dejarlas desactivadas si solo desea los valores predeterminados ...
Estuvo de acuerdo en que combinan bien si tiene ambos.
El punto difícil es implementarlos :)
cómo adaptarlo en el comprobador de tipos / inferencia de tipos ... o de qué otra manera insertarlo.

Los valores predeterminados para los argumentos posicionales dan como resultado un código críptico. ¿Por qué no solo tener valores predeterminados para los argumentos de palabras clave? (Si estos se implementan).

En mi opinión ... no es "una o la otra".
Ambos son útiles. Uno es más sencillo de implementar, con menos opciones que hacer en la sintaxis;
por lo que tiene sentido para nosotros hacerlo, o tratar de implementar eso primero, como un trampolín ...

¿Cómo es que la omisión de valores predeterminados es más críptica que las llamadas a funciones normales? Los programadores de C ++ ya están acostumbrados a ordenar los parámetros para esto. Por lo general, algún tipo de palabra de indicadores de control con un valor predeterminado sensato al final ... simplemente omita el valor en lugar de escribir (.., BLAH_DEFAULT) lo que sea ... aclara enumerando solo información significativa. Es probable que las personas que necesitan Rust ya hayan trabajado con C ++ y, por lo tanto, estén acostumbradas.

Hoy me di cuenta de que mi propuesta anterior (que trató de aprovechar la sintaxis de extensión de estructura existente para proporcionar algo cercano a los argumentos predeterminados, ya que veo que las estructuras ya proporcionan los ingredientes para los argumentos de palabras clave), ignoró un problema importante: la sintaxis que estaba proponiendo asumió que uno podría proporcionar un valor predeterminado para _ cada_ argumento (y luego todos se elevarían al único parámetro de estructura).

Por supuesto, esto no es cierto en general: uno tiene muy a menudo algunos argumentos que son obligatorios y otros que son opcionales.

Todavía me gustaría encontrar alguna forma de aprovechar las características que ya tenemos, en lugar de seguir con la sintaxis de declaración de función, principalmente porque las declaraciones de función son _ya complicadas_ gracias a las diversas formas en que uno puede escribir funciones / cierres / métodos.


Mi lluvia de ideas actual se ha centrado en si podríamos aprovechar la sintaxis de estructura existente, quizás agregando expresiones predeterminadas para campos en la definición de un elemento de estructura. Eso permitiría entonces tener algunos campos que podrían eliminarse y otros que serían necesarios. 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 Me gusta esa sugerencia, tal vez no sería demasiado difícil aprovechar los mismos

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 ¿Realmente debemos preocuparnos por deshacernos del nombre de la estructura? ¿No resuelve una declaración de uso de revinculación el problema de los nombres largos, reduciendo el número mínimo de caracteres adicionales en el sitio de llamada a 3 (para el nombre + corchetes izquierdo y derecho)?

Es decir, con el ejemplo de ServerOpts que bstrie me postuló:

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

@pnkfelix me gusta el

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

sintaxis, pero no creo que lo necesitemos para obtener argumentos obligatorios sensibles (sin nombre, desafortunadamente), simplemente pase los obligatorios por separado:

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

FWIW, también me gusta la estructura con la idea de valores predeterminados. Supongo que sería un atajo para:

struct Test {
    m: int,
    y: int
}

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

Test{y: 5, ..DEFAULT}

También me gustan los valores predeterminados de estructura dentro de la definición

También tengo esos análisis con esa sintaxis, pero en realidad no se usan ... Todavía no sé cómo manejar la fuente del comprobador de tipos, etc.

Quizás el hecho de que ya exista un mecanismo predeterminado podría hacer que sea un poco más fácil de implementar.

re. ¿Estructuras anónimas creo que originalmente las tenían pero tuvieron que eliminarlas 'debido a interacciones extrañas'?
Preferiría tener ambos mecanismos disponibles. quizás las estructuras de parámetros aliviarían la necesidad de argumentos _keyword_, pero los valores predeterminados finales como en c ++ aún serían útiles

para referencia -
http://www.parashift.com/c++-faq-lite/named-parameter-idiom.html
lol - argumentos de palabras clave interesantes pero verdaderos evitarían la necesidad de trucos pesados ​​repetidos como este

re. ¿Estructuras anónimas creo que originalmente las tenían pero tuvieron que eliminarlas 'debido a interacciones extrañas'?

Originalmente, todos los tipos de estructuras eran estructurales. Fueron eliminados (reemplazados con tipos de estructuras nominales) debido a interacciones incómodas con rasgos, creo.

Me gustan los valores predeterminados en la idea de definiciones de estructuras. Esto parece fácil de entender y al menos evita tener que enumerar todos los campos al construir una estructura; esto es un problema para las estructuras de tipo "opciones" porque significa que cuando se agrega un nuevo campo "opcional", cada sitio de llamada debe modificarse para inicialícelo a 0 / Ninguno / etc.

Viniendo de Perl (donde los valores predeterminados son un patrón ad-hoc), Python 2 (donde puede obtener foo() takes at least 2 arguments (2 given) ) y Python 3 (donde puede tener argumentos de solo nombre _sin_defaults), propongo humildemente: agregar un _teeny_ bit de azúcar para permitir declarar una estructura como el argumento final de una función, pero dejar que la persona que llama _en línea_ los campos de la estructura.

p.ej

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

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

Ventajas:

  • No hay una nueva sintaxis para la definición de la función, que en realidad no tiene que preocuparse en absoluto de cómo la llama la persona que llama. Esto es similar a cómo funciona el paso de bloques con do : es puramente asunto de quien llama. (¿Es necesario que haya una forma de optar por entrar / salir, en el caso de tener un último argumento que sea una estructura pero sin la intención de que se use para kwargs? ¿A alguien le importaría? No creo que a nadie le importe do .)
  • El diseño binario y la semántica de llamadas de C, etc., todavía están bastante bien definidos.
  • La sobrecarga por tipo o aridad no es más difícil de implementar algún día, salvo que el tipo del argumento final no puede contribuir a la sobrecarga de tipos. Tampoco debería interferir con argumentos variadic, si es que alguna vez suceden.
  • El mismo conjunto de valores predeterminados se puede reutilizar en múltiples funciones.
  • Algo como **kwargs gratis: simplemente cree la estructura y páselo como un argumento posicional en su lugar.
  • Los argumentos de palabras clave requeridos quedan naturalmente fuera de esto: simplemente no proporcione un valor predeterminado en la estructura, y está obligado a pasar _algo_ por nombre.
  • No hay confusión loca al pasar argumentos posicionales por nombre; simplemente no puedes hacer eso.

Desventajas:

  • Ligeramente mágico. Pero no más que pasar 5 y tener el tipo inferido de la función argspec, creo.
  • No está claro cómo interactuaría con el idioma actual do . Podría ser bastante fácil decir que tiene que ser el último argumento _escrito_, es decir, funciona como el penúltimo argumento solo cuando se usa con do .

FWIW int::from_str("4", radix: 10) entraría en conflicto con el uso propuesto previamente de : como operador de asignación de tipo.

Maldiciones frustradas por ASCII.

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

Yo votaría por el uso de = para argumentos de palabras clave, simplemente rechaza la idea de C de asignaciones dentro de subexpresiones ... (que tal vez no encaja tanto con las ideas en su mayoría inmutables de rust de todos modos) ... creo que Scala tiene esto, por un posible ejemplo de cómo encaja en una sintaxis similar a c?

¿Hay algún lugar para hacer una lluvia de ideas sobre cómo implementar algo de esto? Me preguntaba si el estilo de argumento variable podría implementarse internamente como una sobrecarga de función basada en el recuento de argumentos (casi como si el número de argumentos estuviera posfijado en el nombre de la función ...)

Hablamos de esto en la reunión semanal

Creo que todos estuvieron de acuerdo en que una mayor complicación de los formularios de declaración / invocación de funciones no es buena, y que extender las declaraciones de estructura para permitir valores predeterminados de campo (asociados con expresiones const), como sugerí en mi comentario anterior , sería una forma razonable de obtener la mayoría de lo que se desea aquí.

El equipo también decidió que resolver esto de cualquier manera no es una prioridad para la versión 1.0. El líder del proyecto @brson sugirió que dejemos el prototipo de cualquier cosa en esta tarea para la versión posterior a 1.0 (por ejemplo, para una versión 1.1).

Así que esa es una fuerte sugerencia para todos los colaboradores: piratea todo lo que quieras, pero no envíes solicitudes de extracción ni r + nada para este error hasta la publicación de la versión 1.0.

Sin lugar a dudas, Rust es un idioma increíble. Hace tantas cosas bien que ni siquiera es gracioso. Pero creo que posponer esta función para la publicación 1.0 es un error.

Déjame explicarlo.

Los argumentos de función predeterminados son _críticos_ para un buen diseño de API; la falta de ellos complica lo que de otro modo serían API más simples. Ejemplos de la biblioteca std:

Y otros.

Tener todos estos nombres de funciones adicionales que podrían eliminarse con argumentos predeterminados aumenta innecesariamente la carga cognitiva. El usuario ahora debe conocer dos (o más) nombres en lugar de uno.

Si los argumentos predeterminados se posponen después de 1.0, estas funciones en la biblioteca estándar no se pueden eliminar debido a la compatibilidad con versiones anteriores. Esto significa que incluso si se agregan argumentos predeterminados en el futuro y la versión "básica" de las funciones ahora puede hacer el trabajo de las otras variantes, las variantes antiguas se mantendrán y los desarrolladores tendrán que conocerlas porque alguien seguramente las usará. ellos, aunque solo sea por error.

De modo que esa carga cognitiva extra permanecerá _para siempre_.

[NB Se puede hacer el mismo argumento para la sobrecarga de funciones basadas en tipos de parámetros (la solución alternativa actual basada en rasgos es demasiado engorrosa y las funciones de la biblioteca estándar no la usan), pero este problema no es el lugar para esa discusión].

Desarrolladores de Rust, ¡gracias por trabajar en Rust y hacerlo increíble!

Estoy agregando el hito del "futuro lejano" para enfatizar el desánimo extremo que en el equipo central deseamos comunicar a cualquiera que dedique un segundo de tiempo a este tema. Si desea contribuir a Rust en este momento, aborde uno de los 41 errores abiertos en el hito 1 (bien definido):

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

o uno de los 104 errores abiertos en el hito 2 (compatibilidad con versiones anteriores):

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

o uno de los 68 errores abiertos en el hito 3 (las características que acordamos en un momento son cruciales para lanzar Rust 1.0):

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

o incluso simplemente comenta sobre uno de esos errores para pedir una aclaración o sugerir formas de progresar. Eso es 213 errores para elegir; progresar en cualquiera de ellos en este punto sería mucho más valioso para Rust que este tema. Y cualquiera que pueda cerrar uno de estos errores tendrá nuestro mayor agradecimiento.

Puedo ver que el equipo central tiene cosas mucho más importantes que hacer, pero parece una vergüenza 'comunicar un desánimo extremo' :(
+1 a los comentarios de vallorics. nombrar es difícil, buscar en la documentación y aprender más nombres es desagradable ..; args / overloarding predeterminados lo hacen más fácil; con argumentos de palabras clave tendrías la oportunidad de ir más allá de C ++ en esto. (Ojalá github tuviera votación, podría presionar +1 en lugar de despotricar un poco más aquí jajaja)

@Valloric Estoy completamente de acuerdo (como dije antes), y realmente espero que el equipo central de

No hay consenso de que esta sea una buena característica del lenguaje. Tampoco hay una propuesta concreta con todos los detalles resueltos para los parámetros con nombre / predeterminados. No va a suceder para 1.0, porque hay un conjunto existente de características de lenguajes centrales para corregir o eliminar antes de soñar con un azúcar sintáctico muy poco esencial.

'soñar' hace que suene como algo fantasioso, pero estas características han sido probadas en otros idiomas. No es solo azúcar sintáctica, sino que reduce la cantidad de código por el que tiene que navegar, lo que reduce la cantidad de símbolos que debe aprender.

No va a suceder para 1.0, porque hay un conjunto existente de características de lenguajes centrales para corregir o eliminar antes de soñar con un azúcar sintáctico muy poco esencial.

No estoy diciendo que las otras características del lenguaje que necesitan ser corregidas / implementadas no sean más importantes; probablemente lo sean. Pero no es lo uno o lo otro. Todo lo que estoy diciendo es que esta característica debe considerarse seriamente para 1.0 (además de las otras características) porque no tenerla afecta la calidad de las API en la biblioteca estándar _forever_.

Esta es probablemente una opinión controvertida, pero los lenguajes de programación de la OMI viven y mueren más por las API que proporcionan que por las características principales en ellos. La biblioteca estándar "baterías incluidas" de Python vendió todo el lenguaje. CPAN mantiene vivo a Perl. .Net hace que escribir código C # sea increíble, y LINQ es lo mejor desde el pan de molde. Una de las mayores fallas de C ++ es la falta de buenas API estandarizadas. Etc.

Las API son fundamentales para el éxito de un lenguaje, por lo que las características que permiten la creación de _buenas_ API no deben descartarse como "azúcar sintáctico no esencial".

@dobkeratops : al decir _soñando_, estoy tratando de enfatizar que no se ha hecho una propuesta completa, por lo que no está en un paso de toma de decisiones

Esta discusión es improductiva. Para evitar que agote más el tiempo de alguien, voy a cerrar el tema. Si alguien quiere reabrirlo (o comenzar una nueva edición) dentro de un año, o más tarde, estaría bien.

Si a alguien se le ocurre una propuesta con casi todos los detalles resueltos (gramática, semántica), sugiero que la publique en la lista de correo. Si se llega a un consenso acerca de que cierta forma de hacer esto es la _ forma correcta_, entonces tiene sentido abrir un problema e implementarlo como una característica experimental detrás de una bandera como las funciones once .

Segundo @thestinger : si alguien (o un grupo pequeño de personas) tiene una propuesta completa, o una propuesta con algunos espacios en blanco claramente explicados que están en discusión, sería apropiado que esa persona la ejecute. por la lista de correo. Esa no es una promesa de que implementaremos esa propuesta, pero hacer el trabajo de formalizar la idea y explicar cómo interactúa con otras características del lenguaje aumentaría enormemente el valor de la sugerencia.

@thestinger @catamorphism ¡ Gracias a ambos por mantener la mente abierta! Cuando encuentre algo de tiempo, prepararé una propuesta y se la enviaré a rust-dev.

Creé una plataforma para debatir sobre esta función y escribir una especificación clara al respecto: https://pad.riseup.net/p/hvbg6dQQnEe7

Estoy reabriendo esto. No es una prioridad, ya hay mucho por hacer, pero es una característica que la gente quiere y que no está completamente descartada. No deseo cerrar ninguna conversación sobre el tema.

@brson ¡ Gracias!

Edité el problema original con un enlace al etherpad.

Hola.
Desde la creación de la almohadilla, se ha completado con muchas propuestas de diseño, preguntas, problemas, etc.
Así que he creado un segundo panel para "resumir" el panel de discusión, este panel se utilizará para describir exactamente la solicitud de función.
Pad URL aquí: https://pad.riseup.net/p/Ca5PBeDjUGxW

@KokaKiwi ahora todos los pads están vacíos. ¿A dónde fue el contenido?

@cmr , supongo:

ADVERTENCIA: Este bloc se BORRARÁ si pasan 30 días sin modificaciones. NO HAY MANERA de recuperar la almohadilla después de que esto suceda, ¡así que tenga cuidado!

:ceñudo:

De hecho, estoy buscando el pad que creé en la instancia de Mozilla Etherpad (para evitar este caso), pero no puedo encontrarlo en mi historial y olvidé publicar el enlace aquí :(

@cmr @huonw @KokaKiwi , aquí hay dos enlaces en el historial de mi navegador:

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

@dram ¡ Eso es lo que estaba buscando! Gracias: smiley:
Quizás el problema debería editarse con los nuevos enlaces, creo.

(Editado.)

No tengo nada significativo que agregar aparte de que esta es una de las cosas que estoy observando de cerca antes de dedicar más tiempo al idioma. Viniendo de los lenguajes (Objective-C y Python) que tienen esta característica, creo que los parámetros con nombre son un impulso importante indirecto a la productividad debido a cómo fuerzan el código de otras personas a ser más legible.

Se debe hacer una propuesta concreta a través del nuevo proceso RFC: https://github.com/rust-lang/rfcs

Hay demasiadas ideas contradictorias e hilos de conversación divergentes aquí para que sea un área de discusión útil.

¿Fue útil esta página
0 / 5 - 0 calificaciones