Что такое "связывание переменных"?
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 29.11.21 09:03
Оценка: :))

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

https://habr.com/ru/post/481782/

Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?

«A binding is the association of a variable name with the variable entity, for example " x refers to the variable declared with class x "»
https://stackoverflow.com/questions/50898687/is-a-variable-declaration-the-same-as-a-variables-binding

Ещё мне непонятно, зачем нужно ключевое слово var в C#. Да, исторические причины, привычки, то-сё.
Но если проектировать синтаксис языка начисто, то:
во-первых, тип переменных заранее объявлять не нужно (он выводится);
во-вторых, тип переменной можно сделать зависимым от контекста,
для того чтобы можно было в двух последовательных строчках написать:
data=1; // целое
data="lala"; // строка

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

Рантайм не будет хранить тип переменной, потому что об этом будет заботится компилятор.

Что со мной не так?
типизация компиляция оверхед
Re: Что такое "связывание переменных"?
От: hi_octane Беларусь  
Дата: 29.11.21 09:21
Оценка: +1
ЭФ>Ещё мне непонятно, зачем нужно ключевое слово var в C#.
Без var:

VeryImportantData=1;

а потом где-то в дебрях if-ов:

VeryInportantData=2; //Happy Debugging
Re: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.11.21 09:32
Оценка: 2 (1) +2
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>

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

ЭФ>https://habr.com/ru/post/481782/

ЭФ>Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?

Нет. Связывание — это процесс, который связывает имя объекта с самим объектом.
Чаще всего этот термин употребляют в контексте поиска метода — к примеру, Function(x) может ссылаться (в зависимости от языка и контекста) на десяток разных методов с именем Function.
Если мы говорим про переменные, то каждая переменная — это, в конце концов, некая "ячейка памяти", которая однозначно определяется её адресом.
Соответственно, связывание переменных — это сопоставление имени некоторого адреса.
Практически в любом языке программирования одно и то же имя может сопоставляться с разными адресами, даже в один и тот же момент времени:
int main() {
    static int x = 5;
    std::cout << x << endl; // это - x №1
    {
        static int x = 6;
        std::cout << x << endl; // это - x №2
    }
    std::cout << x << endl; // а это - снова x №1
}


public int CountTreeLeaves<T>(T node)
  where T: IEnumerable<T>
{
   var c = 0;
   foreach(var child in node)
      c+= CountTreeLeaves(child); // когда мы обходим глубокое дерево, у нас одновременно существуют несколько экземпляров переменной с именем "c" - по одной на уровень вложенности вызова
   return c;
}


Но в нормальных, некурящих языках, это сопоставление делается статически — то есть при компиляции программы. В первом примере у нас получилось два разных x с двумя разными адресами. Но вопрос о том, куда обращаться при выводе x, решается один раз, при компиляции, а при исполнении уже нет никакой свободы выбора. Иногда, как в первом примере, связывание получается абсолютным — т.е. адрес ячейки однозначно известен; даже если мы вызовем main() рекурсивно, оба x будут ссылаться на те же адреса, что и во внешнем вызове. Иногда, как во втором примере (и вообще в большинстве случаев) адреса указываются относительно текущего фрейма стека (что и позволяет с иметь одновременно множество не мешающих друг другу значений).
Те места, где связывание делается динамически, в таких языках обозначаются специальным образом (и, как правило, это относится исключительно к связыванию методов, а не переменных).

А в языках типа JS и питона связывание переменных выполняется динамически. То есть адрес ячейки памяти, к которой нужно обратиться при вычислении выражения с участием переменной, определяется в момент исполнения программы; заранее этого сделать нельзя из-за правил связывания, описанных в языке.

ЭФ>Ещё мне непонятно, зачем нужно ключевое слово var в C#. Да, исторические причины, привычки, то-сё.

ЭФ>Но если проектировать синтаксис языка начисто, то:
ЭФ>во-первых, тип переменных заранее объявлять не нужно (он выводится);
ЭФ>во-вторых, тип переменной можно сделать зависимым от контекста,
ЭФ>для того чтобы можно было в двух последовательных строчках написать:
ЭФ>data=1; // целое
ЭФ>data="lala"; // строка
И какова будет семантика этого обращения?
Что должно быть выведено, если третьей строкой мы напишем System.Console.WriteLine(data)?

ЭФ>Язык при этом останется типизированным, потому что

ЭФ>компилятор будут генерировать разный код в зависимости от типа переменной в месте использования.
Это как раз понятно. Непонятно, как определить тип переменной "в месте использования".
ЭФ>Рантайм не будет хранить тип переменной, потому что об этом будет заботится компилятор.
Это и есть статическое связывание, которое уже и так используется в C#.
ЭФ>Что со мной не так?
Нехватка образования. Ничего страшного — это поправимо
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Что такое "связывание переменных"?
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 29.11.21 09:46
Оценка:
S> заранее этого сделать нельзя из-за правил связывания, описанных в языке.
S> Нехватка образования.

А что у питона не так с правилами (какие именно правила есть в питоне)?
Почему в питоне заранее этого сделать нельзя?
Помог бы пример. Но на самом деле мне это неинтересно.

S> Непонятно, как определить тип переменной "в месте использования".


В соответствии с ДРУГИМИ, разумными правилами языка.

S> какова будет семантика этого обращения?


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

Т.е. в данном случае напечатает "lala", а значение 1 будет потеряно (освобождено).
Отредактировано 29.11.2021 9:47 Эйнсток Файр . Предыдущая версия .
Re[3]: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.11.21 10:11
Оценка: 6 (1)
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>Помог бы пример. Но на самом деле мне это неинтересно.


S>> какова будет семантика этого обращения?

ЭФ>Непонятно, почему это важно.

Потому, что это и есть семантика языка. Мелкие решения вроде этих приведут к достаточно существенным последствиям в удобстве использования языка и отладки программ на нём.
ЭФ>Т.е. в одном блоке выигрывает последнее присвоение,
ЭФ>в разных блоках — по другим правилам.
ЭФ>Т.е. в данном случае напечатает "lala", а значение 1 будет потеряно (освобождено).
Ну, ок. Давайте так:
data=1; // целое
if((new Random()).Next(1) == 0)
  data="lala"; // строка
Console.WriteLine(data);

Что выведет программма? Какого типа будет data в строке 4?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Что такое "связывание переменных"?
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 29.11.21 10:52
Оценка:
S>
S>data=1; // целое
S>if((new Random()).Next(1) == 0)
S>  data="lala"; // строка
S>Console.WriteLine(data);
S>

S>Что выведет программма? Какого типа будет data в строке 4?

ЭФ>>> Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?

S>>Нет.

А как тогда питон это делает?
Re: Что такое "связывание переменных"?
От: Pzz Россия https://github.com/alexpevzner
Дата: 29.11.21 13:03
Оценка: +1
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?


В питоне — да.

ЭФ>«A binding is the association of a variable name with the variable entity, for example " x refers to the variable declared with class x "»

ЭФ>https://stackoverflow.com/questions/50898687/is-a-variable-declaration-the-same-as-a-variables-binding

ЭФ>Ещё мне непонятно, зачем нужно ключевое слово var в C#. Да, исторические причины, привычки, то-сё.


Чтобы объявление новой переменной глазами отличалось от присваивания значения старой.

ЭФ>Но если проектировать синтаксис языка начисто, то:

ЭФ>во-первых, тип переменных заранее объявлять не нужно (он выводится);

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

В Go:
var i int  // Объявление переменной без инициализации
j := 5     // Объявление с автовыводом типа и иницализацией
k = 5      // Присваивание нового значения уже имеющейся переменной


ЭФ>Язык при этом останется типизированным, потому что

ЭФ>компилятор будут генерировать разный код в зависимости от типа переменной в месте использования.

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

А явное объявление переменных нужно, чтобы если компилятор видит x = 5, а про x ничего раньше не слышал, чтобы он не гадал, новая ли это переменная, или просто я ошибся в названии старой, а сразу мое внимание обратил на такую странность в коде, выдав соответствующую ошибку.

Это спасает от очень большого количества глупых ошибок.
Re[5]: Что такое "связывание переменных"?
От: Pzz Россия https://github.com/alexpevzner
Дата: 29.11.21 13:05
Оценка: +2
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>А как тогда питон это делает?


А в питоне data может оказаться и целым, и строкой, как повезет. За это я питон и ненавижу.
Re[5]: Что такое "связывание переменных"?
От: Sharov Россия  
Дата: 29.11.21 13:12
Оценка:
Здравствуйте, Эйнсток Файр, Вы писали:

S>>
S>>data=1; // целое
S>>if((new Random()).Next(1) == 0)
S>>  data="lala"; // строка
S>>Console.WriteLine(data);
S>>

S>>Что выведет программма? Какого типа будет data в строке 4?
ЭФ>>>> Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?
S>>>Нет.
ЭФ>А как тогда питон это делает?

Динамически(во время выполнения программы), т.е. в зависимости от того по какой ветки исполнения пошли, такой тип и будет у data.
Кодом людям нужно помогать!
Re[4]: Что такое "связывание переменных"?
От: vaa  
Дата: 29.11.21 14:19
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Что выведет программма? Какого типа будет data в строке 4?

F# shadowing
let test () =
    let data = 1
    let printData () = printfn "%A" data
    printData ()
    let data = "lala"
    printData ()

> FSI_0007.T1.test();;
1
1
val it: unit = ()


локальная функция захватила первое значение.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[5]: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.11.21 14:48
Оценка:
Здравствуйте, Эйнсток Файр, Вы писали:
ЭФ>>>> Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?
S>>>Нет.
ЭФ>А как тогда питон это делает?
Во-первых, есть много разных питонов. Теоретически, ничто не мешает питону хранить разные типы в разных местах памяти, и определять тип, сравнивая значение указателя с известными диапазонами.
Во-вторых, выбор делается не обязательно из функций, одинаковых по назначению.
Например, для операции a + b проверяется, уж не являются ли обе переменных строками — тогда срабатывает конкатенация; иначе сложение.
Считать назначение конкатенации и сложения одинаковым можно только с натяжкой.
Ещё более отвязный пример — операция %: для строк она означает форматирование, которое никак не похоже по семантике на вычисление остатка.

Но это всё относится исключительно к типизации — она делается уже после того, как выполнено связывание переменной.
Смотрите, в Java, С#, JS обращение вида a+=1; может относиться к локальной переменной, к параметру метода, к полю текущего объекта.
В C# — ещё и к свойству текущего объекта, и к статическому свойству одного из классов, которые втащены в текущую область видимости при помоши using static.

Так вот, в Java и C# выбор между всеми этими вариантами определяется статически; в каждой строчке кода "a" имеет ровно одну, чётко определённую семантику. И она не зависит от того, какой код исполнялся перед тем, как исполняется эта строчка.
Если я правильно понимаю Питон, то там — не так. И смысл "а" в выражении a = a + 1 может меняться в зависимости от того, какой код исполнялся до этого.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Что такое "связывание переменных"?
От: vsb Казахстан  
Дата: 29.11.21 14:59
Оценка: +1
Я не думаю, что в пайтоне динамическое связывание переменных. Я его видел только в скриптах и в Emacs Lisp-е. В подавляющем большинстве языков программирования статическое связывание переменных.

Пример динамического связывания переменных:

function f1() {
    x = 1;
}

function f2() {
    var x;
    f1();
    print(x); // 1
}


По сути при динамическом связывании переменных у тебя все переменные глобальные. Рекурсию устраивать вообще сложно. Суть статического связывания в том, что глядя на программу (до выполнения) и на одинаковый идентификатор в двух местах ты всегда можешь сказать — является ли этот идентификатор обозначением одной и той же переменной, или нет. В Python или JavaScript статическое связывание.

Я думаю, имеется в виду динамическая типизация (против статической). При динамической типизации у переменной может быть значение любого типа. При статической — только одного типа.
Отредактировано 29.11.2021 15:02 vsb . Предыдущая версия . Еще …
Отредактировано 29.11.2021 15:01 vsb . Предыдущая версия .
Re[5]: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.11.21 16:07
Оценка: 3 (1) +1
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, Sinclair, Вы писали:


S>>Что выведет программма? Какого типа будет data в строке 4?

vaa>F# shadowing
vaa>
vaa>let test () =
vaa>    let data = 1
vaa>    let printData () = printfn "%A" data
vaa>    printData ()
vaa>    let data = "lala"
vaa>    printData ()
vaa>

vaa>
>> FSI_0007.T1.test();;
vaa>1
vaa>1
vaa>val it: unit = ()
vaa>


vaa>локальная функция захватила первое значение.

Это ответ на какой-то другой вопрос. В вашем примере нет ветвления, поэтому можно считать, что data биндится к целому числу в строчках с 2 по 4, а в строчках 5 и 6 она биндится к строке.
То, что printData захватила первое значение, связано только с тем, что она расположена в тех строчках, где data — это 1.
Если мы заменим вызовы printData на printfn, то они выведут разные значения.
Тем не менее, в каждой точке у вас data имеет ровно один биндинг и ровно один тип.
А если мы перенесём let data = "data" внутрь условного оператора, то после выхода из блока shadow исчезнет, и data снова станет равным 1.
Это ненамного более интересно, чем shadowing в C++, пример которого я привёл в начале дискуссии.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Что такое "связывание переменных"?
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 29.11.21 21:58
Оценка:
Pzz> чтобы человек не перепутал, и не положил количество яблок в переменную, тип которой объявленн, как количество апельсинов.

А что по этому поводу думали авторы Rust, которые сделали кадую строчку переобъявлением переменной,
кроме тех переменных, которые помечены словом mut ?
// Rust          C/C++
    a: &T     == const T* const a; // can't mutate either
mut a: &T     == const T* a;       // can't mutate what is pointed to
    a: &mut T == T* const a;       // can't mutate pointer
mut a: &mut T == T* a;             // can mutate both


Второй раз у них присвоить нельзя:
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html

error[E0384]: cannot assign twice to immutable variable `x`


Но слово let — оно ведь лишнее:
https://doc.rust-lang.org/std/keyword.let.html

Multiple variables can be defined with the same name, known as shadowing.


Можно было сделать shadowing по-умолчанию и сэкономить на словах let и var.
Отредактировано 29.11.2021 22:17 Эйнсток Файр . Предыдущая версия . Еще …
Отредактировано 29.11.2021 22:03 Эйнсток Файр . Предыдущая версия .
Отредактировано 29.11.2021 22:02 Эйнсток Файр . Предыдущая версия .
Отредактировано 29.11.2021 21:58 Эйнсток Файр . Предыдущая версия .
Re[3]: Что такое "связывание переменных"?
От: Pzz Россия https://github.com/alexpevzner
Дата: 29.11.21 22:25
Оценка:
Здравствуйте, Эйнсток Файр, Вы писали:

Pzz>> чтобы человек не перепутал, и не положил количество яблок в переменную, тип которой объявленн, как количество апельсинов.


ЭФ>А что по этому поводу думали авторы Rust, которые сделали кадую строчку переобъявлением переменной,

ЭФ>кроме тех переменных, которые помечены словом mut ?

Авторы rust думали о том, что 80% переменных в программе естественным образом присваиваются один раз и используются, по сути дела, как в математике, чтобы упростить чтение текста, дав короткое и простое название длинному и сложному выражению.

А и в тех случаях, когда переменная используется именно как переменная (именованная сущность, неоднократно меняющая значение), можно ее явно соответствующим образом объявить.

В общем, тоже для автоматического отлова типичных ошибок.

ЭФ>Можно было сделать shadowing по-умолчанию и сэкономить на словах let и var.


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

P.S. Подкину для тебя новую мысль для обдумывания. В программе без циклов мутабельные переменные не нужны.
Re[4]: Что такое "связывание переменных"?
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 29.11.21 23:17
Оценка: :)
Pzz> В программе без циклов мутабельные переменные не нужны.

А небесконечный цикл можно развернуть (loop unrolling). И что? Над чем тут надо думать?
Отредактировано 29.11.2021 23:18 Эйнсток Файр . Предыдущая версия .
Re[4]: Что такое "связывание переменных"?
От: amironov79  
Дата: 01.12.21 07:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, ок. Давайте так:

S>
S>data=1; // целое
S>if((new Random()).Next(1) == 0)
S>  data="lala"; // строка
S>Console.WriteLine(data);
S>

S>Что выведет программма? Какого типа будет data в строке 4?

dynamic Но не думаю, что компилятор должен такой тип выводить. Без объявления переменных далеко не уедешь. В том же питоне из-за отсутствия объявлений, появляются костыли вроде nonlocal.
Re[6]: Что такое "связывание переменных"?
От: amironov79  
Дата: 01.12.21 08:06
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Но это всё относится исключительно к типизации — она делается уже после того, как выполнено связывание переменной.

S>Смотрите, в Java, С#, JS обращение вида a+=1; может относиться к локальной переменной, к параметру метода, к полю текущего объекта.
S>В C# — ещё и к свойству текущего объекта, и к статическому свойству одного из классов, которые втащены в текущую область видимости при помоши using static.

Не думал, что когда-нибудь такое скажу, но здесь примером может быть PHP. При обращении к полям $this или static обязательно.
Re[5]: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.12.21 08:33
Оценка: +1
Здравствуйте, amironov79, Вы писали:

A>Здравствуйте, Sinclair, Вы писали:


S>>Ну, ок. Давайте так:

S>>
S>>data=1; // целое
S>>if((new Random()).Next(1) == 0)
S>>  data="lala"; // строка
S>>Console.WriteLine(data);
S>>

S>>Что выведет программма? Какого типа будет data в строке 4?

A>dynamic

Ну, если идти по этому пути, то data будет иметь тип object. Но есть ли в этом смысл?
Что лучше — вывести вот так наиболее общий тип, выдать ошибку присваивания, или выполнить shadowing внутри then-блока оператора if?
По мне, первый и третий варианты плохи.
В первом у нас есть риск одним неосторожным присваиванием сломать тип переменной, и полностью поменять биндинги вызовов по всему методу. Это может иметь далеко идущие последствия — то есть семантика программы мгновенно поменялась (даже если у нас условие под if никогда не стреляет), и разработчик будет ломать голову о причинах такого поведения. Хорошо ещё, если у него есть IDE, где можно навести курсор на data и увидеть выведенный тип.
После этого всё ещё потребуется найти, какое же из присваиваний изменило тип, но хотя бы будет понятно, что не так. Без IDE надо будет ещё понять, что привело к срабатыванию другой перегрузки того же метода. Нафиг-нафиг.

В третьем у нас возникает следующая проблема:
data=1; // целое
if((new Random()).Next(1) == 0)
  data=2; // целое
Console.WriteLine(data);

Что здесь произойдёт — shadowing или присваивание? Тип на этот раз мы не меняем; если мы по-прежнему выполняем shadowing, то это жутко континтуитивно. Ещё хуже, чем K&R C, оставляющий без диагностики перлы вроде if(nextRandom==0); data = 2;
Если мы выполняем присваивание — то опять не легче. Только что у нас была инициализация строкой, которая выполняла shadowing, и не портила data ниже по коду; а теперь вдруг точно такое же по форме выражение стало присваиванием. В F# есть чёткое различие между let data = 2 и data = 2 — и из этих примеров видно, почему.
Во flow9 разрушающее присваивание разрешено только для специальных byRef типов, и его синтаксис отличается от инициализации — по той же причине.
A>Но не думаю, что компилятор должен такой тип выводить. Без объявления переменных далеко не уедешь. В том же питоне из-за отсутствия объявлений, появляются костыли вроде nonlocal.
Дело даже не в объявлении переменных, а в синтаксической различимости конструкций "связать имя с выражением" и "выполнить разрушающее присваивание"
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.12.21 08:35
Оценка:
Здравствуйте, amironov79, Вы писали:

A>Здравствуйте, Sinclair, Вы писали:


S>>Но это всё относится исключительно к типизации — она делается уже после того, как выполнено связывание переменной.

S>>Смотрите, в Java, С#, JS обращение вида a+=1; может относиться к локальной переменной, к параметру метода, к полю текущего объекта.
S>>В C# — ещё и к свойству текущего объекта, и к статическому свойству одного из классов, которые втащены в текущую область видимости при помоши using static.

A>Не думал, что когда-нибудь такое скажу, но здесь примером может быть PHP. При обращении к полям $this или static обязательно.

По большому счёту, это непринципиально. Пока у нас есть однозначные правила, а биндинг делается статически, всё будет работать и без костылей вроде $this.
В шарпе эта фича не приводит к сколь-нибудь заметному геморрою, зато позволяет в нужных местах писать код компактно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.