Godot: Необязательный ввод в GDScript

Созданный на 25 авг. 2017  ·  38Комментарии  ·  Источник: godotengine/godot

Я не знаю, будет ли это необходимо или интересно какому-либо разработчику, но я думаю, что было бы полезно иметь дополнительный набор текста в GDScript, и я надеялся обсудить это здесь.

Я изучаю язык программирования под названием «Nim», потому что у него есть довольно удобный модуль для GDNative, и мне интересно учиться вносить свой вклад в него (поскольку C ++ для меня сложен визуально, НО не связан с этим, просто немного предыстории). Nim имеет статическую типизацию, и я знаю, что GDSCript является динамическим, но я столкнулся с ситуацией, когда я мог бы захотеть указать тип, чтобы упростить задачу.

В основном при написании функции было бы неплохо, если бы я мог указать тип переменной, которую разрешено передавать, чтобы завершение кода могло знать, о чем я говорю, но я уверен, что есть и другие преимущества для это.

Что-то вроде

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

Где a - динамический, bd - статический, но покажите, как, по моему мнению, может выглядеть синтаксис.

Я знаю, что вы можете указать подсказку типа для редактора при использовании ключевого слова export, но вам не нужно сохранять переменную этого типа после выполнения скрипта. Эта идея может противоречить тому, чем должен быть GDScript, или слишком много работать для очень небольшой выгоды, но давайте поговорим.

discussion feature proposal gdscript

Самый полезный комментарий

Это должно быть доступно в Godot 3.1.

Все 38 Комментарий

Похоже, ты можешь сделать

func myfunction(something = InputEvent):

и это даст вам пустое событие ввода, если они ничего не передают, но не заставляет переменную быть этого типа

myfunction('string')

будет работать нормально.

Это в дорожной карте :)
https://github.com/godotengine/roadmap/blob/master/ROADMAP.md

@Hinsbart
Есть ссылки на то, как он может выглядеть или как он будет работать с другими частями GDScript? Или это просто идея на дорожной карте?

К сожалению, нет, на него пока нет никаких ссылок, кроме некоторых обсуждений irc.

CC @reduz , чтобы дать намек на то, что он

@ LeonardMeagher2 должен выглядеть так, как вы написали

Что о:

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

Для меня это очень хорошо, делает ключевое слово is более полезным, а код более понятным.

Я не согласен. Меня бы устроили String something или something : String . something is String мне не нравится.

String something или something is String оба мне подходят. Я думаю, что использование двоеточия приведет к появлению в коде множества знаков двоеточия.

Если is не будет использоваться, я бы предпочел String something потому что я привык к этой нотации из других языков и думаю, что это более распространено.

@neikeq
Если бы вы также могли объявить тип при создании переменной, например var something:String = " " я бы согласился

В противном случае, я думаю, было бы более запутанным использовать точку с запятой только в определениях функций.

«Is» сейчас используется, чтобы спросить о наследовании в gdscript, так почему же он не может этого требовать?

Префикс типа C вроде был бы лучше IMO. Он короче, быстрее набирается и может заменить var или func при использовании типов.

Пример:

int some_variable

int some_function(int some_argument):
    return some_variable + some_argument

Мой голос по этому поводу.

@ juan-garcia-m
Я не думаю, что это делает его более понятным для тех, кто не знаком с c like набором текста. Я думаю, что это может быть легче печатать, но гораздо труднее читать.
Я предпочитаю сразу знать, что такое функция или переменная, и замена этих ключевых слов типами лишит меня этой возможности; Это одна из причин, по которой мне сложно освоить C и C ++.

Очевидно, что язык не может быть создан для меня, но я уверен, что я не единственный, кто так думает.

Одна проблема с «есть», в настоящее время он используется в 3.0 для того, что раньше было «расширением», то есть для проверки принадлежности узла к классу.

@ Zireael07
Вот почему мне кажется уместным проверить тип в объявлении функции.
Extends по-прежнему используется в верхней части скрипта, но для проверки типа используется "is" (и typeof для литералов, я думаю).

Префикс стиля C в целом более сложен, и для этих типов языков стандартом, по-видимому, является соглашение «переменная: тип», поэтому оно будет использоваться.

Почему это было закрыто? Это до сих пор не реализовано ...

Я закрыл его, потому что это было просто обсуждение, но я открою его снова, чтобы отслеживать прогресс желаемой функции.

Я действительно надеюсь, что префикс стиля C будет пересмотрен. По моему опыту работы с Typescript, я чувствую себя менее продуктивным при инициализации переменных, и лично мне кажется, что это приводит к менее читаемому коду. Надо писать это ...

var foo: String = 'bar'

... вместо простой замены ключевого слова var типом ...

String foo = 'bar'

...сводит меня с ума. Я понимаю, что это звучит очень мелочно и придирчиво, но в течение длительного периода работы над игрой это приводит к увеличению количества нажатий клавиш, чем необходимо.

Вот как это должно выглядеть:
godot windows tools 64_2017-12-12_17-21-27

Следуя синтаксису Python (также похожему на TypeScript и Haxe).

Это довольно самоуверенно. Например, я написал много кода TypeScript, и мне очень нравится синтаксис. Подобно табуляции и пробелам, у каждого будет свое мнение о том, что лучше, обычно связанное с их опытом. Мне легче видеть var и знать, что это переменная, чем видеть String и не знать, будет ли это функция или переменная. Я не считаю «быстрее печатать» хорошим аргументом, «труднее ошибиться» и «легче читать» - лучше (хотя это все еще остается предметом мнений).

Еще один аргумент в пользу подхода Python: его намного проще разбирать. Текущий синтаксический анализатор не должен сильно менять, чтобы приспособиться к этому. Для типов с префиксом нужно будет подумать над контекстом, чтобы решить, что делать.

Да, читаемость кода очень субъективна. Я переписал ваш пример так, как он будет выглядеть с синтаксисом стиля C. _I_ считаю, что приведенный ниже код более читабелен, и у меня нет проблем с различением переменных и функций.

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)

Я не могу согласиться с тем, что «быстрее печатать» не является хорошим аргументом, так как это напрямую влияет на продуктивность разработчиков. Стиль Python немного сложнее для чтения и записи, чем стиль C, тем более, что теперь, когда мы видим, что нам нужно будет ввести -> только для определения возвращаемого типа. Единственные аргументы, которые я могу видеть в пользу синтаксиса стиля Python, - это 1. чтобы больше понравиться тем, кто знаком с Python, и 2. потому что его легче реализовать, и я не думаю, что оба являются особенно хорошими аргументами. Я не говорю, что это легко реализовать, я уверен, что это сложнее и, скорее всего, выходит за рамки моего уровня навыков, но я думаю, что в конечном итоге это того стоит.

Во всяком случае, это всего лишь мои две копейки: smile:. Мне пока нравится работать в GDScript, и я с нетерпением жду, когда у него будет система типов звука.

Python уже поддерживает это, поэтому я думаю, что лучшее, что мы можем сделать, это скопировать оттуда

Для людей, которые ненавидят ключевое слово var, можно добавить синтаксис типа Nim, который позволяет что-то подобное.

  a:int
  b:float
  c:char

const
  PI:float = 3.1416
  MAX:int = 100

Интересно, зачем нужны возвращаемые типы, если вы возвращаетесь, и этот вывод идет в переменную определенного типа, и они не совпадают, тогда он будет бросать.
Я думаю, вы дадите возможность редакторам найти эту ошибку за вас до того, как вы запустите свой код.

Тип возвращаемого значения void для меня не имеет особого смысла, я полагаю, что в этом случае было бы проще просто опустить определение типа возвращаемого значения.

Наконец, -> кажется мне странным, если бы у нас были типы возврата, но я понимаю, что вы могли бы не сделать что-то вроде нима func somefunc(): String: (может быть?), Но я не поклонник этой стрелки, как синтаксис, поскольку он также мало что говорит мне, кажется направленным, но не имеет к этому никакого отношения, напоминает мне о побитовых операциях >> и << но те в каком-то смысле направлены.

__Не хотел закрывать вопрос __

Я вижу, что возвращаемые типы, как известно, в некоторых случаях важны, поскольку теперь можно будет читать каждую функцию для узла, его переменные и его возвращаемый тип.

Возможно, сейчас это не имеет большого смысла, но люди смогут писать плагины и даже игровой код, который может быть чрезвычайно динамичным в том, как он работает. IE, плагин может иметь следующее предупреждающее сообщение: «Этому узлу требуется функция с возвращаемым типом x, но узел, на который вы пытаетесь сослаться, не имеет метода с требуемой сигнатурой метода».

