Programming Languages
July 22, 2018

Академичность головного мозга на примере Swift, Golang, Rust и Kotlin

UPD: Немного стыдно за этот пост. В течение последних трех лет я сильно поменял свое мнение, это был долгий процесс и за эти годы я получил массу дополнительного опыта. Я перестал считать C-style declaration чем-то более лучшим. Учитывая, что конструкции типа let x: u32 = 5 облегчают жизнь компилятору (парсинг грамматики происходит куда проще) и к ним быстро привыкаешь, я все меньше и меньше стал предпочитать С-подобный синтаксис. Тем более, что современные языки программирования хорошо умеют в вывод типов, а линтер вам и так подсветит выведенный тип. Go, правда, все еще выглядит стремно. Ну и возвращаемый тип функции в конце иногда мешает читаемости еще сильнее, особенно в Rust с кучей Dyn/Box.

Знаете, у меня есть одно интересное правило, которого я придерживаюсь последние 3 года. Выглядит оно так:

Если я хочу посмотреть новый язык программирования, то я начинаю писать на нем эмулятор. Простенький, типо CHIP-8.

Почему? Потому что это лучший "бенчмарк" языка по многим параметрам: I/O, работа с файлами, работа с графическими библиотеками и звуком, типы данных, классы/структуры, циклы, и все-все-все.

Написав простенький эмулятор ты сразу для себя закрываешь огромное число вопросов уровня "а как тут делать что-то?".


Мне часто советуют всякие новые языки, и сам я интересуюсь развитием Rust, к примеру. Я часто гляжу на Go, Swift, Kotlin, Crystal, Nim и другие интересные для меня ЯП, слежу за их развитием и прочим.

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

Вы никогда не задумывались, почему вы пишете именно так, как вы пишете? Почему существуют стили синтаксиса?

Новые языки создаются удобными и лаконичными. Тогда почему в этих новых языках я встречаю убогие и не лаконичные конструкции времен Паскаля? Почему эти языки уже на фундаментальном уровне пытаются выглядеть неудобно?

Объясню на простом примере синтаксиса Golang и других языков.

Присваивание

Go

var i int = 2 //явно

var i = 2 //неявно вне функции

i := 2 //неявно, но только в функциях

Swift (тут вообще полный пиздец)

var myVariable = 42 //обычная переменная

myVariable = 50 //обычная переменная (???)

let myConstant = 42 //константа

let explicitDouble: Double = 70 //константа с явным типом

var myVariable: String = "Hello" //переменная с явным типом

Rust

let x = 5; //константа 

let x: i32 = 5; //константа с явным типом

let mut x = 5 //изменяемая переменная

Kotlin

val c: Int = 5 //явно, неизменяема

val c = 5 //неявно, неизменяема

var c = 5 //неявно, изменяема

Да, Rust безопасный язык, и я не говорю, что mut — это плохо. Плохо излишнее let и тип данных в конце. Зачем? Зачем Golang и var, и int в одной строке? Зачем Swift вообще существует с таким переусложненным объявлением переменных?

Ведь можно сделать это аккуратно и лаконично — и не надо ничего изобретать. Обычное присваивание переменной с указанием типа:

int i = 2 //явно

var i = 2 //неявно

Константа и изменяемое (для Rust)

const i = 2 //неявно
const int i = 2 //явно

mut i = 2 //невяно, изменяемо
mut int i = 2 //явно, изменяемо

Сравните сами два примера и их читаемость

var i: Float = 5.0 // Swift

float i = 5.0 //классический C-style

Да, у константы букв больше, но читаемость намного лучше. Сразу видно и тип данных, и изменяем он или нет.

У Golang, в принципе, все еще не так плохо, но избыточность прослеживается. Зачем она здесь нужна, чем так плох C-style? Почему все современные языки хотят казаться такими элитными и пытаются следовать неудобному академическому стилю?

Что интересно, все выдумывают максимально уебанские конструкции и умудряются соединять в одном языке var и val (Kotlin, привет).

Кто вообще додумался писать тип переменной после ее имени, а вначале еще и var/val/let добавлять?


Зато у Swift можно использовать ЭМОДЖИ

let 🐶🐮 = "dogcow"

Функции

О да, сейчас вы просто охуеете, если еще нет. Без прелюдий сразу в пекло.

Go

func sqrt(x float64) string {
   if x < 0 {
      return sqrt(-x) + "i"
   }
   return fmt.Sprint(math.Sqrt(x))
}

func вначале, после скобок возвращаемое значение. Аргументы в порядке "имя -> тип переменной".

Swift

func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}

-> -> -> смотри сюда и не пропусти возвращаемое значение, ты же такой тупой <- <- <-

Rust

fn add_one(x: i32) -> i32 {
    x + 1
}

Kotlin

fun double(x: Int): Int {
   return 2 * x
}

Заметили, как их уебищный синтаксис похож? Это ключевые слова func, fun, fn, лишь бы ты не перепутал функцию с чем-то еще (когда, блять, такая проблема вообще была?) и возвращаемое значение после скобок, убогие типы данных через двоеточие.

Давайте напишем простую функцию в C-style, который эти замечательные языки послали нахуй ради АКАДЕМИЧНОСТИ.

int double(int x){
  return 2*x
}

Сравним, еще раз, со Swift

func double(x: Int) -> Int {
  return 2*x
}

Лаконично, ничего не скажешь, ебать.

Кстати, вы никогда не задумывались, насколько вам удобно набирать -> каждый раз при написании функции?

Массивы

Напоследок покажу вам ебаный ад

Go

var a [2]string //пустой массив

primes := [6]int{2, 3, 5, 7, 11, 13} //создаем и инициализируем

Swift

var someInts = [Int]() //пустой массив

let oddNumbers = [1, 3, 5, 7, 9, 11, 13, 15] //создаем и инициализируем неявно (статический)

let oddNumbers: [Int] = [1, 3, 5, 7, 9, 11, 13, 15] создаем и инициализируем явно (статический)

var shoppingList: [String] = ["Eggs", "Milk"] //создаем массив и инициализируем два значения через литералы

Rust

let array: [i32; 3] = [0; 3]; //создаем массив и три раза вносим значние 0

Kotlin

val x: IntArray = intArrayOf(1, 2, 3) //эту ебалу я даже комментировать не хочу

Посмотрим, что так не понравилось уникальным АКАДЕМИЧЕСКИМ языкам в классическом C-style

int balance[5]; //просто создаем

int balance[5] = {1000, 2, 3, 70, 50}; //создаем и инициализируем

int balance[] = {1000, 2, 3, 70, 50}; //можно даже так

Сравним с великолепием, которое предлагает Swift (для удобства сравнивайте последнюю строчку верхнего блока с нижней).

let balance: [Int] = [1000, 2, 3, 70, 5] //создаем и инициализируем

Я сначала хотел написать код для статического массива, чтобы точно сравнить с C-style, но я с удивлением для себя обнаружил, что Swift не дает возможности создать массив фиксированного размера. Зато мы всегда можем использовать эмоджи 😂😂😂😂😂😂😂😂😂😂😂

Не понимаю

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

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

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

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

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

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