Иногда бывают такие моменты истины. Они происходят совершенно неожиданно, например, когда я читаю этот твит:
Дэвид является автором менее известного, но отнюдь не менее интересного языка программирования 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) {
return
x.length;
}
else
{
return
0
;
}
}
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 type List<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, slowly Stream.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 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:
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 . |