Я написал плагин для Unity под названием AI Behavior, который использует довольно много вещей этого типа (через System.Reflection) для удобства пользователя. У меня буквально есть раскрывающееся меню, которое я заполняю доступными именами методов и позволяю им выбирать, какой метод они хотят использовать в качестве обратного вызова на основе всех методов с правильной подписью, а затем, когда игра запускается, этот метод вызывается под обстоятельства, для которых это сделано. Если для объекта / сценария, к которому они пытаются получить доступ, отсутствует метод, он покажет образец кода в инспекторе, чтобы они могли просто создать сценарий, скопировав / вставив код и переименовав класс. :)

Итак, я считаю, что возможность иметь как можно больше информации о сценариях сделает Godot гораздо более расширяемой для разработчиков плагинов.

Я также считаю, что синтаксис стиля TypeScript (с использованием var name: String = "Godot" ) предпочтительнее, поскольку он стал чем-то вроде стандартного синтаксиса для дополнительной типизации, добавленной к динамическим языкам.

Кроме того, хорошей идеей будет следующий синтаксис Python:
https://www.python.org/dev/peps/pep-0484/
https://docs.python.org/3/library/typing.html

Это поможет людям, переходящим на GDScript с аналогичных динамических языков с необязательной типизацией.

Кстати, когда / в каком выпуске мы можем ожидать, что это будет реализовано? Я видел 3.1 упомянутые?

Это должно быть доступно в Godot 3.1.

Что касается аргументов против того, чтобы префикс стиля C был более сложным или менее понятным / читаемым, и было бы более приятно использовать синтаксис Python или аналогичный - разве все документы не используют этот префикс стиля C со всеми методами и т. Д.? Мол, просто быстро снять один со страницы камеры set_orthogonal ( float size, float z_near, float z_far ) Сначала я сам не совсем понял это, но мне действительно нужно мгновение, чтобы научиться, я думаю, и после этого в этом есть смысл.

Привет, предложение здесь.

Обоснование:
Я думаю, что нам нужна статическая типизация, но я бы по возможности не заставлял пользователя указывать типы.
Все основные компилируемые языки движутся к статическому выводу типов.
Я знаю, что @reduz на самом деле не нравится вывод типов для исходного кода Godot, и я могу понять это для ясности. Однако я думаю, что этот компромисс может действительно хорошо работать для GDScript, поскольку он не сильно снижает скорость кодирования с большинством преимуществ проверки статического типа.
Не уверен, что реализация сложнее.
Для этого потребуются некоторые дополнительные конструкции в виде алгебраических типов данных.
Вот язык, который использует этот принцип

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

для сохранения обратной совместимости потребуется новое ключевое слово для определения функций и переменных. Или, может быть, мы могли бы иметь какой-нибудь волшебный комментарий для включения / отключения проверки статического типа.

Вот пример того, что может делать такая система типов (для тех, кто немного знаком с рубином)

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

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

выход:

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

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

Мысли?

Я действительно хочу улучшить вывод типа, особенно для завершения кода, но также для проверки несуществующих свойств / методов и недопустимого количества аргументов во время редактирования. Я думаю, что эта часть второго шага после факультативного набора текста сделана, так что пока я не беспокоюсь по этому поводу.

Пример из @ m4nu3lf заходит довольно далеко, он фактически выводит результирующий тип на основе ветвления. Я не могу понять, как это сделать без моделирования выполнения кода, где это может стать довольно сложным. Я не уверен, насколько далеко @reduz хочет зайти с GDScript.

Учтите, что Godot имеет довольно строгую систему типов, но все переменные являются Variant (т.е. они могут хранить любой тип). Поскольку это не означает нарушения совместимости, они должны оставаться такими. Таким образом, выполнение var value = 42 не будет автоматически предполагать, что переменная является целым числом, особенно для переменных уровня класса, которые могут быть установлены по желанию внешними скриптами. Это сильно мешает тому, что может делать вывод типа (хотя его можно использовать для завершения).

Одна из возможностей, как уже упоминалось, - сделать это необязательным. Возможно, это настройка проекта, поскольку я не вижу особой ценности включения / выключения для каждого сценария. Тем не менее, это отдельное предложение, о котором нужно подумать на следующем этапе.

@vnen Теоретически, используя новое ключевое слово для параметров / возвращаемых типов / переменных, таких как 'auto' или 'let', и используя тот же алгоритм, что и crystal-lang, вы могли бы вывести тип во время компиляции .
Это статическая типизация с выводом полного типа.
Допустим, у вас есть
auto my_field = 42 в сценарии вы можете просто вывести тип как int и ограничить базовый вариант только целыми числами.

