Godot: Escritura opcional en GDScript

Creado en 25 ago. 2017  ·  38Comentarios  ·  Fuente: godotengine/godot

No sé si esto sería necesario o interesante para algún desarrollador, pero creo que podría ser útil tener escritura opcional en GDScript y esperaba tener una discusión al respecto aquí.

He estado aprendiendo el lenguaje de programación llamado "Nim" porque tiene un módulo bastante amigable para GDNative y me interesa aprender a contribuir allí (ya que C ++ es difícil para mí visualmente PERO no está relacionado con esto, solo algunos antecedentes). Nim tiene un tipo estático, lo que sé que GDSCript es dinámico, pero me encontré con una situación en la que podría querer especificar el tipo para facilitar las cosas.

Principalmente al escribir una función, sería bueno si pudiera especificar el tipo de variable que se permite pasar para que la finalización del código pueda saber de lo que estoy hablando, pero estoy seguro de que hay otras ventajas para esta.

Algo como

func myfunction(a, b : int, c : InputEvent , d : String = "Default Value"):
    (...)

Donde a es dinámico, bd son estáticos pero muestran cómo creo que podría verse la sintaxis.

Sé que puede especificar una sugerencia de tipo para el editor cuando usa la palabra clave export, pero no tiene que mantener la variable en ese tipo una vez que se ejecuta el script. Esta idea podría ir en contra de lo que se supone que debe ser GDScript, o demasiado trabajo para muy poca ganancia, pero hablemos.

discussion feature proposal gdscript

Comentario más útil

Esto debería estar disponible en Godot 3.1.

Todos 38 comentarios

Parece que puedes hacer

func myfunction(something = InputEvent):

y eso le dará un evento de entrada vacío si no pasan nada, pero no obliga a que la variable sea de ese tipo

myfunction('string')

funcionaría bien.

@Hinsbart
¿Alguna referencia a cómo podría verse o cómo funcionará con otras partes de GDScript? ¿O es solo una idea en la hoja de ruta?

Desafortunadamente no, aún no hay referencias al respecto, además de algunas discusiones del IRC.

CC @reduz , para dar algunas pistas de lo que tiene en mente para esto.

@ LeonardMeagher2 debería parecerse a lo que escribiste

Qué pasa:

func myfunction(something is InputEvent, something_else is String):

Eso es bastante bueno para mí, hace que la palabra clave is sea más útil y el código más fácil de entender.

Estoy en desacuerdo. Estaría bien con String something o something : String . something is String parece mal.

String something o something is String ambos bien para mí. El uso de dos puntos hará muchos signos de dos puntos en el código, creo.

Si no se usará is , preferiría String something porque estoy acostumbrado a esta notación de otros idiomas y creo que es más común.

@neikeq
Si pudiera declarar el tipo al crear una var también, como var something:String = " " , estaría de acuerdo

De lo contrario, creo que sería más confuso usar el punto y coma solo en las definiciones de funciones.

"Is" se usa ahora para preguntar acerca de la herencia en gdscript, entonces, ¿por qué no podría requerirlo?

C como prefijo de tipo sería mejor en mi opinión. Es el más corto, más rápido de escribir y puede reemplazar var o func cuando se usan tipos.

Ejemplo:

int some_variable

int some_function(int some_argument):
    return some_variable + some_argument

Mi voto sobre eso.

@ juan-garcia-m
No creo que lo haga más comprensible para alguien que no esté familiarizado con c como escribir. Creo que podría ser más fácil de escribir, pero mucho menos legible.
Prefiero saber de un vistazo qué es una función o variable, y reemplazar esas palabras clave con tipos eliminaría esa capacidad para mí; Es una de las razones por las que C y C ++ son difíciles de entender.

Obviamente, el lenguaje no se puede hacer para mí, pero estoy seguro de que no soy el único que se siente así.

Un problema con "es", actualmente se usa en 3.0 para lo que solía ser "extiende", es decir, verificar si un nodo pertenece a una clase.

@ Zireael07
Es por eso que me parece apropiado verificar el tipo en una declaración de función.
Extends todavía se usa en la parte superior del script, pero para la verificación de tipos se usa "is" (y typeof para los literales, creo).

El prefijo de estilo C es más complicado en general, y para este tipo de lenguajes, el estándar parece ser la variable: convención de tipo, por lo que se usará

¿Por qué se cerró esto? Todavía no está implementado ...

Lo cerré porque era solo una discusión, pero lo volveré a abrir para rastrear el progreso de la función deseada

Realmente espero que se reconsidere el prefijo de estilo C. Desde mi experiencia con Typecript, me siento menos productivo al inicializar variables y personalmente siento que da como resultado un código menos legible. Tener que escribir esto ...

var foo: String = 'bar'

... en lugar de simplemente reemplazar la palabra clave var con el tipo ...

String foo = 'bar'

...me vuelve loca. Soy consciente de que esto suena muy pequeño y delicado, pero a largo plazo de trabajar en un juego, esto solo da como resultado muchas más pulsaciones de teclas de las necesarias.

Así es como debería verse:
godot windows tools 64_2017-12-12_17-21-27

Siguiendo la sintaxis de Python (también similar a TypeScript y Haxe).

Esto es bastante obstinado. Por ejemplo, escribí mucho código TypeScript y me gustó mucho la sintaxis. Al igual que las pestañas frente a los espacios, todos tendrán su propia opinión sobre lo que es mejor, generalmente relacionado con sus antecedentes. Me resulta más fácil ver var y sé que es una variable en lugar de ver String y no saber si será una función o una variable. No veo "más rápido para escribir" como un buen argumento, "más difícil de cometer un error" y "más fácil de leer" son bastante mejores (lo que aún está sujeto a opiniones).

Otro punto a favor del estilo Python: es mucho más fácil de analizar. El analizador actual no tendrá que cambiar mucho para adaptarse a esto. Para los tipos con prefijo, tendrá que considerar más cosas sobre el contexto para decidir qué hacer.

Sí, la legibilidad del código es muy subjetiva. Reescribí tu ejemplo de cómo se vería con la sintaxis de estilo C. _I_ encuentro el código de abajo más legible y no tengo problemas para diferenciar entre variables y funciones.

extends Node

const int MY_CONSTANT = 0
String my_member_variable = "Value"

void my_empty_func():
    pass

float my_other_func(bool arg1, Node2D arg2, bool default = false):
    Vector2 local_variable
    int other_variable = 0

    return float(other_variable)

Tengo que estar en desacuerdo con que "escribir más rápido" no sea un buen argumento, ya que afecta directamente la productividad de los desarrolladores. El estilo Python es un poco más engorroso de leer y escribir que el de C, incluso más ahora que tendremos que escribir -> solo para definir un tipo de retorno. Los únicos argumentos que puedo ver para la sintaxis del estilo Python son 1. para atraer más a aquellos que están familiarizados con Python y 2. porque es más fácil de implementar, y no creo que ninguno de los dos sean argumentos particularmente buenos. No estoy sugiriendo que sea algo fácil de implementar, estoy seguro de que es más difícil y probablemente más allá de mi nivel de habilidad, pero creo que valdría la pena a largo plazo.

De todos modos, eso es solo el valor de mis dos centavos: sonríe :

Python ya es compatible con esto, así que creo que lo mejor que podemos hacer es copiar desde allí.

Para las personas que odian la palabra clave var, podría ser posible agregar la sintaxis similar a Nim que permite algo como esto.

  a:int
  b:float
  c:char

const
  PI:float = 3.1416
  MAX:int = 100

Me pregunto por qué se necesitan los tipos de retorno, si está regresando y esa salida va a una variable de un tipo específico y no coinciden, arrojaría entonces.
Supongo que obtendrás la capacidad de los editores para encontrar ese error antes de ejecutar tu código.

El tipo de retorno void no tiene mucho sentido para mí, imagino que sería más fácil simplemente omitir la definición del tipo de retorno en ese caso.

Finalmente, el -> me parece extraño si tuviéramos tipos de devolución, pero entiendo que es posible que no hagas algo como func somefunc(): String: (¿tal vez?) Pero yo no soy un fanático de esa flecha como sintaxis ya que tampoco me indica mucho, parece direccional pero no tiene nada que ver con eso, me recuerda a las operaciones de bits >> y << pero esos son direccionales en cierto modo.

__No quise cerrar el problema__

Puedo ver que se sabe que los tipos de retorno son importantes en algunos casos, ya que ahora será posible leer cada función para un Nodo, sus variables y su tipo de retorno.

Puede que ahora no tenga mucho sentido, pero la gente podrá escribir complementos e incluso código de juego que puede ser extremadamente dinámico en su funcionamiento. IE, un complemento podría tener este mensaje de advertencia: "Este nodo requiere una función con el tipo de retorno x, pero el nodo al que está intentando hacer referencia no tiene un método que tenga la firma de método requerida".

Escribí un complemento para Unity llamado AI Behavior que usa bastante de este tipo de cosas (a través de System.Reflection) para facilitar el uso. Literalmente tengo un menú desplegable que completo con los nombres de los métodos disponibles y les dejo seleccionar qué método quieren usar como devolución de llamada en función de todos los métodos que tienen la firma correcta, luego, cuando se ejecuta el juego, ese es el método que se llama debajo la circunstancia para la que está hecho. Si no existe ningún método en el objeto / script en el que intentan acceder, mostrará un código de muestra en el inspector para que puedan crear un script copiando / pegando el código y cambiando el nombre de la clase. :)

Entonces, mi pensamiento es que poder tener tanta información de script como sea posible hará que Godot sea mucho más extensible para los escritores de complementos.

También creo que la sintaxis de estilo TypeScript (usando var name: String = "Godot" ) es preferible, ya que se ha convertido en una sintaxis estándar para la escritura opcional agregada a los lenguajes dinámicos.

Además, seguir la sintaxis de Python es una buena idea:
https://www.python.org/dev/peps/pep-0484/
https://docs.python.org/3/library/typing.html

Esto ayudará a las personas que acceden a GDScript desde lenguajes dinámicos similares con escritura opcional.

Por cierto, ¿cuándo / en qué versión podemos esperar que esto se implemente? ¿He visto 3.1 mencionado?

Esto debería estar disponible en Godot 3.1.

Acerca de los argumentos en contra de que el prefijo de estilo C sea más complicado o menos comprensible / legible y sea más acogedor ir con la sintaxis de Python o similar, ¿no están todos los documentos usando ese tipo de prefijo de estilo C con todos los métodos y demás? Por ejemplo, solo para sacar uno de la página de la cámara muy rápido set_orthogonal ( float size, float z_near, float z_far ) No lo entendí del todo al principio, pero creo que solo toma un momento aprenderlo, y tiene mucho sentido después de eso.

Hola, propuesta aqui.

Razón fundamental:
Creo que necesitamos escritura estática, pero evitaría forzar al usuario a especificar tipos si es posible.
Todos los lenguajes compilados principales se están moviendo hacia la inferencia de tipos estáticos.
Sé que a @reduz realmente no le gusta la inferencia de tipos para el código fuente de Godot, y puedo entenderlo para tener un punto de vista
Sin embargo, no estoy seguro de si la implementación es más complicada.
Esto requeriría algunos costructos adicionales como tipos de datos algebraicos.
Aquí hay un lenguaje que usa este principio.

https://crystal-lang.org/docs/

para mantener la compatibilidad con versiones anteriores, se necesitarán algunas palabras clave nuevas para definir funciones y variables. O tal vez podríamos tener algún comentario mágico para habilitar / deshabilitar la verificación de tipos estáticos.

Aquí hay un ejemplo de lo que puede hacer este tipo de sistema de tipos (para aquellos que saben un poco de rubí)

def test(x)
    return true if x
    return "This is false"
end

puts test(false).upcase
puts test(true)

producción:

Error in test.cr:6: undefined method 'upcase' for Bool (compile-time type is (Bool | String))

puts test(false).upcase
                 ^~~~~~

¿Pensamientos?

Quiero mejorar la inferencia de tipos, especialmente para completar el código, pero también para verificar propiedades / métodos no existentes y el recuento de argumentos no válidos en el momento de la edición. Creo que esta parte de un segundo paso después de que se realiza la escritura opcional, por lo que no me preocupo por eso por ahora.

El ejemplo de @ m4nu3lf va bastante lejos, en realidad está infiriendo el tipo resultante en función de la ramificación. No puedo ver cómo se hace sin simular la ejecución del código, donde puede volverse bastante complejo. No estoy seguro de hasta dónde quiere llegar @reduz con GDScript.

Considere que Godot tiene un sistema de tipos bastante estricto, pero todas las variables son Variant (es decir, pueden almacenar cualquier tipo). Dado que esto no pretende romper la compatibilidad, tienen que permanecer así. Entonces, hacer var value = 42 no asumirá automáticamente que la variable es un número entero, especialmente para las de nivel de clase que se pueden configurar a voluntad mediante scripts externos. Eso dificulta mucho lo que puede hacer la inferencia de tipo (aunque se puede usar así para fines de finalización).

Una posibilidad, como se mencionó, es hacer esto opcional. Tal vez una configuración de proyecto, ya que no veo mucho valor de que esto esté habilitado / deshabilitado por script. Aún así, esa es una propuesta separada que debe pensarse en un próximo paso.

@vnen En teoría, al usar alguna palabra clave nueva para parámetros / tipos de retorno / variables como 'auto' o 'let' y usando el mismo algoritmo de crystal-lang, podría inferir el tipo en tiempo de compilación .
Es tipado estático con inferencia de tipo completo.
Digamos que tienes
auto my_field = 42 en una secuencia de comandos, simplemente podría inferir que el tipo es int y restringir la variante subyacente solo a ints.

Sin embargo, ese lenguaje va más allá de simples variables. Puede restringir el tipo a un subconjunto de tipos según las ramas.
como en el ejemplo anterior, el tipo de retorno solo puede ser String O Bool.
Pero para hacer esto debes

  • Cree instancias de funciones para cada nueva combinación de tipos de argumentos e infiera los tipos internos para cada expresión de retorno, guarde la lista de posibles tipos de retorno como el tipo del valor de retorno.
  • Para las ramas, explique todas las ramas posibles donde se asigna una variable, para inferir todos los tipos posibles de la variable en un momento dado. En cualquier momento, restrinja el tipo de variante a aquellos

Además, debe verificar las condiciones de "instancia de" y como en

if my_var instanceof MyClass:
...

entonces sabrá que en el cuerpo de la condición if my_var solo puede ser 'MyClass' por lo que el tipo está aún más restringido.
Para que esto tenga alguna ventaja, solo debe poder llamar a métodos o acceder a campos que tengan nombres compartidos entre todos los tipos posibles.
Así que digamos

auto x = MyClass.new()
if (...):
   x = "hi"

Luego, después de esta x, es MyClass o String. solo podrá llamar a métodos que sean comunes a ambos para evitar errores en tiempo de ejecución.

Doy que esto es mucho más trabajo de lo que pensé originalmente.
Así que quiero volver sobre esto. Tal vez se pueda implementar en el futuro, o tal vez pueda esperar que alguien escriba un enlace para crystal-lang.
De cualquier manera, creo que es una característica realmente genial.

@ m4nu3lf Entiendo cuál es su solicitud, TypeScript es muy similar (que es un lenguaje con el que estoy más familiarizado).

Sin embargo, hay algunos problemas a considerar para GDScript:

  • Primero, no es trivial implementar eso. Puedo ver cómo otros idiomas lo hacen posible, pero aún así tomaría mucho tiempo. Por eso quiero ir paso a paso y pensar en esto más tarde. TypeScript fue creado por el mismo tipo que hizo Turbo Pascal y C #, por lo que hay mucha experiencia allí que no tengo.
  • GDScript se compila un archivo a la vez. Tanto Crystal como TypeScript requieren que compile todo el proyecto antes de usarlo. Eso significa que está un poco nublado para verificar la interacción con otros scripts y singletons.
  • El árbol de la escena. Con get_node() (que se usa mucho) es simplemente imposible saber qué tipo de nodo obtendrá. Solo sabes que es Node pero puede ser de cualquier subtipo. Lo mismo se aplica a los recursos cuando llamas a load() , especialmente si lo estás llamando con un nombre dinámico. _input(event) tiene el mismo problema, ya que event puede ser cualquier subtipo InputEvent.
if my_var instanceof MyClass:
    ...

Esto ya se infiere para la finalización del código. Podría reutilizarse para la verificación de tipos real. Este caso en particular es trivial, pero puede resultar bastante complicado. Por ejemplo, si agrega una segunda condición: if some value == 42 and my_var is Sprite , la finalización del código ya no funcionará.


También quiero escuchar los comentarios de los usuarios, por lo que tener algo disponible pronto es más importante (para mí) que tener todo perfecto después de un largo tiempo de espera.

Resumiendo: quiero mejorar la inferencia de tipos, pero hay que hacerlo paso a paso, con pequeñas mejoras. Una vez que la escritura opcional está disponible, el resto es cuesta arriba.

¿Por qué esta cerrado?

Leí mal un poco, mi mal.

De vuelta al prefijo y sufijo: UML y Kotlin usan ": tipo".
Basic también hizo "DIM a como INT" (y se usó "como" en lugar de ":" o "es")

@programaths Ha pasado mucho tiempo y se ha debatido una y otra vez. Además, hay un PR que implementa postfix, por lo que ahora está un poco fuera de lugar: smiley:

¿Debería cerrar esto?

@ LeonardMeagher2 se cerrará automáticamente cuando se fusione mi RP.

@ bojidar-bg Solo quería agregar algunos lugares donde se usan postfix porque es interesante en sí mismo. UML está diseñado para análisis y personas no técnicas también. Kotlin es un lenguaje que fue desarrollado para ser más cómodo y más moderno que Java mientras implementaba "java efectivo".

Por lo tanto, si necesita escribir por qué se ha elegido postfix, también se pueden incluir. A veces, las personas quieren tener motivos y descartar fácilmente los técnicos a menos que los relacione con el usuario.
(por ejemplo, "El analizador puede resolver ambigüedades antes y tiene una velocidad de 100x, lo que significa que el mismo programa se puede compilar más rápido y permite más pruebas).

Siempre es bueno tener material, ¿no? ;-)

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