Comments 2
интересно! пробежался по всем статьям, и только когда вернулся к началу и начал внимательно изучать всё по порядку только на средине второй статьи понял как это можно применять на практике! тоесть с помощью этого механизма можно генерировать свои функции из какогото внешнего источника данных?
интересно будет подумать как сделать такое на C#
а что вам не нравится в partial классах на C#? я про ваш пример с Godot
Да, функции тоже можно. Так работает кодоген по Swagger и т.п. Однако по моему опыту в F# на основе внешнего источника данных чаще генерируются структуры данных, а не функции. Для последних характерно использование рефлексии по существующим библиотекам, то есть источник данных скорее внутренний.
Лет 6-7 назад в C# уже был рослин, которые позволял ворочить AST непосредственно в VS. Я практически не пробовал вытаскивать его наружу, но не думаю, что это вызовит серьёзные сложности. Кроме того в C# есть сорсгены, но они обычно подразумевают более общие задачи без серьёзной кастомизации.
partial
в неумелых руках может превратить проект в кашу, но в случае с Godot разрабы накидали своих фишек.
В Godot всё описывается через ноды, которые могут вкладываться друг в друга без ограничений, за счёт чего получаются очень раскидистые деревья. Это напоминает UI-фреймворки, но в качестве ноды может быть объект, который отвечает только за логику и вообще никак не взаимодействует с UI. У этих нод есть свойства и события, но они полностью не закрывают всех фич, часть из которых доступна только через переопределение методов. Проблема в том, что просто переопределить метод недостаточно, ещё важно сделать это в нужном месте проекта.
Для того, чтобы Godot узнал о override
в типе, этот тип должен лежать в C#-проекте и быть partial
. Если я просто закину анонимную ноду в глубине сцены:
{ new Node() with
override this._Process delta =
if Input.IsKeyPressed Key.Shift then
GD.print "Shift pressed!!!"
}
То Godot никогда не вызовет метод _Process
, потому что будет считать, что он не был изменён. Я пока не понял, почему они сделали именно так, но проблему приходится решать через создание прослойки вида:
public partial class InCSharp : InFSharp
{
public override void _Process(double delta) => base._Process(delta);
}
На каждый тип вида:
type InFSharp () =
override this._Process delta =
()
Получается, что я:
Не могу избавиться от C#-проекта.
Вынужден дублировать все ключевые типы с переопределениями.
Вынужден создавать их через фабрики, так как вместо
InFSharp()
мне надо неявно вызватьInCSharp()
.
Кроме того есть проблемы с экспортируемыми свойстваи и сигналами, которые также надо протаскивать через C#.
Я постепенно закатываю весь этот шум в асфальт и уже явно вышел в плюс, но мне не понятно, зачем этот комплекс проблем вообще был создан. В 3 версии Godot такой проблемы не было.
Большой код. Учимся генерировать F#-исходники с помощью Fantomas. Часть 3. Модули и типы