Однако этот язык выходит за рамки простых переменных. Он может ограничить тип подмножеством типов в зависимости от ветвей.
как и в примере выше, возвращаемый тип может быть только String ИЛИ Bool.
Но для этого вы должны

  • Создавайте экземпляры функций для каждой новой комбинации типов аргументов и выведите внутренние типы для каждого возвращаемого выражения, сохраните список возможных возвращаемых типов как тип возвращаемого значения.
  • Для ветвей экспортируйте все возможные ветки, которым присвоена переменная, чтобы вывести все возможные типы переменной в данный момент. В любой момент ограничьте тип варианта теми

Кроме того, вы должны проверить условия "instanceof" и подобные в

if my_var instanceof MyClass:
...

тогда вы знаете, что в теле условия if my_var может быть только MyClass, поэтому тип ограничен еще больше.
Чтобы это имело какое-либо преимущество, вы должны иметь возможность вызывать методы или получать доступ только к полям с общими именами для всех возможных типов.
Итак, скажем

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

Затем после этого x является либо MyClass, либо String. вы сможете вызывать только те методы, которые являются общими для обоих, чтобы избежать ошибок времени выполнения.

Я считаю, что это намного больше того, о чем я думал изначально.
Итак, я хочу вернуться к этому. Может быть, это будет реализовано в будущем, или, может быть, я могу надеяться, что кто-нибудь напишет привязку для crystal-lang.
В любом случае, я считаю, что это действительно крутая функция.

@ m4nu3lf Я понимаю, что ваш запрос, TypeScript очень похож (это язык, с которым я более знаком).

Однако есть несколько проблем, которые следует учитывать при использовании GDScript:

  • Во-первых, реализовать это нетривиально. Я могу посмотреть, как это происходит в других языках, но все равно это займет много времени. Вот почему я хочу делать шаг за шагом и думать об этом позже. TypeScript был создан тем же парнем, который создал Turbo Pascal и C #, так что у меня много опыта, которого у меня нет.
  • GDScript компилируется по одному файлу за раз. И Crystal, и TypeScript требуют, чтобы вы скомпилировали весь проект перед его использованием. Это означает, что проверять взаимодействие с другими скриптами и синглтонами немного туманно.
  • Дерево сцены. С get_node() (который часто используется) просто невозможно узнать, какой тип узла вы получите. Вы знаете только, что это Node но он может быть любого подтипа. То же самое относится к ресурсам, когда вы вызываете load() , особенно если вы вызываете его с динамическим именем. _input(event) имеет ту же проблему, поскольку event может быть любым подтипом InputEvent.
if my_var instanceof MyClass:
    ...

Это уже предполагается для завершения кода. Можно повторно использовать для фактической проверки типов. В частности, этот случай тривиален, но может оказаться весьма сложным. Например, если вы добавите второе условие: if some value == 42 and my_var is Sprite , автодополнение больше не будет работать.


Я также хочу услышать отзывы пользователей, поэтому иметь что-то доступное в ближайшее время важнее (для меня), чем иметь все идеально после долгого ожидания.

Подводя итог: я действительно хочу улучшить вывод типов, но это нужно делать шаг за шагом, с небольшими улучшениями. Как только станет доступен дополнительный набор текста, остальное будет в гору.

Почему это закрыто?

Немного неправильно прочитал, мой плохой.

Вернемся к префиксу и постфиксу: UML и Kotlin используют ": type".
Basic также сделал "DIM a as INT" (и использовал "as" вместо ":" или "is")

@programaths Это было давно, и это обсуждалось снова и снова. Более того, есть PR, реализующий постфикс, так что сейчас об этом немного не может быть и речи: smiley:

Я должен закрыть это?

@ LeonardMeagher2 он закроется автоматически, когда мой PR будет объединен.

@ bojidar-bg Я просто хотел добавить несколько мест, где используются постфиксы, потому что это интересно само по себе. UML предназначен для аналитиков и нетехнических специалистов. Kotlin - это язык, который был разработан, чтобы быть более удобным и современным, чем Java, но при этом реализовывать «эффективную Java».

Итак, если вам нужно записать, почему был выбран постфикс, их тоже можно включить. Иногда люди хотят иметь причины и легко отклоняют технические, если вы не связали их с пользователем.
(например, "Parser может разрешать неоднозначности раньше и имеет скорость в 100 раз, что означает, что ту же программу можно скомпилировать быстрее и позволяет проводить больше тестов)"

Всегда хорошо иметь материал, не правда ли? ;-)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги