Иногда бывают такие моменты истины. Они происходят совершенно неожиданно, например, когда я читаю этот твит:
Дэвид является автором менее известного, но отнюдь не менее интересного языка программирования whiley, языка , в котором встроена большая статическая проверка типов. Одной из наиболее интересных особенностей языка Whiley является чувствительная к потоку типизация (иногда также называемая просто типизацией по типу потока), которая наиболее полезна при использовании вместе с объединенными типами. Пример из руководства по началу работы
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
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 тоже есть типы объединения!
|
1
2
3
4
5
6
7
8
9
|
try { ...}catch (SQLException | IOException e) { if (e instanceof SQLException) doSomething((SQLException) e); else doSomethingElse((IOException) e);} |
Конечно, Java-чувствительная типизация является явной и многословной. Мы можем ожидать, что компилятор Java выведет все типы. Следующее должно также проверить тип и скомпилировать:
|
01
02
03
04
05
06
07
08
09
10
11
|
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, что в этой функции должна быть проверка на ноль. Эта версия делает проверку типа:
123456789function length(x) {if(x) {returnx.length;}else{return0;}}var total = length('Hello') + length(null);Flow может сделать вывод, что
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
|
01
02
03
04
05
06
07
08
09
10
11
12
|
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
|
1
2
3
4
5
6
7
8
9
|
// 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
|
1
2
3
4
5
6
7
8
9
|
// Better, but I still need to write down two// times the "same" List typeList<String> list = new ArrayList<>();list.add("abc");list.add("xyz");for (String value : list) { // [...]} |
Java 8
|
1
2
3
4
|
// We're now getting there, slowlyStream.of("abc", "xyz").forEach(value -> { // [...]}); |
Кстати, да, вы могли бы использовать Arrays.asList() все время.
Java 8 все еще далека от совершенства, но все становится все лучше и лучше. Тот факт, что мне, наконец, больше не нужно объявлять тип в лямбда-списке аргументов, поскольку он может быть выведен компилятором, является чем-то действительно важным для производительности и принятия.
Рассмотрим эквивалент лямбды до Java 8 (если раньше у нас был Streams):
|
1
2
3
4
5
6
7
8
9
|
// Yes, it's a Consumer, fine. And yes it takes StringsStream.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:
|
1
2
3
|
["abc", "xyz"].forEach(function(value) { // [...]}); |
Мы почти достигли такой степени многословия, как функциональный язык с динамической типизацией, которым является JavaScript ( я действительно не возражаю против этих пропущенных литералов списков и карт в Java ), с той лишь разницей, что мы (и компилятор) знаем, что value имеет введите String . И мы знаем, что метод forEach() существует. И мы знаем, что forEach() принимает функцию с одним аргументом.
В конце концов, все сводится к следующему:
Динамически типизированные языки, такие как JavaScript и PHP, стали популярны, главным образом, потому что они «просто запускались». Вам не нужно было изучать весь «тяжелый» синтаксис, который требовался для классических статически типизированных языков (просто подумайте об Ada и PL / SQL!). Вы можете просто начать писать свою программу. Программисты « знали », что переменные будут содержать строки, нет необходимости записывать их. И это правда, нет необходимости записывать все!
Рассмотрим Scala (или C #, Ceylon, почти любой современный язык):
|
1
|
val value = "abc" |
Что еще это может быть, кроме String ?
|
1
|
val list = List("abc", "xyz") |
Что еще это может быть, кроме List[String] ?
Обратите внимание, что вы все равно можете явно ввести свои переменные, если необходимо — всегда есть такие крайние случаи:
|
1
|
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. Мы будем любить тебя всегда за это, обещали!
| Ссылка: | Неудобная правда о динамической и статической типизации от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |

