Pull to refresh

Comments 11

Многие случаи «странного» кода в этом проекте поясняются тем, что код пишется девелоперами оригинального Tensorflow, и многие методы буквально «переводятся» с Python.
Отсюда заполнения словарей через LINQ — автор кода явно пытался эмулироваться питоновские генераторы, хотя на C# это выглядит крайне неинтуитивно.
Отсюда же и проверка на is Tensor — в аналогичном методе в питоновской реализации return type неизвестен, потому что в коде Tensorflow нет статических тайп хинтов.

Пожалуй, в подобных проектах (где разработчикам приходится писать не на своём «естественном» языке программирования) статический анализ кода особенно важен, потому что помимо контроля качества кода он ещё и помогает разработчику выучить новую для него экосистему.
UFO just landed and posted this here

Это какая-то жесть. Как-будто пригнали питонистов писать на шарпе без подготовки.
Мало того что для словарей есть специальный инструмент LINQ:


var producer_op_dict = producer_op_list.Op.ToDictionary(op => op.Name, op => op);

Вот эта запись просто убила:


var init_from_fn = initial_value.GetType().Name == "Func`1"; // <=

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


var init_from_fn = initial_value?.GetType() is Type t 
     && t.IsGenericType 
     && t.GetGenericTypeDefinition() == typeof(Func<>);

Можно проверить initial_value на is Delegate сначала, чтобы различить ицнициализацию по значению и с помощью функции.

Уточнение. При создании словаря второй аргумент не нужен:


var producer_op_dict = producer_op_list.Op.ToDictionary(op => op.Name);

Я пропустил вот эту строчку


init_from_fn ? (initial_value as Func<Tensor>)():initial_value,

По всей видимости, ожидается либо Func<Tensor>, либо Tensor. В таком случае подошел бы стандартный:


var initialValue = initial_value switch 
{
    Tensor tensorVal => tensorVal,
    Func<Tensor> initializer => initializer(),
    _ => throw new ArgumentException(nameof(initialValue))
};

На самом же деле нужен перегруженный метод, принимающиу Func<Tensor>, вычисляющий его, и вызывающий метод, принимающий просто Tensor, где и просходит вся работа.


Сложно представить сколько еще всего там просто так упаковывается/распаковывается и таскается в object-ах. Расчитывать на какой-то перформанс в .NET-части либы вообще не стоит.


А потом нам говорят что "статическая типизация не нужна", "с динамической типизацией проще, раз-раз и проект готов".

А потом нам говорят что «статическая типизация не нужна», «с динамической типизацией проще, раз-раз и проект готов».
Динамическая типизация — мечта менеджера. Таски закрываются в три раза быстрее, а что их общее количество раз в десять больше — так зато можно вышестоящему менеджменту показывать, что вы делом заняты и красивые графики рисовать…

Не, я не против динамической типизации, сам пишу на R, и это очень удобно для коротких проектов/высокоуровневых задач, когда вам нужно условно CSV.LoadData, TensorFlow.MakeEverythingWork. Но когда пишется большая либа/обертка к performance-critical бэкенду — тут хочется нормальной доменной модели, а не вот этих вот гаданий по строке, какой же тип прилетел к нам в object.

Не, я не против динамической типизации
Извините, но вы как раз против динамической типизации. Более того, подавляющее большинство людей, которые «топят» за динамическую типизацию — хотят вовсе не её.

тут хочется нормальной доменной модели, а не вот этих вот гаданий по строке, какой же тип прилетел к нам в object.
Но ведь в этом — вся суть динамической типизации! Весь её смысл! Когда у вас в одной переменной может быть и число и строка и, я извиняюсь, Жопа-С-Ручкой!

То, чего вы на самом деле хотите — это как раз статическая типизация… только без писанины. Ну вот как в Go:
  s := "Hello, world"
Статическая тут типизация или динамическая? Статическая, конечно. Ничего, кроме строки вы в s уже никогда не положите.

Но объявлять тип перемнной тут не нужно — если его можно вычислить.

Так многие языки умеют. Даже C++ современный.

Просто динамически типизированные языки предоставляют такую возможность всегда… а языках со статической типизацией — есть ньюансы.

Но это — именно причина из-за которой Роб Пайк, внезапно, обнаружил, что люди активно переходят с Python на Go, а с C++… не очень.

Забавно, кстати, что современный Python обзавёлся-таки типами — что и показывает, что даже большинству людей, использующих его хочется-таки статической типизации.

За всех не скажу, но я хочу не "статическую типизацию без писанины", а нечто другое. Я и сейчас могу написать var s = "Hello, world!";, другое дело что в текущем скоупе (области видимости) s может содержать инстанс типа string или наследуемого типа (если таковые имеются), а не, внезапно, (int)42.


Для меня статическая типизация это в первую очередь контракт. Если у меня есть int GetStrLength(string @string), и я его вызову используя переменную s (var len = GetStrLength(s);), я асболютно уверен, что внутрь функции я передам либо строку, либо null, а из функции вернется знаковое целое размером 4 байта; или все это упадет с исключением. Более того, не ломая CLR, без грязных трюков это ограничение обойти невозможно (или мне неизвестно как). Именно ради этих контрактов и строятся все эти слоищи абстракций и тратится столько времени по сравнению с разработкой на языке с динамической типизацией.


тут хочется нормальной доменной модели, а не вот этих вот гаданий по строке, какой же тип прилетел к нам в object.

Но ведь в этом — вся суть динамической типизации! Весь её смысл! Когда у вас в одной переменной может быть и число и строка и, я извиняюсь, Жопа-С-Ручкой!


И это снова контракт, который всего лишь означает, что в compile-time допускается вызов этого метода с аргументом любого типа. И такой контракт используется повсеместно, например, в обработке событий. Легко представить типичный обработчик вида void EventFired(object sender, EventArgs e). И если sender у вас может быть любой, то подписаться на событие, где вторым аргументом передается не каноничный EventArgs, а, например, строка — уже не получится. И об этом вы узнаете в момент написания строчки кода в IDE или в момент компиляции — благодаря статической проверке типов. В случае же с динамической, узнаете вы об этом печальном type mismatch в продакшене, когда вам в метод прилетит строка, и вы попытаетесь вызвать на ней метод, доступный только на EventArgs.


Справедливости ради, в C# есть "настоящая" динамическая типизация, и это dynamic, который на самом деле object, но с дополнительными инструментами.

Перегрузка плачет по этому коду…
Предупреждение анализатора: V3051 An excessive type check. The object is already of the 'Tensor' type. array_grad.cs 154

Если он null, то он не Tensor (если это не структура).

Sign up to leave a comment.