Иногда бывают такие моменты истины. Они происходят совершенно неожиданно, например, когда я читаю этот твит:
Дэвид является автором менее известного, но отнюдь не менее интересного языка программирования whiley, языка , в котором встроена большая статическая проверка типов. Одной из наиболее интересных особенностей языка Whiley является чувствительная к потоку типизация (иногда также называемая просто типизацией по типу потока), которая наиболее полезна при использовании вместе с объединенными типами. Пример из руководства по началу работы
function indexOf(string str, char c) => null|int:
function split(string str, char c) => [string]:
var idx = indexOf(str,c)
// idx has type null|int
if idx is int:
// idx now has type int
string below = str[0..idx]
string above = str[idx..]
return [below,above]
else:
// idx now has type null
return [str] // no occurrence
Помните, что другие языки, такие как Ceylon, также знают типизацию , чувствительную к потоку , и даже Java в некоторой степени знает, потому что в Java тоже есть типы объединения!
try {
...
}
catch (SQLException | IOException e) {
if (e instanceof SQLException)
doSomething((SQLException) e);
else
doSomethingElse((IOException) e);
}
Конечно, Java-чувствительная типизация является явной и многословной. Мы можем ожидать, что компилятор Java выведет все типы. Следующее должно также проверить тип и скомпилировать:
try {
...
}
catch (SQLException | IOException e) {
if (e instanceof SQLException)
// e is guaranteed to be of type SQLException
doSomething(e);
else
// e is guaranteed to be of type IOException
doSomethingElse(e);
}
Потоковая типизация или чувствительная к потоку типизация означает, что компилятор может вывести единственно возможный тип из потока управления окружающей программы. Это относительно новая концепция в современных языках, таких как Цейлон , и она делает статическую типизацию чрезвычайно мощной, особенно если язык также поддерживает сложный вывод типов через var или val ключевые слова!
Статическая типизация JavaScript с помощью Flow
Давайте вернемся к твиту Дэвида и посмотрим, что говорится в статье о Flow:
http://sitr.us/2014/11/21/flow-is-the-javascript-type-checker-i-have-been-waiting-for.html
Наличие использования
lengthсnullаргументом сообщает Flow, чтоnullв этой функции должна быть проверка. Эта версия делает проверку типа:function length(x) { if (x) { return x.length; } else { return 0; } } var total = length('Hello') + length(null);Поток способен сделать вывод, что
xне может бытьnullвнутриifтела.
Это довольно хитро. Подобную предстоящую особенность можно наблюдать в Microsoft TypeScript . Но Flow отличается (или утверждает, что он отличается) от TypeScript. Суть Facebook Flow можно увидеть в этом абзаце из официального объявления Flow :
Проверка типов в Flow включена — вам не нужно проверять весь код сразу. Однако в основе дизайна Flow лежит предположение, что большая часть кода JavaScript неявно статически типизирована; даже если типы не могут появиться где-либо в коде, они думают разработчик как способ обосновать правильность кода. Поток автоматически выводит эти типы везде, где это возможно, что означает, что он может находить ошибки типов, не требуя каких-либо изменений в коде вообще. С другой стороны, некоторые JavaScript-коды, особенно фреймворки, интенсивно используют рефлексию, о которой зачастую сложно статически рассуждать. Для такого динамического кода проверка типов была бы слишком неточной, поэтому Flow предоставляет простой способ явно доверять такому коду и двигаться дальше. Этот дизайн подтвержден нашей огромной базой кода JavaScript на Facebook:Большая часть нашего кода относится к неявно статически типизированной категории, где разработчики могут проверять свой код на наличие ошибок типов без необходимости явно аннотировать этот код типами.
Пусть этот тонет в
большая часть кода JavaScript неявно статически типизирована
еще раз
Код JavaScript неявно статически типизирован
Да!
Программисты любят системы типов. Программисты любят формально рассуждать о своих типах данных и накладывают на них узкие ограничения, чтобы убедиться в правильности программы. В этом вся суть статической типизации: меньше ошибок из-за хорошо спроектированных структур данных.
Люди также любят размещать свои структуры данных в хорошо спроектированных формах в базах данных, поэтому SQL так популярен, и базы данных «без схемы» не получат большую долю рынка. Потому что на самом деле это та же история. У вас все еще есть схема в базе данных «без схемы» , она просто не проверяется на тип и, таким образом, оставляет вам все бремя гарантии правильности.
На заметку: очевидно, что некоторые поставщики NoSQL продолжают писать эти нелепые посты в блоге, чтобы отчаянно позиционировать свои продукты, утверждая, что вам на самом деле не нужна никакая схема, но через эту маркетинговую шутку легко увидеть. Истинная потребность в бесхарактерности так же редка, как истинная потребность в динамической типизации. Другими словами, когда вы в последний раз писали программу на Java и вызывали каждый метод через отражение? В точку…
Но есть одна вещь, которой статически типизированные языки не имели в прошлом, и что у динамически типизированных языков были: средства обхода многословия. Потому что, в то время как программисты любят системы типов и проверку типов, программисты не любят печатать (как печатать на клавиатуре).
Многословие убийца. Нестатическая печать
Рассмотрим эволюцию Java:
Java 4
List list = new ArrayList();
list.add("abc");
list.add("xyz");
// Eek. Why do I even need this Iterator?
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
// Gee, I *know* I only have strings. Why cast?
String value = (String) iterator.next();
// [...]
}
Java 5
// Agh, I have to declare the generic type twice!
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");
// Much better, but I have to write String again?
for (String value : list) {
// [...]
}
Java 7
// Better, but I still need to write down two
// times the "same" List type
List<String> list = new ArrayList<>();
list.add("abc");
list.add("xyz");
for (String value : list) {
// [...]
}
Java 8
// We're now getting there, slowly
Stream.of("abc", "xyz").forEach(value -> {
// [...]
});
Кстати, да, вы могли бы использовать Arrays.asList() все время.
Java 8 все еще далека от совершенства, но все становится все лучше и лучше. Тот факт, что мне, наконец, больше не нужно объявлять тип в лямбда-списке аргументов, потому что он может быть выведен компилятором, является чем-то действительно важным для производительности и принятия.
Рассмотрим эквивалент лямбды до Java 8 (если раньше у нас был Streams):
// Yes, it's a Consumer, fine. And yes it takes Strings
Stream.of("abc", "xyz").forEach(new Consumer<String>(){
// And yes, the method is called accept (who cares)
// And yes, it takes Strings (I already say so!?)
@Override
public void accept(String value) {
// [...]
}
});
Теперь, если мы сравниваем версию Java 8 с версией JavaScript:
["abc", "xyz"].forEach(function(value) {
// [...]
});
Мы почти достигли столь же многословия, как и функциональный динамически типизированный язык JavaScript ( я действительно не возражаю против этих пропущенных литералов списков и карт в Java ), с той лишь разницей, что мы (и компилятор) знаем, что value это тип String, И мы знаем, что forEach()метод существует. И мы знаем, что forEach() принимает функцию с одним аргументом.
В конце концов, все сводится к следующему:
Динамически типизированные языки, такие как JavaScript и PHP, стали популярны, главным образом, потому что они «просто запускались». Вам не нужно было изучать весь «тяжелый» синтаксис, который требовался для классических статически типизированных языков (только подумайте об Ada и PL / SQL!). Вы можете просто начать писать свою программу. Программисты « знали », что переменные будут содержать строки, нет необходимости записывать их. И это правда, нет необходимости записывать все!
Рассмотрим Scala (или C #, Ceylon, почти любой современный язык):
val value = "abc"
Что еще это может быть, кроме String?
val list = List("abc", "xyz")
Что еще это может быть, кроме List[String]?
Обратите внимание, что вы все равно можете явно ввести свои переменные, если необходимо — всегда есть такие крайние случаи:
val list : List[String] = List[String]("abc", "xyz")
Но большая часть синтаксиса является «opt-in» и может быть выведена компилятором.
Динамически типизированные языки мертвы
Вывод из всего этого заключается в том, что после удаления синтаксического многословия и трения из языков со статической типизацией использование языка с динамической типизацией абсолютно не дает никаких преимуществ. Компиляторы очень быстрые, развертывание также может быть быстрым, если вы используете правильные инструменты, и выгода от статической проверки типов огромна. ( не верите? прочитайте эту статью )
Например, SQL также является статически типизированным языком, где большая часть трений все еще создается синтаксисом. Тем не менее, многие люди считают, что это динамически типизированный язык, потому что они получают доступ к SQL через JDBC, то есть через конкатенационные строки SQL-операторов без типов. Если бы вы писали PL / SQL, Transact-SQL или встроенный SQL в Java с помощью jOOQ , вы бы не подумали о SQL таким образом, и вы бы сразу оценили тот факт, что ваш PL / SQL, Transact-SQL или Java Компилятор проверит все ваши операторы SQL.
Итак, давайте оставим этот беспорядок, который мы создали, потому что нам лень набирать все типы (каламбур). Удачного набора текста!
И если вы читаете это, члены группы экспертов по языку Java, пожалуйста, добавьте var и val, а также чувствительную к потоку типизацию к языку Java. Мы будем любить тебя всегда за это, обещали!
