Ой да ладно. Скажите мне название любого языка программирования, и я скажу почему он говно.
Если программист не может рассказать почему язык программирования на котором он пишет говно, то он не знает этот язык программирования
Для работы ему нужен .NET, а сам он никому не нужен. Нулевое комьюнити. И самое главное нет ни одного кейса, где он был бы более применим чем популярные промышленные языки
Затем что фэ решётка красивый, компактный, а так же "современный" (бОльшинство фич, появляющихся в C# сначала появляются в F#). Да и никто не ебёт голову, F# наоборот менее напряжный по вышеперечисленным причинам
Ну метод напиши. Метод принимает arg: string. На выходе отдаёт IPEndPoint. Ввод либо ip:port, либо ip (в этом случае подставляется дефолтный port, пусть будет 80). Без обработки ошибок и прочего. Написать надо максимально компактно, но при этом читабельно и с соблюдением стандартного форматирования
IPEndPoint ParseIp(string address)
{
var splitted = address.Split(':');
var ip = IPAddress.Parse(splitted[0]);
var port = splitted.Length >= 1 ? Int.Parse(splitted[1]) : 80;
return new IPEndPoint(ip, port);
}
Я прост не оч понимаю что ты хочешь мне показать. Что на F# будет красивее и чище? А толку от этого если за тобой это никто не сможет поддерживать, потому что F# не знает?
Не у всех же такая кваливикация чтобы разные языки понимать.
1) Когда все же надо работать с мутабельными переменными (и массивами), синтаксис неудобный. Например, написать a.[f(x)].[g(y)] += 2 нетривиально.
Да и синтаксис создания массива мне не оч нравится: [| 1; 2; 3 |]
2) У него была какая-та бага, когда используешь индентацию 2 вместо 4.
match e with
| -> aaa
__bbb
В таком случае bbb, кажется, относился к блоку до match. Может пример не такой, но какая-то похожая проблема точно была.
3) Долгий билд. У JetBrains также с ним была проблема, что у F# особая билдовая система, и его не так легко взять и поддержать.
4) if-then-else громоздкий. Обычно это не проблема, но в лямбдочках, если написать в одну строчку, это выглядит неоч, и приходится разбивать.
Может это уже пофиксили, не знаю. А вообще да, классный язык. Pipe-forward я бы добавил в каждый язык. И там были вещи типа ParallelSeq, КОТОРЫЕ НАКОНЕЦ ПОЗВОЛЯЮТ ПРОСТОЕ РАСПАРАЛЛЕЛИВАНИЕ ДЕЛАТЬ ПРОСТО.
1.1) Вполне удобный: var <- value.
1.2) А как вы хотели? [1; 2; 3]? Так это есть у нас, только так объявляются "списки" (которые являются приоритетными при работе с F#)
2) Нипонятно. Лично я использую 2 пробела и не замечал ничего похожего
3) На глаз вроде не отличается от билда C#
4) Что громоздкого то? Имеется ввиду что нету сокращённой формы?
1.1) Как нормально написать на F# пример, который я привел?
1.2) Ну вот как раз в том и проблема, что они считают списки приоритетными. Списками можно пользоваться, когда не пишешь что-то крупное (где-то возмутятся фанаты хаскеля). Когда нужно писать приложение, работающее со сколь-либо большими данными, списки и медленнее (я так полагаю; не проверял), и перестают влезать в память (проверял).
3) У вас был крупный проект на F#? Время билда очень заметно отличается (отличалось, по крайней мере).
4) Да, по сравнению, например, с тернарным оператором это занимает заметно больше места.
1.1) Присвоить переменной значение?
name <- value
// массивы, списки, таблицы
arr.[idx] <- value
1.2) Пользуйтесь массивами - реально не нравятся прямые слэши? Могу предложить костыль: [1;2;3] |> Array.ofList
3) Нет
4) Тернарный оператор кстати имеет свойство запутывать читателя если начать городить дворцы ?:
1.1)
a.[f(x)].[g(y)] += 2
Конечно, предполагайте, что f и g с сайд-эффектами.
4) Ну лол, просто не надо городить их.
1.1 Всё равно не понял
4. Я кстати не могу быть уверенным, используется ли вообще тернарный оператор в не C-подобных языках.
5. Вспомнил дебильность F# - отсутствие try with finally конструкции. Есть try...with и try...finally отдельно друг от друга. Хочешь и то и другое - пиши try try...with finally
1.1) Пусть a : int[][], readInt() читает число из input-a. Как написать на F# a[readInt()][readInt()] += 2? (Я не помню, гарантируется ли порядок вычислений, но предположим, что гарантируется).
5) Ну, я об этом не знал, потому что никогда не пользовался finally:) Все реальные ситуации решались при помощи IDisposable. Даже Java до этого как-то доперла.
• Ручное управление памятью может быть утомительным.
• Обращение со строками является частью ручного управления памятью. См. выше.
• Поддержка параллельного программирования довольно слабая.
• Ужасные названия стандартных функций: isalnum, fprintf, fscanf и т.д.
• Препроцессор.
• Не было сильных ощущений сегодня? Выполните аварийное завершение программы.
• Нехватка достоверной информации при аварийном завершении программы… «Стандартный» набор инструментальных средств GNU не информирует вас о случаях аварийного завершения программы во время стандартного прогона.
• Если программа унаследованная, то через пару дней у вас будет совсем не геройский вид.
• Стандартная библиотека охватывает только основные манипуляции со строками, ввод-вывод файлов, а также распределение памяти. И qsort() по какой-то причине. Вы не получаете ничего такого, что является по-настоящему переносимым.
• Разработка своих собственных контейнеров (вероятно, более низкого качества по сравнению со стандартной, хорошо оптимизированной версией) является тем, что вам лучше привыкнуть делать для каждого нового приложения; или следует использовать чрезвычайно сложные существующие конструкции, управляющие вашим приложением (nspr, apr, glib...).
• Также отсутствует стандарт для сетевого программирования. Большинство платформ использует интерфейс прикладного программирования (API) сокетов Berkeley, но он сильно зависит от поставщика. В частности, API от Microsoft имеет много необычных особенностей. Хотя можно пожелать удачи в поиске переносимого O (1) API выбора асинхронного сокета; нет ни одного (select() и poll() имеют характеристику O(n)).
• Автоматическая инициализация переменных в нуль не происходит, хотя они являются статическими, поскольку «это более эффективно».
• Есть желание использовать С99 в переносимом виде? Конечно; но если вам потребуется использовать MSVC, более старый чем 2015 (вполне вероятно у некоторых разработчиков), то ничего не получится.
• Есть желание использовать С11 в переносимом виде? Конечно, но в значительной степени никто, кроме GCC и случайных троллей, не поддерживает этого. Извините, пользователи MSVC.
• Массивы переменной длины, как правило, помещаются в стек, а это означает, что передача при слишком большом размере небезопасна: может произойти переполнение стека в тот момент, когда вы используете его. Нет никакого способа узнать, что ваше назначение является слишком большим, и это усугубляет опасность.
• malloc() берёт один параметр размера (размер в байтах), но calloc() — два (количество элементов и размер элемента в байтах).
• «Undefined behaviour» («Неопределённое поведение») — основной лейтмотив в С. Под «неопредёлённым» разработчики здесь понимают «Добро пожаловать делать всё, что желает поставщик, без необходимости учитывать, чего желаете вы». Самое плохое в этом то, что невозможно надёжно найти все случаи «неопределённого поведения» в какой-нибудь большой программе.
• int a = 0; f(a, ++a, ++a); не гарантировано, что будет f(0, 1, 2). Порядок вычисления не определён.
• Еще один сюрприз, который «впечатляет»: i[++a] = j[a];, поскольку не определено, какая сторона рассчитывается сначала.
• Знаковое переполнение является технически неопределённым поведением. Это происходит на большинстве платформ.
• Сдвиг на более чем N битов на типе intN_t является неопределённым поведением.
• Преобразование типа int * во float * с последующим разыменованием является неопределённым поведением. Необходимо использовать memcpy().
• Разыменование указателя NULL является неопределённым поведением. Здесь нет подвоха — можно получить действительную память из этого!
• Преобразование типов между указателями функции и указателями данных является технически неопределённым поведением; на некоторых платформах они имеют разные размеры (почему это должно заботить программиста в C, оставим как упражнение для читателя).
• Преобразование типа void (*)() в int (*)(int, const char *), действительно, должно быть неопределённым? Нет! Все указатели функции могут быть преобразованы друг в друга. Хотя фактически вызов функции после преобразования, если тип неправильный, является неопределённым поведением (как можно было бы с полным правом и ожидать этого).
• Преобразование чего-нибудь вроде, скажем, FILE * в double * и снова обратно не является неопределённым, поскольку в промежутке не производится разыменование. Все указатели данных являются эквивалентными.
• Отладка оптимизированного кода может иногда не иметь смысла из-за агрессивной оптимизации компилятора.
• Безопасность данных в многопоточных программах, в действительности, не гарантируется языком; то, что, как вы полагаете, является элементарным, не обязательно является таковым без использования (C11, но см. выше) элементов. Получить нарушения при чтении и записи чрезвычайно просто, и никакие инструменты никогда не предупредят вас, что это происходит.
• Указатели на объект, тип которого неизвестен, создают проблемы; нет никакого способа определить, что на самом деле стоит за ними без какой-то определённой пользователем схемы маркировки, которая по определению не может содержать все типы. Решением является, несомненно, — «будьте внимательными».
• Система типов в общем случае является бесполезной, поскольку можно преобразовать (почти) всё во всё.
go - нет дженериков, уебанская работа с зависимостями, GOPATH, кодстайл захадкожен в компилятор, комьюнити состоит из даунов
c# - требует .NET, оверхед в виде виртуалки, поддержка non-windows платформ с болью
java - jvm-based, а значит тормозное говно. кривые дженерики(нельзя делать new T, не поддерживают примитивные типы), уебанское разделение на примитивные типы и объекты, не умеет в объеты на стеке
Это так есть. Язык получился говном, но комьюнити активно с него слюнами течёт ПОТОМУШТА ГУГЛЬ. По факту в go нет никаких плюсов кроме годной многопоточности (во многих других языках многопоточность не хуже), а от минусов многих других языков он не избавился
Ну так суть не в умении копипасты. Люди уверены, что зная только 1 инструмент - этот инструмент хорош для всего. Мое мнение - для каждой задачи хорош именно свой инструмент. И знать и уметь пользоваться 5-6 языками программирования - это не достижение, а всего лишь необходимый минимум.
Чем вам названия функций не угодили? isalnum - is alphanumeric, fprintf - file print formatted (data), fscanf - file scan formatted (data). Всё логично и понятно.
С остальным более или менее согласен, насколько позволяет мой опыт.
Ну адепты языка Ruby и некоторых других языков точно этого не одобрят, для них названия должны говорить сами за себя, без запоминания и походов в документации. То есть вместо сокращений, можно писать полное название: IsAlphanumeric( ); FilePrintFormatted( ); FileScanFormatted( );. А разрешение длинных имен должна заниматься IDE, чтобы не утомлять вбиванием этих длинных имен. В таком случае ваша личная память не засоряется запоминанием сокращений и происходит самодокументирование функций. Спорно, но такие аргументы встречаются.
И так плавно люди наконец-то поймут, что каждый язык хорош в своей области, и говорить "говно" из-за ручного управления памятью не стоит.
Не пытайтесь объяснить людям, что молоток не хуже дрели, просто задачи разные. Ну и время создания учитывать надо.
IT-юмор
5.7K постов52.5K подписчиков
Правила сообщества
Не публикуем посты:
1) с большим количеством мата
2) с просьбами о помощи
3) не относящиеся к IT-юмору