Почему Tcl разрешает имена процессов с пробелами, но не параметры с пробелами?

#arguments #tcl #whitespace

#аргументы #tcl #пробел

Вопрос:

Просто для развлечения я написал следующий фрагмент кода:

 proc "bake a cake" {"number_of_people" {number_of_children}} {
    set fmt "Take %d pounds of flour and %d bags of marshmallows."
    puts [format $fmt "${number_of_people}" ${number_of_children}]
    puts "Put them into the oven and hope for the best."
}

"bake a cake" 3 3
{bake a cake} 5 0
  

Мне показалось забавным, что имена процедур могут содержать пробелы. Я подумал, что объединение этого с практически неиспользуемыми аргументами позволило бы сделать программы Tcl очень похожими на разговорный естественный язык, аналогично Smalltalk с его bakeACake forPeople: 3 andChildren: 3 , только без странного двоеточия, нарушающего предложения, и неестественного порядка слов.

Чтобы изучить эту идею дальше, я попробовал тот же шаблон для параметров процедуры, заменив каждый из них _ простым пробелом. tclsh8.6 это не понравилось:

 too many fields in argument specifier "number of people"
    (creating proc "bake a cake")
    invoked from within
"proc "bake a cake" {"number of people" {number of children}} {
        set fmt "Take %d pounds of flour and %d bags of marshmallows."
        puts [format $fmt "${n..."
    (file "bake.tcl" line 1)
  

Это вызвало следующие вопросы:

  • Есть ли убедительная причина, по которой имена процедур могут содержать пробелы, а имена параметров — нет?
  • Это просто деталь реализации proc ?
  • Можно ли было бы написать, spaceproc что допускает этот синтаксический вариант?

Ответ №1:

Внимательно прочитайте proc документацию: каждое из аргументов в списке аргументов само по себе является списком, который должен содержать 1 или 2 элемента: обязательное имя аргумента и необязательное значение по умолчанию. "number of people" содержит слишком много элементов. Вы можете получить то, что хотите, с помощью еще одного слоя фигурных скобок:

 % proc "bake a cake" {{"for people"} {"and children"}} {
    puts "baking a cake for [set {for people}] people and [set {and children}] children"
}
% "bake a cake" 1 2
baking a cake for 1 people and 2 children
% "bake a cake"
wrong # args: should be "{bake a cake} {for people} {and children}"
  

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

Обратите внимание, что получить код, похожий на Smalltalk, не так сложно

 % proc bakeACake {forPeople: nPeople andChildren: nChildren} {
    if {[set forPeople:] ne "forPeople:" || [set andChildren:] ne "andChildren:"} {
        error {should be "bakeACake forPeople: nPeople andChildren: nChildren"}
    }
    puts "baking a cake for $nPeople people and $nChildren children"
}
% bakeACake
wrong # args: should be "bakeACake forPeople: nPeople andChildren: nChildren"
% bakeACake foo 1 bar 2
should be "bakeACake forPeople: nPeople andChildren: nChildren"
% bakeACake forPeople: 3 andChildren: 4
baking a cake for 3 people and 4 children
  

Хотя, в отличие от Smalltalk, у вас не может быть других команд, начинающихся с «bakeACake» (если вы не вникаете в «ансамбли пространств имен»)

Комментарии:

1. Вы могли бы использовать ${for people} … но, как и большинство программистов, мы ленивы.