неділю, 16 червня 2013 р.

Erlang: Dynamic vs Static

Имел как-то я неосторожность обмолвится, что Erlang — не совсем динамически типизирован, даже немножко статически.

Но давайте смотреть.

Разница между динамической типизацией и статической предельно проста: в динамически типизированном языке все объекты имеют один единственный тип; в статически типизированном типов может быть много.

Типы Erlang'а

Ну что ж, в Erlang действительно есть один тип, называется терм. Термом может быть число, атом, список, кортеж, идентификатор процесса и т.д. Haskell любезно согласился одолжить свой синтаксис и семантику для визуализации этого:

data Term = Num Float
          | Atom String
          | Tuple [Term]
          | List [Term]
          | Fun ([Term] -> Term)
          ...

Функции

В Erlang возможно задавать арность функций и проверять это на этапе компиляции. Рассмотрим две функции:

sqr(X) -> X * X.
hypot(X, Y) -> math:sqrt(sqr(X) + sqr(Y)).

А вот какие они имеют типы:

sqr :: Term -> Term
hypot :: Term -> Term -> Term

Думаю очевидно, что они разные. Получается в Erlang не один тип, а целое семейство, задаваемое рекуррентной формулой:

Type = Term | Term -> Type

variable 'X' is unbound

Многие видели это сообщение, прерывающее компиляцию. Значит это, что переменная не определена (кэп). Но откуда компилятор знает об этом? Все просто — проверка области видимости — компилятор о каждом имени точно скажет где оно определено (bound) и места где оно используется.

То же самое (проверка области видимости) происходит и с функциями компилируемого модуля, и с явно импортированными функциями — компилятор для каждой функции ищет место ее объявления и места ее использования. Более того в местах использования происходит проверка арности, что, как мы видели ранее, есть не что иное как проверка типа:

% imported function declaration
% sqrt :: Term -> Term
-import(math, [sqrt/1]).

% module function declaration (and definition)
% sqr :: Term -> Term
sqr(X) -> X * X.

% references to declared functions
% and arity (type) checking
weird_id(X) -> sqr(sqrt(X)).

Scope checking

  • проверка области видимости является неотъемлемой частью статической типизации;
  • далеко не все динамически типизируемые языки используют проверку области видимости во время компиляции, а только в runtime;
  • компилятор Erlang проверяет на этапе компиляции область видимости локальных переменных;
  • компилятор Erlang проверяет на этапе компиляции область видимости для явно импортируемых функций и функций компилируемого модуля;

Erlang не удовлетворят определению динамически типизированного языка, поскольку в нем не единственный тип. Он статически типизирован? Нет!

Erlang — язык с проверкой области видимости.

Вывод

Между статической и динамической типизациями есть еще целый мир.

22 коментарі:

  1. > далеко не все динамически типизируемые языки используют проверку области видимости во время компиляции, а только в runtime;

    Угу, на кладбище языков можно много чего накопать.

    > Erlang не удовлетворят определению динамически типизированного языка, поскольку в нем не единственный тип.

    > Ну что ж, в Erlang действительно есть один тип, называется терм.

    Взаимоисключающие параграфы — такие взаимоисключающие.

    ВідповістиВидалити
    Відповіді
    1. Кэп? Ну это же специально было сделано, разве это не понятно???

      Видалити
    2. А кстати какие это по вашему динамически типизируемые языки используют scope checking?

      Видалити
    3. > Ну это же специально было сделано, разве это не понятно???

      Специально написаны два противоречащих друг другу утверждения? Охотно верю. Менее противоречащими они от этого не становятся.

      > А кстати какие это по вашему динамически типизируемые языки используют scope checking?

      По-моему, сейчас уже все, если не считать парочки древних лиспов.

      Видалити
    4. Python, Lua, Ruby, PHP, JavaScript, Clojure, Scheme - на вскидку - проверка области видимости только в runtime.

      Видалити
    5. Так, въехал, я неправильно читаю. Сорри.

      Но теперь связь между scope checking и типизацией совсем непонятна. Scope checking - атрибут компилируемого языка, а не статически типизированного.

      И что там с противоречиями в статье?

      Видалити
    6. Scope checking - это атрибут любого языка, просто в Python это происходит в рантайме, а в Haskell на этапе компиляции.
      А противоречие придумал не я, это продукт общества. Есть аксиомы. Первая, что в динамически типизируемом языке только один тип. Другая, что Erlang - динамически типизируемый. Но в посте я показываю, что в нем больше одного типа. Противоречие.

      Видалити
    7. > Но в посте я показываю, что в нем больше одного типа.

      Нет.

      Видалити
    8. Вы уже признали, что неправильно читаете. Может стоит проверить нет ли рецидива?

      Видалити
    9. Может. Читаю "Думаю очевидно, что они разные." Вижу типы Хаскеля. То, что Хаскель статически типизирован мне доказывать не надо. Про эрланг соответствующего доказательства не нашёл.

      Видалити
  2. > в динамически типизированном языке все объекты имеют один единственный тип

    Лолчто?! на примере питончика:

    a = 2
    b = 3
    a/b # 0
    a = 2.0
    a/b # 0.6(6)

    ВідповістиВидалити
    Відповіді
    1. Ну да, 2.0 и 2 - разные значения одного и того же типа. Что не нравится?

      Видалити
    2. То есть ви таки хотите утверждать, что у нас переменные одного и того же типа у нас имеют разное поведение в зависимости от значения переменной? Это конечно, новое слово в CS, и IT вцелом. Давайте тогда разберемся, что из себя представляет "тип" переменной, чем он характеризуется и чем типа друг от друга отличаются.

      Видалити
    3. > у нас переменные одного и того же типа у нас имеют разное поведение в зависимости от значения переменной?

      Гм. Сами поняли, что сказали?

      Я вам секрет открою. Разные значения ВСЕГДА имеют разное поведение.

      > что из себя представляет "тип" переменной, чем он характеризуется и чем типа друг от друга отличаются.

      Тип переменной - класс эквивалентности по отношению "одну переменную можно подставить вместо другой, сохранив при этом валидность программы".

      Видалити
    4. Окей, пусть есть некая сферическая функция f, принимающая в качестве аргумента значение типа . Далее, мы имеем 2 переменные:

      a =
      b =

      Означает ли это, что мы можем безболезненно заменить вызов f(a) на f(b)?


      Видалити
    5. уродский парсер

      a = [некое-значение-integer]
      b = [другое-значение-integer]

      также допустим, что сам результат вычисления нам монопенисуален, важно только его наличие.

      print f(a), f(b)

      Видалити
    6. Тип задает интерфейс или протокол. А класс задает поведение. В питоне, к сожалению, чтобы узнать класс нужно воспользоваться функцией type, но на самом деле, она все равно (почти всегда) возвращает __class__.
      В Питоне все объекты имеют одинаковый интерфейс: у каждого можно спросить его класс, спросить какие у него есть атрибуты и т.д.
      Короче, с абсолютно любым объектом в Питоне вы найдете общий язык, потому что вы знаете, какие вопросы ему задавать.
      А вот что он на них ответит - это уже поведение - как реализация в C#.
      То есть когда вы пишете на Питоне класс, вы задаете поведение.
      Если уж очень грубо взять, то каждый объект в Питоне - это словарь. То есть нет ничего кроме словаря. Словарь - единственный тип.

      Видалити
  3. Я хочу акцентировать внимание на фразе "в динамически типизированном языке все объекты имеют один единственный тип". Это ложное утверждение. Черт с ним, с питоном, возьмем GW-BASIC. Есть переменная A = 20, есть B = "FOOBAR". Одно можно поделить на 2, второе - нет. О каком единственном типе речь?

    Интерпретатор по типу значения определяет, что можно, а что нельзя сделать с данным объектом (данным value). Это означает, что рядом с самим значением лежит ссылка (код, номер, ссылка на таблицу операций) на тип (интерфейс работы со значениями данного типа). Т.е. мы имеем своего рода boxed value.

    В динамически типизированном языке реализация скрывает от нас множество типов, лежащих этажом ниже. Поэтому создается иллюзия, что тип якобы один (откуда, кстати, и растут все уродства динамически типизированных языков).

    ВідповістиВидалити
    Відповіді
    1. Это уже второстепенное. В первую очередь у объекта вы можете спросить его ссылку на поддерживаемые интерфейсы - это и есть его тип, который известный на этапе компиляции (IAskable :-D). И все. На этапе компиляции у вас нет доступа к списку поддерживаемых объектом интерфейсов.
      Прошу учесть важный факт: типизация, проверка типов, а значит и сами типы есть только на этапе компиляции. В рантайме все - значения. Может показаться, что и типы тоже есть, но это всего лишь список поддерживаемых интерфейсов, и этот список уже представляется в виде значения.
      Типизация (динамическая или статическая) определяется тем, что известно на этапе компиляции - это наверное мне нужно было подчеркнуть ранее.

      Видалити
    2. в динамически типизированном языке всего один тип. то что вы называете типами на самом деле лучше называть видами. на existentialtype.wordpress.com все это обьясняется на пальцах.

      Видалити
    3. Пойду посмотрю...
      А в какой именно статье?

      Видалити
  4. Жонглирование словами без видимого результата.

    ВідповістиВидалити