Julia: Правила области видимости глобальных переменных приводят к неинтуитивному поведению в REPL / блокноте

Созданный на 21 авг. 2018  ·  98Комментарии  ·  Источник: JuliaLang/julia

Пример 1

Это придумал студент, который обновился с 0.6 до 1.0 напрямую, поэтому у него даже не было шанса увидеть предупреждение об устаревании, не говоря уже о том, чтобы найти объяснение новому поведению:

julia> beforefor = true
true

julia> for i in 1:2
         beforefor = false
       end

julia> beforefor  # this is surprising bit
true

julia> beforeif = true
true

julia> if 1 == 1
         beforeif = false
       end
false

julia> beforeif  # Another surprise!
false

julia> function foo()
         infunc = true
         for i in 1:10
           infunc = false
         end
         <strong i="7">@show</strong> infunc
       end
foo (generic function with 1 method)

julia> foo()  # "I don't get this"
infunc = false 

Пример 2

julia> total_lines = 0
0

julia> list_of_files = ["a", "b", "c"]
3-element Array{String,1}:
 "a"
 "b"
 "c"

julia> for file in list_of_files
         # fake read file
         lines_in_file = 5
         total_lines += lines_in_file
       end
ERROR: UndefVarError: total_lines not defined
Stacktrace:
 [1] top-level scope at ./REPL[3]:4 [inlined]
 [2] top-level scope at ./none:0

julia> total_lines  # This crushs the students willingness to learn
0

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

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

Я не уверен, каковы правила в Python, но я знаю, что обычно вы не можете назначать вещи в глобальной области без вызова global. Но в REPL это действительно работает, по-видимому, потому, что в REPL правила другие или применяется та же логика, как если бы они все находились в области действия функции.

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

Перекрестные ссылки:

19324

https://discourse.julialang.org/t/repl-and-for-loops-scope-behavior-change/13514
https://stackoverflow.com/questions/51930537/scope-of-variables-in-julia

REPL minor change

Самый полезный комментарий

@JeffBezanson , помните, что многие из нас хотели бы использовать Julia в качестве замены Matlab и т. Д. В технических курсах, таких как линейная алгебра и статистика. Это не курсы программирования, и студенты часто не имеют опыта программирования. Мы никогда не занимаемся структурированным программированием - оно почти полностью интерактивно с короткими фрагментами и глобальными переменными.

Более того, причина, по которой я использую динамический язык, в первую очередь, заключается в том, чтобы плавно переключаться между интерактивным исследованием и более дисциплинированным программированием. Невозможность использовать один и тот же код в глобальном контексте и в контексте функции является препятствием для достижения этой цели даже для тех, кто привык к концептуальным концепциям, и это намного хуже для студентов, не имеющих опыта работы в CS.

Все 98 Комментарий

(Per @mlubin , это соответствующее изменение https://github.com/JuliaLang/julia/pull/19324)

Стефан предложил здесь , что одна возможность решить эту проблему , автоматически упаковка записей Repl в let блоков

Но разве это не сбивает с толку, если ты не умеешь делать

a = 1

и использовать после этого a ? Полагаю, если только global не вставлен для всех назначений верхнего уровня?

Поведение не заключалось бы в том, чтобы просто обернуть все в блок let - это более сложно. Вам нужно разрешить привязку любого глобала, назначенного внутри выражения, а затем извлечь привязанное значение let к глобалу в конце выражения.

Итак, вы превратите a = 1 во что-то вроде a = let a; a = 1; end . И что-то вроде

for i in 1:2
    before = false
end

превратится в это:

before = let before = before
    for i in 1:2
        before = false
    end
end

Честно говоря, меня очень раздражает то, что люди дают только такую ​​обратную связь сейчас. Это изменение находится в ведении мастера уже десять месяцев.

Я виноват в том, что до недавнего времени не следил за мастером очень закрыто, так что этот отзыв действительно немного запоздал. Это больше, чем забота программистов (большинство циклов for будут внутри функции в коде библиотеки). Боюсь, это проблема для обучения. Часто циклы for преподаются перед функциями или областями действия (конечно, вам нужно понимать области действия, чтобы действительно понимать, что происходит, но при обучении вещи часто упрощаются).

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

Честно говоря, меня очень раздражает то, что люди дают только такую ​​обратную связь сейчас. Это изменение находится в ведении мастера уже десять месяцев.

Честно говоря, Julia 0.7 вышла 13 дней назад. Это новое изменение для большинства пользователей Julia.

Честно говоря, меня очень раздражает то, что люди дают только такую ​​обратную связь сейчас. Это изменение было на мастере уже десять месяцев.

К сожалению для тех из нас, кто не может жить на грани, это новинка с нашей точки зрения.

Честно говоря, меня очень раздражает то, что люди дают только такую ​​обратную связь сейчас. Это изменение находится в ведении мастера уже десять месяцев.

А для тех из нас, кого побуждают держаться подальше от ветвей разработки, «это новинка с нашей точки зрения».

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

Я виноват в том, что до недавнего времени не следил за мастером очень закрыто, так что этот отзыв действительно немного запоздал. Больше, чем беспокойство для программистов (большинство циклов for будет внутри функции в коде библиотеки). Боюсь, это проблема для обучения. Часто циклы for изучаются перед функциями или областями (конечно, вам нужно понимать области действия, чтобы действительно понимать, что происходит, но при обучении вещи часто упрощаются).

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

Это важный момент. После того, как выяснилось, в чем проблема на самом деле, удивительно, насколько мало она проявляется на самом деле. Это меньшая проблема с большим количеством кода Julia в дикой природе и в тестах, и он действительно выявил множество переменных, которые были случайно глобальными (в обоих тестах Julia Base в соответствии с исходным PR, и я заметил это в большинстве случаев. Тесты DiffEq). В большинстве случаев кажется, что слегка неправильное поведение - это не то, что вы получаете (ожидание изменения в цикле), а скорее ожидание возможности использовать переменную в цикле - это то, что я обнаружил как подавляющее большинство где это проявляется при обновлении тестовых сценариев до версии 1.0. Так что хорошо то, что в большинстве случаев пользователю выдается ошибка, и ее нетрудно исправить.

Плохо то, что помещать global x внутри циклов немного многословно, и теперь ваш код REPL также отличается от кода функции. Трудно сказать, является ли это более интуитивным поведением, чем раньше, потому

Я лично хотел бы увидеть эксперименты с блокировкой let . Это сохранит аспект «вы действительно не хотите так много глобальных объектов» вместе с упрощенным объяснением области видимости, и в то же время заставит код REPL вести себя как внутренности функций (что, по-видимому, именно то, что мы всегда хотели). Или, наоборот, заставляя людей указывать переменные, которые они хотят действовать как глобальные

global x = 5
for i = 1:5
  println(x+i)
end

может быть хорошим способом сохранить ясность и сделать "код REPL медленным из-за глобальных переменных" будет гораздо более очевидным. Обратной стороной является то, что повторное добавление вещей в функцию не потребует маркеров global .

Но, учитывая то, как это обычно проявляется, это не является прорывом в игре или шоу-стопом. Я бы классифицировал это как бородавку, о которой следует упомянуть на любом семинаре, но это не значит, что v1.0 из-за этого непригоден для использования. Я надеюсь, что изменение этого поведения не классифицируется как нарушение и требует версии 2.0.

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

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

Я лично хотел бы увидеть эксперименты с блокировкой let . Это сохранит аспект «вы действительно не хотите так много глобалов» вместе с упрощенным объяснением области видимости, и в то же время заставит код REPL вести себя как внутренности функций (что, по-видимому, то, что мы всегда хотели)

Если мы собираемся «REPL - это то же самое, что и внутренняя часть функции», нам также следует подумать о outer :

julia> i = 1
1

julia> for outer i = 1:10
       end
ERROR: syntax: no outer variable declaration exists for "for outer"

против:

julia> function f()
          i = 0
          for outer i = 1:10
          end
          return i
       end
f (generic function with 1 method)

julia> f()
10

Честно говоря, меня очень раздражает то, что люди дают только такую ​​обратную связь сейчас. Это изменение находится в ведении мастера уже десять месяцев.

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

(Тем не менее, я был одним из немногих, кто высказал свое мнение в # 19324, где я отстаивал прежнее поведение .)

Неразрывным выходом из этого было бы вернуться к старому поведению (в идеале не вставляя неявные блоки let или что-то еще - просто восстановите старый код в julia-syntax.scm как вариант) в REPL. Или, скорее, чтобы сделать его доступным в таких средах, как IJulia, которым он может понадобиться, добавьте флаг soft_global_scope=false к include , include_string и Core.eval чтобы восстановить старое поведение.

(Тем не менее, я был одним из немногих, кто высказал свое мнение в # 19324, где я отстаивал прежнее поведение.)

Да, и я это очень ценю. Сейчас это не имеет большого значения, так как мы сделали выбор, дали ему выпекать десять месяцев и теперь выпустили его с долгосрочным обязательством по стабильности. Поэтому единственное, что нужно сделать сейчас, - это сосредоточиться на том, что делать в будущем.

Возможность выбирать между старым и новым поведением - это интересно, но кажется очень хакерским. Это означает, что у нас не только иногда бывает поведение при определении области видимости, которое все, по-видимому, находят невероятно запутанным, но у нас оно не всегда, и есть ли оно у нас или нет, зависит от глобального флага. Боюсь, это неудовлетворительно.

Возможность выбирать между старым и новым поведением - это интересно, но кажется очень хакерским.

Если кто-то реализует преобразование AST с мягкой областью видимости «unbreak me», будет очень заманчиво использовать его в IJulia, OhMyREPL и т. Д., И в этот момент вы получите еще более проблематичную ситуацию, в которой REPL по умолчанию будет считаться сломанным.

Я не это говорю. Ясно, что мы должны использовать одно и то же решение во всех этих контекстах. Но реализация его в виде двух разных вариантов правил области видимости кажется менее чистой, чем реализация ее в виде преобразования кода с одним набором правил области видимости. Но, возможно, они функционально эквивалентны. Однако, кажется, легче объяснить в терминах новых более простых правил области видимости + преобразование, которое принимает входные данные в стиле REPL и преобразует их перед оценкой.

Это можно сделать как Meta.globalize(m::Module, expr::Expr) которое преобразует выражение, автоматически аннотируя любые глобальные объекты, существующие в модуле, как глобальные, если они назначены внутри любой нефункциональной области верхнего уровня. Конечно, я думаю, что это эквивалентно тому, что делал старый синтаксический анализатор, но немного более прозрачно, поскольку вы можете сами вызвать Meta.globalize и посмотреть, что будет оценивать REPL.

Это можно сделать как Meta.globalize(m::Module, expr::Expr) которое преобразует выражение, автоматически аннотируя любые глобальные объекты, существующие в модуле, как глобальные, если они назначены внутри любой нефункциональной области верхнего уровня.

Я действительно начал изучать реализацию чего-то подобного несколько минут назад. Однако похоже, что было бы намного проще реализовать в качестве опции в julia-syntax.jl :

  • Написание внешнего преобразования AST возможно, но, похоже, есть много сложных угловых случаев - вам в основном нужно повторно реализовать правила области видимости - тогда как у нас уже был код, чтобы сделать это правильно в julia-syntax.scm .
  • Это еще сложнее для чего-то вроде IJulia, которое в настоящее время использует include_string для оценки всего блока кода и получения значения последнего выражения. Мы не только должны переключиться на синтаксический анализ выражения по выражению, но также могут потребоваться некоторые хакерские действия, чтобы сохранить исходные номера строк (для сообщений об ошибках и т. Д.). (Хотя я нашел взлом ChangePrecision.jl для такого рода вещей, который может работать и здесь.)
  • Не говоря уже о случаях, когда люди используют include внешние файлы, которые не будут перехвачены вашим преобразованием AST.

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

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

Вот черновик реализации globalize(::Module, ast) : https://gist.github.com/stevengj/255cb778efcc72a84dbf97ecbbf221fe

Хорошо, я понял, как реализовать функцию globalize_include_string которая сохраняет информацию о номере строки, и добавил ее в свою суть .

Возможный (неразрывный) путь вперед, если людям нравится такой подход:

  1. Выпустите пакет SoftGlobalScope.jl с функциями globalize и т. Д.
  2. Используйте SoftGlobalScope в IJulia (и, возможно, в Juno, vscode и OhMyREPL).
  3. Включите функции SoftGlobalScope в будущий выпуск пакета REPL stdlib и используйте его в REPL.

Или это целесообразно сразу же накатить в REPL.jl? Я не совсем понимаю, как обновления stdlib работают в 1.0.

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

Разве мы не можем сделать это не по умолчанию функцией REPL в 1.1?

Дубликаты № 28523 и № 28750. Тем, кто говорит, что они не хотят учить людей глобальным переменным, я предлагаю сначала обучить функциям, прежде чем циклы for . В любом случае функции более фундаментальны, и это поможет установить ожидание того, что код должен быть написан в функциях. Хотя я понимаю неудобства, такое поведение области видимости можно превратить в педагогическое преимущество: «На самом деле, глобальные переменные - это настолько плохая идея, особенно использование их в циклах, что язык заставляет вас наклоняться назад, чтобы использовать их».

Однако мне кажется, что добавление нестандартной функции в REPL для этого вполне нормально.

@JeffBezanson , помните, что многие из нас хотели бы использовать Julia в качестве замены Matlab и т. Д. В технических курсах, таких как линейная алгебра и статистика. Это не курсы программирования, и студенты часто не имеют опыта программирования. Мы никогда не занимаемся структурированным программированием - оно почти полностью интерактивно с короткими фрагментами и глобальными переменными.

Более того, причина, по которой я использую динамический язык, в первую очередь, заключается в том, чтобы плавно переключаться между интерактивным исследованием и более дисциплинированным программированием. Невозможность использовать один и тот же код в глобальном контексте и в контексте функции является препятствием для достижения этой цели даже для тех, кто привык к концептуальным концепциям, и это намного хуже для студентов, не имеющих опыта работы в CS.

помните, что многие из нас хотели бы использовать Julia в качестве замены Matlab и т. д. в технических курсах, таких как линейная алгебра и статистика. Это не курсы программирования, и студенты часто не имеют опыта программирования. Мы никогда не занимаемся структурированным программированием - оно почти полностью интерактивно с короткими фрагментами и глобальными переменными.

Многие из нас, пользователей Джулии, абсолютно не имеют опыта CS (включая меня), но мне кажется, что правильное отношение ( особенно для студентов) - это готовность учиться, а не требовать, чтобы что-то изменилось в худшую сторону, чтобы приспособиться к нашей наивности.

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

Итак, мой вопрос как пользователя, не имеющего отношения к CS, который также заботится о производительности, заключается в том, как я, вероятно, облажусь, если бы это было сделано по умолчанию. Являются ли проблемы буквально одними из тех примеров, которые мы здесь видим (о которых я уже знал), или мы, скорее всего, часто будем плохо это делать более тонкими способами?

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

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

Разве мы не можем сделать это не по умолчанию функцией REPL в 1.1?

Выбор варианта «Разбить меня» по умолчанию кажется более разумным, особенно вариант, ориентированный непосредственно на начинающих пользователей. Если это не вариант по умолчанию, то именно те люди, которые нуждаются в нем больше всего, будут теми, у кого он не включен (и не знает, что он существует).

Что предложенный REPL-режим сделает со скриптами include ed? Будет ли оценка глобальных операторов зависеть от того, активирован ли режим REPL? Если так, ИМО, это будет противоречить обещанию стабильности 1.0.

Если бы мы сделали что-то подобное, похоже, для модуля имеет смысл определить, как он работает. Таким образом, Main будет модулем "мягкой области", тогда как по умолчанию другие модули будут модулями "жесткой области".

Мне было интересно узнать, можно ли обезьяно исправить REPL, чтобы использовать функцию globalize @stevengj, и, похоже, это без особых усилий (хотя и довольно хакерских). Увидеть суть . Это не работает с Juno (или чем-либо еще, что напрямую вызывает Core.eval ).

Я не собираюсь рекомендовать это людям, но для меня это очень полезно при проведении быстрого и грязного анализа данных. Я бы очень хотел увидеть (более продуманное) решение, поскольку оно действительно сбивает с толку неопытных и часто сопротивляющихся кодеров (то есть моих студентов), когда вы не можете скопировать и вставить код из функции в REPL, чтобы увидеть что он делает, и наоборот.

julia> a = 0                                                                
0                                                                           

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  
ERROR: UndefVarError: a not defined                                         
Stacktrace:                                                                 
 [1] top-level scope at .\REPL[2]:2 [inlined]                               
 [2] top-level scope at .\none:0                                            

julia> using SoftGlobalScope                                                
[ Info: Precompiling SoftGlobalScope [363c7d7e-a618-11e8-01c4-4f22c151e122] 

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  

julia> a                                                                    
55                                                                          

(Кстати: это примерно столько же тестов, сколько и было!)

Что предложенный REPL-режим сделает с включенными скриптами?

Ничего такого. По сути, предполагается, что это будет только для кода, вводимого в интерактивной подсказке. Как только вы начнете складывать вещи в файлы, вам нужно изучить правила «жесткой области». Надеюсь, когда вы начнете помещать код в файлы, вы начнете использовать functions.

Не идеально иметь более придирчивые правила области видимости для глобального кода в файлах, чем в подсказке. Но я думаю, что # 19324 в сочетании с обещанием стабильности Julia 1.0 не оставляет нам идеальных вариантов.

@stevengj :

@JeffBezanson , помните, что многие из нас хотели бы использовать Julia в качестве замены Matlab и т. Д. В технических курсах, таких как линейная алгебра и статистика. Это не курсы программирования, и студенты часто не имеют опыта программирования. Мы никогда не занимаемся структурированным программированием - оно почти полностью интерактивно с короткими фрагментами и глобальными переменными.

Я преподавал курсы с использованием Julia студентам, ранее знакомым с Matlab / R / ..., и я сочувствую этой проблеме. Но в то же время я не думаю, что использование Julia в качестве замены Matlab и т. Д. Является жизнеспособным подходом: как бесчисленное количество раз демонстрировалось в вопросах по Discourse и StackOverflow, это может привести к ошибкам производительности, которые трудно исправить и понять, возможно, это потребует даже больших затрат, чем инвестирование в понимание того, чем Julia отличается от этих других языков (см. сообщения с темами «Я перевел этот код из Matlab, и он в 10 раз медленнее»).

Я думаю, что ключевой вопрос - это тихий провал; саму по себе проблему легко понять и исправить. Я бы предложил сохранить новое поведение, но с предупреждением в Main (по умолчанию; должно быть возможно отключить его).

Для меня более серьезной проблемой является кажущееся несоответствие. То есть, я не против, чтобы Юля поступала по-другому, но:

  • Почему код, вставленный из функции, не должен работать в REPL?
  • Ни один другой язык, который я когда-либо использовал, не имеет такого поведения, и это еще один барьер для принятия
  • Почему блоки for ведут себя иначе, begin и if ? ( if Я вроде понимаю, но блок [должен быть] блоком.).

Что касается пункта 2, я думаю, что это важнее, чем мы, которые уже какое-то время использовали Джулию (и привержены языку), могли бы понять. Я могу сказать вам, что сейчас я 0 из 7 в том, чтобы убедить мою группу писать код на Julia; два из них были связаны с проблемой цикла for которую я не мог объяснить, потому что раньше не сталкивался с ней. Остальное, полагаю, можно списать на недостаток моей харизмы.

Я бы предпочел убедиться, что код, вставленный из функции в REPL, ведет себя идентично функции и чтобы циклы for выполняли ожидаемые действия при использовании их для интерактивного анализа данных; то есть, в частности, что они изменяют внешние / глобальные переменные при указании без каких-либо специальных ключевых слов.

Я не думаю, что использование Julia в качестве замены Matlab и т. Д. Является жизнеспособным подходом: как бесчисленное количество раз демонстрировалось в вопросах по Discourse и StackOverflow, это может привести к ошибкам производительности, которые трудно исправить и понять, что, возможно, повлечет за собой еще большие затраты. чем вкладывать средства в понимание того, чем Джулия отличается от других языков.

Извините, но этот аргумент мне смешон. Я не говорю о классах, где я преподаю программирование. Есть место для простых интерактивных вычислений, и в классах, не относящихся к CS, обычно вводят в языки программирования как «прославленный калькулятор» для начала. Обучение вычислению производительности в Julia - это совершенно другой процесс, но это не повредит, если они уже использовали Julia в качестве своего «калькулятора».

Если вы начнете с ознакомления студентов с Matlab как с их «калькулятором», будет намного сложнее перейти к «настоящему» программированию, потому что их первое желание - сделать как можно больше с Matlab, прежде чем покинуть корабль, и в этот момент их вредные привычки укоренились, и они не хотят изучать новый язык. Напротив, если вы начнете с Джулии в качестве вашего прославленного калькулятора, когда придет время заняться более серьезным программированием, у вас будет гораздо более широкий спектр доступных вариантов. Вам не нужно учить их втискивать все в «векторные» операции или заставлять их делать что-то плохо, прежде чем они сделают это правильно.

Вы хотите сказать, что мне не следует использовать Джулию в моем курсе линейной алгебры ? Или что я должен использовать его, только если я готов преподавать информатику, а также линейную алгебру?

Я согласен с @stevengj как в отношении проблемы (обучение непрограммистов становится намного сложнее), так и в отношении решения (заставить все работать в REPL и различных IDE). Включение сценария по-прежнему будет иметь правила области видимости Julia 1.0, но это не вызывает беспокойства, просто нужно быть осторожным, чтобы иметь класс «мы можем поместить наш цикл for в функцию, а затем вызвать функцию» перед классом «мы можем поместить наш цикл for в файле и включите класс file.

Это звучит как хороший компромисс, поскольку интерактивная отладка в REPL не становится более болезненной, чем должна быть (или более запутанной для новых пользователей), в то время как обычный код в сценариях должен следовать строгим правилам области видимости и защищен от ошибок, перезаписывающих некоторые переменные случайно.

Вы хотите сказать, что мне не следует использовать Джулию в моем курсе линейной алгебры? Или что я должен использовать его, только если я готов преподавать информатику, а также линейную алгебру?

Возможно, вы неправильно поняли то, что я говорил (или я не выразил это четко). Я говорил о курсах, в которых Джулия используется для обучения чему-то специфическому для предметной области (например, я преподавал численные методы студентам-экономистам), а не о курсах CS (с которыми у меня нет опыта).

Я пытался подчеркнуть, что разумно ожидать определенного уровня различий между Julia и языком X (которым может быть Matlab); и наоборот, игнорирование этого может (и приводит) к проблемам.

Лично я, изучая новый язык, предпочитаю сталкиваться с этими проблемами на ранней стадии; Кроме того, я считаю, что простота и последовательность семантики языка более важны, чем сходство с другими языками в долгосрочной перспективе. Но я признаю эти предпочтения субъективными, а у разумных людей могут быть разные.

Я создал (незарегистрированный) пакет https://github.com/stevengj/SoftGlobalScope.jl

Если это кажется разумным, я могу продолжить и зарегистрировать пакет, а затем использовать его по умолчанию в IJulia (и, возможно, отправить PR в Juno и так далее).

Я пытался сделать вывод о том, что разумно ожидать определенного уровня разницы между Julia и языком X (которым может быть Matlab).

Очевидно. Когда я говорю «используйте Julia вместо Matlab», я не имею в виду, что пытаюсь научить их синтаксису Matlab в Julia, и я не нацелен на бывших пользователей Matlab.

Я предпочитаю решать эти проблемы на ранней стадии

Дело не в отличиях от Matlab как таковых. Я бы действительно предпочел не говорить о глобальной и локальной области видимости и полезности ключевого слова global для статического анализа в первый раз, когда я пишу цикл перед студентами, не имеющими образования в области CS, или когда они впервые вставляют код из функцию в REPL, чтобы попробовать ее в интерактивном режиме. Я бы предпочел сосредоточиться на математике, которую я пытаюсь выразить с помощью цикла.

Никто здесь не выступает за мягкую интерактивную область видимости только потому, что этого ожидают пользователи Matlab. , что длинные отступления в незнакомой концепции «области действия» наверняка сорвут любую лекцию, не global когда мы работаем в интерактивном режиме.)

Еще одно исправление, не упомянутое здесь, заключается в том, чтобы просто перестать определять для 'for' блок области видимости (просто function и let создадут новую область видимости)

@vtjnash , я бы предпочел сосредоточить это обсуждение на том, что мы можем делать до Julia 2.0. Я согласен с тем, что изменение поведения интерактивного режима - это лишь временная мера, и мы должны серьезно подумать об изменении правил определения объема работ через несколько лет.

Хороший момент, для этого также требуется import Future.scope 😀

(Я думаю, что этот модуль / пространство имен / поведенческий эффект уже зарезервирован / существует)

Напоминаем, что изменение должно было гарантировать, что код ведет себя одинаково во всех средах глобальной области видимости, независимо от того, что еще ранее оценивалось в этом модуле. До этого изменения вы могли получить совершенно разные ответы (в результате различного назначения области), просто выполнив один и тот же код дважды или перемещая его в файле.

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

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

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

Вы имеете в виду, что в приведенном ниже коде a меняется между первым и вторым циклами for ? На мой взгляд, это ожидаемое поведение, а не ошибка.

a = 0
for i = 1:5
  a += 1
end

for i = 1:5
  a += 1
end

Что предложенный REPL-режим сделает с включенными скриптами?

@ mauro3 @stevengj Я полагаю, добавление функции (скажем, exec("path/to/script.jl") ) может быть выполнено с помощью всего лишь небольшого изменения версии? Мы также можем предупредить exec 'другой файл из сценария exec ' ed, а затем поместить туда некоторые педагогические сообщения, чтобы подтолкнуть их к использованию include .

Некоторые мысли, которые я записал вчера вечером, пытаясь осмыслить эту проблему (еще раз), чтобы попытаться выяснить, какой может быть лучший курс действий. Без вывода, но я думаю, что это довольно ясно излагает проблему. Поразмыслив над этой проблемой в течение нескольких лет, я не думаю, что есть какое-либо «идеальное решение» - это может быть одна из тех проблем, для которых есть только неоптимальный выбор.


Люди наивно считают глобальную область видимости забавной, заключающей в себе локальную область видимости. Вот почему глобальные осциллографы работали так же, как в Julia 0.6 и ранее:

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

Однако основное отличие заключается в следующем:

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

Более того, поскольку глобальные области видимости часто бывают довольно длинными - нередко разбросанными по нескольким файлам - значение выражения зависит от других выражений, находящихся на произвольном расстоянии от него, это эффект «жуткого действия на расстоянии» и, как таковой, весьма нежелательный. .


Это последнее наблюдение показывает, почему две разные версии цикла for в глобальной области видимости ведут себя по-разному, поэтому проблематично:

# file1.jl
for i = 1:5
  a += 1
end
# file2.jl
a = 1



md5-f03fb9fa19e36e95f6b80b96bac9811e



```jl
# main.jl
include("file1.jl")
include("file2.jl")
include("file3.jl")

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

Другой проблемный случай - длительный сеанс REPL. Попробовать пример где-нибудь в сети? Это не удается, потому что у вас есть глобальная переменная с тем же именем, которое в примере используется для локальной переменной в цикле for или аналогичной конструкции. Таким образом, представление о том, что новое поведение - единственное, что может вызвать путаницу, определенно неверно. Я согласен с тем, что новое поведение - это проблема удобства использования в REPL, но я просто хочу смягчить разговор и ясно представить здесь другую сторону.

Мое небольшое предложение, которое не имеет отношения к проблеме ответа, но было бы полезно в дидактических целях при обучении языку не интерактивно, по крайней мере: определите основной блок с именем «программа», как это можно сделать в fortran (это то же, что и "let ... end" выше, только с более естественной записью):

тест программы
...
конец

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

Другой проблемный случай - длительный сеанс REPL. Попробовать пример где-нибудь в сети? Это не удается, потому что у вас есть глобальная переменная с тем же именем, которое в примере используется для локальной переменной в цикле for или аналогичной конструкции.

Сколько жалоб на рассылку и проблем с github было подано по этому поводу от расстроенных пользователей? По моим подсчетам, ноль. Почему? Вероятно, потому, что такое поведение принципиально неудивительно для людей - если вы работаете в глобальном масштабе, вы зависите от глобального состояния.

Таким образом, представление о том, что новое поведение - единственное, что может вызвать путаницу, определенно неверно.

Я думаю, что это ложная эквивалентность - здесь существует огромная разница в уровне потенциальной путаницы . В Julia 0.6 я мог бы объяснить ваш пример ученику за секунды: «О, видите ли, этот цикл зависит от a , которое вы здесь изменили». В Julia 1.0 я искренне беспокоюсь о том, что буду делать, если я нахожусь в середине лекции по линейной алгебре и мне придется таинственным образом ввести ключевое слово global перед студентами, которые никогда не слышали это слово «объем» в смысле CS.

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

Точно нет. Вы серьезно хотите вернуться в мир цикла до версии 0.2 (см. # 1571 и # 330)?

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

x = 0
f(y) = (x=y)

Внутри функции f изменит x из первой строки. В REPL этого не будет. Но с подобным преобразованием в SoftGlobalScope.jl это могло сработать. Конечно, мы, вероятно, не захотели бы, чтобы это было включено по умолчанию, поскольку тогда вставка определений автономных функций не сработает. Первое, что приходит в голову, - это режим REPL для построчной отладки функций.

Вы серьезно хотите вернуться в мир до версии 0.2?

Нет, я хочу вернуться в мир версии 0.6. 😉

Думаю, я больше отвечал на:

Еще одно исправление, не упомянутое здесь, - это просто перестать делать для определения блока области видимости.

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

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

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

Чтобы быть конкретным, мне бы очень хотелось, чтобы REPL соответствовал правилам области видимости тела функции; т.е. переменные являются локальными, а не глобальными, и вы можете просто скопировать и вставить код непосредственно из функции и знать, что это будет работать. Я предполагаю, что наивная реализация будет чем-то вроде обертывания let-block (как уже упоминалось ранее) формы

julia> b = a + 1

превращается в

let a = _vars[:a]::Float64 # extract the variables used from the backing store
    # Code from the REPL
    b = a + 1
    # Save assigned variables back to the backing store
   _vars[:b] = b
end

Я полагаю, что если все будет сделано правильно (то есть кем-то, кто знает, что они делают), это будет иметь ряд преимуществ по сравнению с существующим REPL. 1. Предыдущие рабочие процессы с интерактивным анализом / вычислением данных просто работают. 2. гораздо меньше постов на Discourse, где основной ответ - «прекратить тестирование с глобальными переменными» - все будет локально и так, надеюсь, быстро! :) 3. Копирование и вставка в / из тела функции работает должным образом. 4. Функция типа workspace() тривиальна, если резервное хранилище является своего рода Dict; просто убери это. 5. глобальные переменные становятся явными - вещи являются локальными, если вы специально не просите сделать их глобальными; с моей точки зрения, это большое преимущество, я не люблю неявно создавать глобальные объекты. Очень незначительный заключительный момент (и я не решаюсь добавить это!), Это будет соответствовать поведению Matlab, облегчая переход людей - в Matlab REPL все переменные кажутся локальными, если явно не аннотированы как глобальные.

Еще несколько часов назад эта история казалась мне великолепной. Но после комментария Джеффа о функциях я подумал о вставке в определения автономных функций и о том, как этот подход в основном предотвратит это, поскольку определения функций должны находиться в глобальной области (по крайней мере, это, вероятно, то, что было задумано); но то , что если бы они были предназначены , чтобы перейти в локальную область видимости (внутренняя функция)? Нет никакой информации, чтобы устранить двусмысленность этих двух возможностей. Казалось бы, необходимы два режима REPL: один с локальной областью видимости, а другой - с глобальной. С одной стороны, это может сбивать с толку (представьте себе сообщения Discourse ...), но с другой стороны, это может быть чрезвычайно полезно. (Наличие обоих режимов REPL также не будет нарушать работу, поскольку вы просто вводите новые функции :))

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

Извиняюсь за длинный пост, но я думаю, что это важно для удобства использования (и это помогло мне все продумать!).

Сколько жалоб на рассылку и проблем с github было подано по этому поводу от расстроенных пользователей? По моим подсчетам, ноль. Почему? Вероятно, потому, что такое поведение принципиально неудивительно для людей - если вы работаете в глобальном масштабе, вы зависите от глобального состояния.

Хм, вы действительно проводили систематическое изучение этого? Я, должно быть, пропустил это. Тем не менее, это не означает, что такое поведение не является источником ошибок или неожиданных результатов; просто после того, как пользователь понял это, это было признано правильным поведением и, таким образом, не вызвало проблемы / жалобы.

В Julia 1.0 я искренне беспокоюсь о том, что буду делать, если я нахожусь в середине лекции по линейной алгебре и мне придется таинственным образом ввести глобальное ключевое слово

Сочувствую этой проблеме. Когда я учил студентов-экономистов по некоторому простому программированию, необходимому для курса, я обычно предлагал им переходить туда и обратно между упаковкой кода в функции и просто комментированием function и end и запуском вещей. в глобальном масштабе, чтобы они могли проверить, что происходит. Это в значительной степени компенсировало отсутствие инфраструктуры отладки в то время в Julia.

Похоже, такой подход больше не применим. Но мне интересно, действительно ли это был правильный способ сделать это в любом случае, и тем временем различные вещи значительно улучшились (# 265 был исправлен, Revise.jl и недавно Rebugger.j значительно улучшили рабочий процесс / отладку).

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

Небольшое замечание: несмотря на то, что глобальная область видимости REPL ограничена специальным корпусом, он позволяет копировать и вставлять код в функции и из функций, но не позволяет копировать и вставлять в / из глобальной области действия другого модуля.

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

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

(И это не только с педагогической точки зрения. Циклы в глобальном масштабе полезны для интерактивной работы. И одна из основных причин, по которой людям нравятся динамические языки для технических вычислений, - их возможности для интерактивного исследования. Не все кодирование ориентировано на производительность.)

На протяжении многих лет возникали десятки тем и проблем, в которых люди сбивали с толку или жаловались на старое различие «мягкая / жесткая область видимости», поэтому утверждать, что никто никогда не был сбит с толку или жаловался на старое поведение, просто ... не правда. Я мог бы выкопать некоторые из них, но вы были рядом, @stevengj , так что вы можете выкопать их так же легко, и мне трудно поверить, что вы не заметили или не помните эти жалобы и разговоры.

@StefanKarpinski , я конкретно имею в виду людей, жалующихся, что глобальный цикл зависит от глобального состояния. Я не припомню, чтобы кто-нибудь жаловался, что это плохое поведение, и я не могу найти никаких примеров этого.

Я согласен с тем, что люди были сбиты с толку относительно того, когда и где присваивание определяет новые переменные, но обычно это было в другом направлении - они хотели, чтобы локальные области действия действовали более глобально (а не наоборот) или чтобы не было различия между begin и let . IIRC, жалоба никогда не заключалась в том, что присвоение глобальной переменной в глобальном цикле имело неожиданный побочный эффект изменения глобальной переменной.

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

@StefanKarpinski : У меня такое чувство, что раньше путаница с мягким / жестким прицелом носила скорее теоретический характер (люди читали руководство), нежели практический (люди получали неожиданные результаты). Это определенно было для меня, и что, например, результаты поиска здесь подтверждают это; Я нашел один контрпример здесь .

С другой стороны, это новое поведение запутает людей не при чтении руководства, а при использовании REPL. Возможно, последнее хуже.

SoftGlobalScope.jl теперь является зарегистрированным пакетом. Я намерен включить его по умолчанию (отказ) для IJulia, по крайней мере, в этом семестре.

@ mauro3 , даже ваш «контрпример» касается кого-то, кого смущает жесткий прицел, а не мягкий. Если сделать больше осциллографов «сложным» в версии 0.7, это наверняка приведет к еще большей путанице.

Я хотел бы отметить, что в IJulia есть интересная возможность сделать переменные локальными блоками do по умолчанию. Т.е. если вы сделаете это в одном блоке, то это сработает:

t = 0
for i = 1:n
    t += i
end
t

... и t виден только в этом блоке оценки. Если вы хотите, чтобы он был виден снаружи, вам нужно будет сделать следующее:

global t = 0
for i = 1:n
    global t += i
end
t

Я также рассмотрел аналогичный подход для Джулии, где блоки являются файлами, а не модулями. Другими словами, просто выполнение t = 0 в верхней области видимости создает переменную, которая является локальной, а не глобальной. Чтобы объявить действительно глобальную переменную, вам нужно написать global t = 0 которое затем будет видно во всем модуле. Возможно, это слишком странно, но за эти годы мне это приходило в голову много раз.

У IJulia есть интересная возможность сделать переменные локальными блоками do по умолчанию.

@StefanKarpinski , я думаю, это было бы еще более запутанным и противоречило бы тому, как обычно используются ноутбуки. Обычно одна и та же переменная используется / изменяется в нескольких ячейках, поэтому требование ключевого слова global для всех межъячеечных переменных для меня не является стартером - это потребует даже более подробного обсуждения концепций области действия, чем проблема с циклами for мы здесь обсуждали.

Я думаю, что до тех пор, пока мы все согласны - как нам кажется, - что это в основном или полностью вопрос взаимодействия, у нас есть путь вперед. Если мы используем особый случай в REPL (как это делается для IJulia), единственный плохой случай - это разработка чего-то в REPL и последующее перемещение этого в код сценария верхнего уровня. Возможно, именно здесь вам следует ввести функции, так что я не думаю, что это так уж плохо. Копирование кода между REPL и телами функций будет работать (в основном), что, вероятно, достаточно хорошо.

Затем у нас также есть возможность дополнительно обосновать / прояснить различие, сделав переменные REPL каким-то образом локальными для REPL, то есть не обычными глобальными переменными, недоступными как Main.x . Это очень похоже на то, что @StefanKarpinski только что предложил выше, но совместно используется всеми входными блоками / ячейками.

С практической точки зрения, исправить это в REPL не так.
важно только для обучающих / непрограммистов. Это поведение также
делает интерактивную отладку через REPL (путем копирования-вставки частей) очень
непрактично. Этот режим отладки иногда может быть предпочтительнее (даже перед
хороший отладчик и) даже для опытных программистов (а часто бывает одним из
причины предпочесть динамический язык). Конечно для опытных
программисты, быть необязательными не должно быть проблемой; Для начинающих пользователей это
было бы предпочтительно по умолчанию.

@StefanKarpinski
Как наивный программист, я действительно не понимаю, что такого плохого в просмотре
глобальная область видимости как забавный вид, заключающий в себе локальную область видимости, особенно в динамических
языков. Я понимаю, что с точки зрения компилятора это не так.
обязательно правильная (в Юлии), но это красивая, легкая и полезная модель
для (наивного) программиста. (Я также подозреваю, что это может быть реализовано таким образом в
некоторые языки).
Джулия, похоже, так же преподносит это программисту:
Следующая функция function выдаст ошибку «не определено», которая
он не будет работать, если перед циклом for поставить a = 1.

функциональный тест ()
для i = 1:10
а = а + я
конец
а = 1
@ показать
конец

что, если я полностью не понял, кажется, расходится с тем, "Был ли
внешняя локальная переменная существует, по замыслу, не зависит от порядка
появление или выполнение выражений во внешней локальной области ".

Я полностью согласен с тем, чтобы избегать «жутких действий на расстоянии», и многое
предпочитаю явное определение для использования глобальных объектов в стеке функций / вызовов
уровень и лично также хотел бы иметь что-то вроде загрузки из файла в
его собственная область видимости и требует явного определения для использования глобальных переменных.
На уровне петель для меня это слишком далеко, так как
определения / контекст обычно довольно близко.
Пример с тремя файлами немного надуман (и терпит неудачу из-за ожидаемого "а не
определена "ошибка"): обычно вы помещаете исходное определение в то же
файл.
В этом есть настоящая жуткая опасность (и меня это укусило
на других языках), в которые включены, выполняются в глобальной области, поэтому вы
непреднамеренно определяют глобальную переменную, которая может мешать другим
код. Однако необходимость использования global в цикле не является решением
Эта проблема.

по отношению к длительному сеансу REPL:
Текущее поведение заменяет очень редкий и легко обнаруживаемый режим отказа.
для запуска онлайн-примера в REPL (вы пропустите копирование / вставку
начальное определение переменной перед циклом, и уже есть
та же переменная, определенная глобально из чего-то предыдущего), но не
может правильно запустить онлайн-пример, если он является частью функции
(не добавляя глобальный везде), и не решая проблему, если она
нет (если global уже присутствует в онлайн-коде, вы все равно будете использовать
неверное значение в уже существующей глобальной переменной)

Я должен был настроиться на это раньше, но после краткого беспокойства все, кажется, хорошо.

На самом деле мы никогда полностью не поддерживали копирование и вставку кода из функции построчно в REPL ... Первое, что приходит на ум, - это режим REPL для построчной отладки функций.

Действительно, Rebugger (а это именно так) работает правильно на 1.0 только потому, что ему не хватает устаревшей области видимости 0.7, и его никогда нельзя заставить работать на 0.6. Однако мне приятно убедиться, что SoftGlobalScope.jl, похоже, не нарушает этого. Например, если вы достаточно глубоко войдете в show([1,2,4]) вы получите:

show_delim_array(io::IO, itr::Union{SimpleVector, AbstractArray}, op, delim, cl, delim_one) in Base at show.jl:649
  io = IOContext(Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting))
  itr = [1, 2, 4]
  op = [
  delim = ,
  cl = ]
  delim_one = false
  i1 = 1
  l = 3
rebug> eval(softscope(Main, :(<strong i="10">@eval</strong> Base let (io, itr, op, delim, cl, delim_one, i1, l) = Main.Rebugger.getstored("bbf69398-aac5-11e8-1427-0158b103a88c")
       begin
           print(io, op)
           if !(show_circular(io, itr))
               recur_io = IOContext(io, :SHOWN_SET => itr)
               if !(haskey(io, :compact))
                   recur_io = IOContext(recur_io, :compact => true)
               end
               first = true
               i = i1
               if l >= i1
                   while true
                       if !(isassigned(itr, i))
                           print(io, undef_ref_str)
                       else
                           x = itr[i]
                           show(recur_io, x)
                       end
                       i += 1
                       if i > l
                           delim_one && (first && print(io, delim))
                           break
                       end
                       first = false
                       print(io, delim)
                       print(io, ' ')
                   end
               end
           end
           print(io, cl)
       end
       end)))
[1, 2, 4]

Так что он отлично работает на 1.0 (с softscope или без него). На 0.7 оценка этого (с или без softscope ) даст

┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
[ERROR: invalid redefinition of constant first
Stacktrace:
 [1] top-level scope at ./REBUG:9 [inlined]
 [2] top-level scope at ./none:0
 [3] eval(::Module, ::Any) at ./boot.jl:319
 [4] top-level scope at none:0
 [5] eval at ./boot.jl:319 [inlined]
 [6] eval(::Expr) at ./client.jl:399
 [7] top-level scope at none:0

Так что 0.7 / 1.0 определенно шаг вперед, и если softscope упрощает некоторые вещи, не нарушая важных функций, это прекрасно.

Поэтому самая большая проблема заключается в том, как правильно перехватить это, не запуская другие пакеты (https://github.com/stevengj/SoftGlobalScope.jl/issues/2).

@timholy , SoftScope не затрагивает аргументы вызовов макросов (поскольку нет способа узнать, как макрос будет его перезаписывать), поэтому :(<strong i="6">@eval</strong> ...) защищен.

кажется, расходится с тем, "Был ли
внешняя локальная переменная существует, по замыслу, не зависит от порядка
появление или выполнение выражений во внешней локальной области ".

(Внешняя) локальная переменная a существует, но еще не назначена. Если цикл попытается присвоить a перед его чтением, назначение также будет видно снаружи.

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

Каковы временные рамки этого? Похоже, это значительно улучшило бы удобство использования для пользователя. И в это «критическое» время Джулии с выходом 1.0 было бы выгодно получить это исправление как можно скорее (способом, предложенным Джеффом выше) и пометить новую версию Джулии или версию REPL. (Извините за этот комментарий кресла, так как я, конечно, не буду это исправлять!)

@JeffBezanson
Я собирался возразить, что, хотя это верно (для реализации / компилятора), наивный программист julia не может не видеть другого поведения по сравнению с его более простой концептуальной моделью (переменная начинает существовать в момент ее определения). К сожалению, вы правы, следующий код не выдаст ошибку, но он выдаст ошибку, если вы оставите a = 2 в конце
функциональный тест ()
для i = 1:10
а = 1
конец
println (а)
а = 2
конец
Я объясню, к сожалению: я могу понять поведение (потому что раньше я работал с компилируемыми языками), но все же нахожу его запутанным и неожиданным. Как же плохо это должно быть для человека, имеющего только опыт написания сценариев или новичка в программировании. Также я нашел какой-то код, который показывает поведение, я не вижу полезного приложения (может, вы мне в этом поможете)

В REPL:
Я просто еще больше убедился, что изменение области видимости обратно на "нормальное", по крайней мере, в REPL (нет необходимости добавлять глобальные циклы в циклы) имеет высокий приоритет: сегодня я тестировал некоторые вещи в REPL и (снова) меня это укусило, нужно время, чтобы осознать это. Учитывая, что я уже какое-то время слежу за Джулией, мне очень нравится, что я даже слежу за этой веткой о проблеме, я бы даже назвал это демонстрацией: новичок (к Джулии), тестирующий язык, скорее всего, не найдет решить проблему и просто сдаться.

Мы с @jeffbezanson находимся в долгожданном отпуске (мне не следовало это читать). Мы можем решить, что делать примерно через неделю.

@derijkp , хотя обратная связь приветствуется, правила определения

@derijkp Короткий ответ: я думаю, проще, если область видимости переменной соответствует некоторой блочной конструкции (например, телу функции или цикла). С вашим предложением область видимости переменной будет представлять собой некоторое подмножество блока, что, на мой взгляд, в конечном итоге более сложное и запутанное - вы не можете указать синтаксическую форму, которая соответствует области действия переменной.

Да, я могу поверить, что это не соответствует интуиции некоторых людей. Но вы можете оптимизировать только первые десять минут использования языка до определенного момента. Реальный вопрос заключается в том, насколько сложно научить / изучить, как это работает, и какой дизайн сэкономит время в долгосрочной перспективе (за счет упрощения языка, облегчения разработки инструментов и т. Д.)?

(в соответствии с большей частью сказанного выше об изменении поведения REPL)
Я бы хотел, чтобы REPL не приводил к этому вопросу о переполнении стека.
и скорее будет лучше, так как многие новые глаза смотрят на Юлию

Я согласен ... И также думаю, что правила области видимости не обязательно должны меняться, просто все интерактивные интерфейсы (т.е. входят элементы управления REPL, Jupyter и Juno)

Речь идет не только о том, чтобы новички изучали новое правило. Если вы не можете копировать и вставлять фрагменты кода в REPL, jupyter и т. Д., А также в функции, это также сильно раздражает программистов среднего уровня.

Конечно, я также согласен с другими плакатами ... с новичками они собираются брать фрагменты кода, которые они видят внутри функций, копировать I в сценарии и полностью сбивать с толку, когда он не ведет себя так же, как при копировании внутри функции. , в juno, the repl и jupyter. Будет 100 вопросов об обмене стеком, которые сводятся к одной и той же проблеме. Программисты среднего уровня получат всевозможные самодельные решения с упаковкой в ​​блоки let и т. Д., Что еще больше запутает ситуацию.

Будет 100 вопросов об обмене стеком, которые сводятся к одной и той же проблеме. Программисты среднего уровня получат всевозможные самодельные решения с упаковкой в ​​блоки let и т. Д., Что еще больше запутает ситуацию.

Возможно, но на данном этапе это гипотетически (также OP связанного вопроса спрашивает об обосновании правила масштабирования, а не о том, чтобы запутаться в нем).

Кроме того, хотя я уважаю преподавательский опыт всех, кого это беспокоит, время покажет, окажется ли это важным в классе.

спрашивающий, похоже, был сбит с толку: «Интересно, интуитивно ли это понятно для начинающих пользователей julia. Для меня это не было интуитивно понятно…»

вопрошающий, похоже, был сбит с толку:

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

Возможно, но на данном этапе это гипотетически

Я уже ответил на несколько вопросов, связанных с этим, в stackoverflow, в основном от новых пользователей, и даже больше в реальной жизни (последний - вчера, от пользователя Matlab, который посчитал это недопустимым).

Будет 100 вопросов об обмене стеком, которые сводятся к одной и той же проблеме.

В свободное время я добавляю теги scope , scoping и global-variables к вопросам SE. Я останавливаюсь только из-за нехватки времени, а не потому, что его больше нет.

Заключение после долгого обсуждения, включая сортировку: мы собираемся включить что-то вроде SoftGlobalScope в Base и использовать его в REPL и во всех других контекстах интерактивной оценки. @JeffBezanson указал, что способ, которым это реализовано, на самом деле по сути такой же, как и ранее реализованный soft scope, так что до некоторой степени мы идем по кругу. Разница в том, что теперь в модулях или скриптах нет поведения области видимости, только в контекстах, подобных REPL. Я также думаю, что _объяснение_ мягкой области видимости как перезаписи исходного кода более ясно, чем пытаться различать жесткую и мягкую области видимости (я мог бы указать на это, как Джефф объяснил это никогда).

Эти два утверждения меня немного смущают, поскольку кажутся немного противоречащими друг другу:

и использовать его в REPL и во всех других контекстах интерактивной оценки.

в сценариях [...] нет поведения области видимости, только в контекстах, подобных REPL.

Означает ли это, что модуль Main иногда имеет мягкую область видимости (скажем, в приглашении REPL), а иногда и жесткую область действия (скажем, когда julia -L script.jl )? Разве не имеет смысла сказать, что Main всегда имеет мягкую область видимости? И модуль может подписаться на soft scope с помощью using SoftGlobalScope ?

(Я полагаю) правила области видимости не могут быть изменены в сценариях, потому что они были бы обратно несовместимы, то есть нарушили бы обещание, что любой код, написанный для 1.0, будет работать в любой версии 1. *. Вы правы, хотя та же проблема с областью видимости для REPL также применима к сценариям (наивный пользователь совершенно не понимает, почему его / ее код не работает должным образом при запуске в качестве сценария). Способ решить / облегчить эту проблему без серьезной несовместимости - это добавить в julia cmdline возможность использовать softscope (или альтернативу), например julia -f programfile, и показать эту опцию в любом описании / руководстве, которое, вероятно, новичок попадались.
Я также вижу потенциальную альтернативу для софтскопа, которая может иметь некоторые преимущества (хотя я, вероятно, упускаю из виду недостатки): что, если файл (вызываемый скрипт) всегда будет вводить свою собственную локальную область действия: правила области видимости будут полностью согласованы с правилами в функций, и с ожиданиями многих пользователей. Это также устранило бы множество проблем с производительностью новых пользователей:
Больше никаких ненужных глобальных переменных (глобальные переменные должны быть определены явно), и код может быть скомпилирован
(Сколько раз вам приходилось говорить, что нужно все поместить в функцию и избегать использования глобальных переменных?)

Я только что столкнулся с этим и был совершенно ошеломлен, если честно, никогда раньше не видел этого на любом другом языке. Я планирую ввести дополнительный курс Julia для продвинутых пользователей R в моем университете в конце этого года, когда все уляжется, и мои студенты получат это в день 0, когда начнут произвольно вводить что-то в REPL. И тот факт, что циклы for ведут себя иначе, if операторы

Я понимаю обещание обратной совместимости, которое было дано, но выполнение этой работы _, как ожидалось каждым человеком на планете, не имеющим отношения к CS (и большинством людей с CS, которых я подозреваю) _ кажется исправлением ошибки, а не проблемой обратной совместимости - мы не говорим что каждая ошибка будет воспроизводиться вечно, не так ли? Исправление REPL, очевидно, необходимо, поэтому здорово, что вы предлагаете это, но затем вам нужно объяснить, что вы не можете скопировать скрипт в REPL и ожидать, что такое же поведение будет таким же плохим или хуже, чем исходная проблема.

Пожалуйста, пожалуйста, подумайте о том, чтобы рассматривать это как исправление ошибки и распространять его с помощью скриптов, а также REPL - даже если есть переключатель для перехода к «старому» поведению - и делать это как можно скорее в 1.0.1.

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

Я не думаю, что стоит рассматривать это как «исправление ошибки», потому что это нарушит договор стабильности 1.0. Однако мне кажется разумным использовать софтскоп для скриптов, запускаемых с julia -i (т.е. в "интерактивном" режиме).

(То есть будет флаг --softscope={yes|no} и по умолчанию он будет иметь значение isinteractive .)

Придется рассмотреть выбор режима скрипта.

В этом отношении для меня не безумие использовать по умолчанию --softscope=yes для любого "скрипта", то есть для julia foo.jl , и включать только "жесткие" правила области видимости для модулей и include (в этот момент вы действительно должны помещать большую часть кода в функции).

В этом отношении для меня не безумие использовать по умолчанию --softscope = yes для любого "скрипта",

Что. Другой, о котором стоит серьезно подумать, - это Юнона. Помните, что люди будут <shift-enter> через свой код для интерактивной разработки (особенно при работе с регрессионными тестами), а затем ожидать, что они смогут запустить тот же файл. Имеет ли значение, находится ли код в @testset или нет (что, как я думаю, может ввести область видимости)? Для пользователя было бы очень запутанным, если бы тот же текст изменился в @testset сравнению с не при использовании интеграции Atom, а также несовместим с ] test .

Мне кажется, что лучшим решением является то, что жесткая область видимости - это просто подписка, когда при любом другом использовании (включая include в сценариях) используется softscope если вы не укажете иное. .

отличается от любого другого языка на земле

Вы хотите написать var x = 0 чтобы ввести каждую переменную? Это тоже «исправит» это и будет больше похоже на другие языки.

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

Это не так. Вы не можете изменить язык, который хотите, просто назвав текущее поведение ошибкой.

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

Позвольте мне еще раз дать объяснение этому, которое может быть полезно для избежания мании, истерии и кровавой бойни, которые люди видят в классе:

"
У Джулии есть два типа переменных: локальные и глобальные. Переменные, которые вы вводите в REPL или на верхнем уровне, помимо чего-либо еще, являются глобальными. Переменные, введенные внутри функций и циклов, являются локальными. Обновление глобальных переменных в программе, как правило, плохо, поэтому, если вы находитесь внутри цикла или функции и хотите обновить глобальные переменные, вы должны явно указать на это, снова написав объявление global .
"

Возможно, это можно улучшить; предложения приветствуются. Я знаю, вы бы предпочли вообще не нуждаться в каких-либо объяснениях. Я понимаю. Но мне это не кажется таким уж плохим.

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

Я согласен. Для меня это похоже на головную боль обучения и общения.

Например, может иметь смысл только жесткая область видимости внутри модулей.

Я так понимаю: если бы у меня был короткий скрипт (не в модуле!) В файле .jl который я скопировал из записной книжки IJulia, то, если бы я запустил этот код либо в REPL напрямую, либо в shift- войдите в Juno, тогда он будет вести себя последовательно как soft-scope ... но если я скопирую его вместо блока module тогда он будет кричать мне о глобальных объектах? Но если я скопировал этот код внутри функций внутри модуля, он должен работать.

Если так, то это имеет смысл, очень понятно и логично. Скрипты верхнего уровня - это интерактивный интерфейс для исследования и т. Д., Но вы никогда не поместите такой код в модуль. Модули - это то, что вы должны наполнить функциями, которые очень тщательно считаются глобальными. Об этих правилах будет легко рассказать людям.

Вы хотите написать var x = 0, чтобы представить каждую переменную? Это тоже «исправит» это и будет больше похоже на другие языки.

Нет, не хочу! Но языки сценариев, в которых есть REPL, редко это делают (например, ruby, python, R, ...), они ведут себя как Julia v0.6.

У Джулии есть два типа переменных: локальные и глобальные. Переменные, которые вы вводите в REPL или на верхнем уровне, помимо чего-либо еще, являются глобальными. Переменные, введенные внутри функций и циклов, являются локальными. Обновление глобальных переменных в программе, как правило, плохо, поэтому, если вы находитесь внутри цикла или функции и хотите обновить глобальные переменные, вы должны четко указать на это, снова написав глобальное объявление.

Я полностью понимаю, о чем вы здесь говорите, и не буду (трогать дерево!) Снова эту ошибку. Но вся проблема, о которой я беспокоюсь, не во мне. Я обнаружил, что относительно легко ввести область видимости (не упоминая об этом напрямую), когда я объясняю, что переменные внутри функций не могут видеть переменные снаружи и наоборот (хотя это больше стремление, чем факт в R!), Потому что функции сами по себе уже являются относительно продвинутой концепцией. Но это происходит гораздо раньше на кривой обучения, где мы не хотим, чтобы что-то отдаленно столь же сложное, как область видимости, вторгалось в людей ...

Также обратите внимание, что это не просто «_ переменные, которые вы вводите в REPL или на верхнем уровне, вне чего-либо еще, являются глобальными_» и «_ переменные, введенные внутри функций и циклов, являются локальными_», это также и переменные в операторах if в REPL или верхний уровень является глобальным, но переменные в @testset являются локальными. В итоге мы оказались в кроличьей норе: «просто попробуй и попробуй сам, будь то локальный или глобальный, удачи».

Тем не менее, я согласен с @jlperla - предложение о том, что «только жесткая область видимости внутри модулей может иметь смысл», мне кажется совершенно нормальным! Модули - это снова достаточно продвинутая концепция ... если soft scope работает для REPL и скриптов, это абсолютно нормально.

мы не хотим, чтобы что-то даже отдаленно настолько сложное, как размах, вторгалось в людей ...
на верхнем уровне являются глобальными, но переменные в @testset являются локальными

Я пытаюсь понять, что, по моему мнению, для начального обучения достаточно простого описания глобального и локального - вам даже не нужно произносить слово «объем» (его вообще не бывает в моем объяснении выше). Когда вы просто показываете несколько простых выражений и циклов в REPL, вы не обучаете людей тестовым наборам, и вам не нужен исчерпывающий список поведения области видимости всего на языке.

Единственное, что я хочу сказать, это изменение не делает внезапной необходимости преподавать много деталей о языке заранее. Вы по-прежнему можете игнорировать подавляющее большинство вещей о областях, тестовых наборах и т. Д., И простой строки о глобальном и локальном будет достаточно.

и простой строки о глобальном и локальном должно хватить.

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

Проблема в том, что вам нужно научить студентов не только области, но и пониманию области, в которой они скопировали код, из которого они получили. Вы должны научить их, что если они копируют и вставляют код, который находится в stackexchange внутри функции или блока let, им необходимо просканировать его и найти, где добавить «глобальный», если они вставляют его в REPL или .jl файл. Но если они копируют этот код внутри функции или в блокнот Jupyter. они не должны. И если они найдут код внутри stackexchange или учебной страницы, в которой есть глобальные переменные, но они хотят скопировать и изменить этот код внутри своей собственной функции, тогда им нужно вырезать глобальные переменные.

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

В итоге мы оказались в кроличьей норе: «просто попробуй и попробуй сам, будь то локальный или глобальный, удачи».

Популярная викторина: в julia 0.6 это x global или local:

for i = 1:10
    x = i
end

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

Ребята, это обсуждение уже практически перестало быть продуктивным. Джефф очень хорошо знает, что прежнее поведение в REPL было хорошим. Как вы думаете, кто это разработал и реализовал в первую очередь? Мы уже взяли на себя обязательство изменить интерактивное поведение. Еще предстоит принять решение о том, является ли «сценарий» интерактивным или нет. Он звучит интерактивно, когда вы называете его «сценарием», но он звучит гораздо менее интерактивно, когда вы называете его «программой» - тем не менее, это одно и то же. Пожалуйста, отвечайте кратко и конструктивно и сосредоточьтесь на вещах, которые еще предстоит решить. Если есть комментарии, которые отличаются от этого, они могут быть скрыты, а цепочка может быть заблокирована.

Одна мысль, что у меня была, но мы отклонили ее как «слишком раздражающую» и «которая может заставить жителей вытащить вилы» заключалась в том, что в неинтерактивных контекстах мы могли потребовать local или global аннотация в "мягкой области". Это гарантирует, что код из модуля будет работать так же, если его вставить в REPL. Если бы мы применили это к «скриптам» / «программам», то то же самое было бы справедливо и для них.

Когда я впервые познакомился с Джулией (не так давно, и я пришел в основном из Фортрана), меня учили, что «Джулия компилируется и быстро на уровне функций, поэтому все, что должно быть эффективным, должно выполняться внутри функций. В основной «программе» он ведет себя как скриптовый язык ». Я нашел это достаточно справедливым, так как я не могу представить, чтобы кто-то делал что-то слишком требовательное к вычислениям, не понимая этого утверждения. Следовательно, если есть какая-либо жертва в производительности в основной программе для использования тех же обозначений и конструкций, что и в функциях, я считаю это полностью приемлемым, гораздо более приемлемым, чем попытки понять и обучить этим правилам области видимости и невозможность копировать и вставлять коды из одного места в другое.

Между прочим, я еще новичок в Джулии, выбрав ее, чтобы научить некоторых старшеклассников и студентов старших курсов некоторым основам моделирования физических систем. И я уже предполагаю, что эта проблема вернется к «нормальному» поведению предыдущих версий, потому что это вызывает у нас головную боль.

Этот разговор сейчас заблокирован, и только коммиттеры Julia могут комментировать.

@JeffBezanson , каков был бы план реализации семантики, которую вы предложили в этой беседе , первоначально только в REPL и в другом месте?

Похоже, вы планируете поместить это непосредственно в понижающий код ( julia-syntax.scm ), а не как переписывание синтаксиса ala SoftScope.jl? Или вы бы предпочли сначала переписать синтаксис (изменить SoftScope в соответствии с предложенным правилом и преобразовать его в stdlib) и отложить его включение в понижающий код для более позднего выпуска Julia?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги