Оператор для отступа

hindent изменил мой код на:

do download i inputFile
   onException
     (callProcess (List.head args) (List.tail args))
     (removeFileIfExists name)
   `finally` removeFileIfExists inputFile

Я не могу определить, относится ли finally к остальной части блока do или только к началу состояния onException. Согласно этому,

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

Я не уверен, что это правило применимо здесь.

Применяется ли `finally` к остальным действиям или только к последнему утверждению и почему?


person Matt Joiner    schedule 26.02.2018    source источник
comment
Наконец, имеет (как и все операторы) более низкий приоритет, чем функции, поэтому это finally (onException (callProcess ...) (removeFileIfExists name)) (removeFileIfExists inputFile). Таким образом, finally является основной функцией.   -  person Willem Van Onsem    schedule 26.02.2018


Ответы (2)


Мы можем узнать, используя GHCI: написание

Prelude> let f = (>>)
Prelude> :{
Prelude| do print 5
Prelude|    print 4
Prelude|    `f` print 3
Prelude|    print 2
Prelude| :}

вызывает следующую ошибку типа (не ошибку синтаксического анализа!)

<interactive>:12:8: error:
    • Couldn't match expected type ‘(() -> IO ()) -> Integer -> IO b’
                  with actual type ‘IO ()’
    • The function ‘print’ is applied to three arguments,
      but its type ‘Integer -> IO ()’ has only one
      In the second argument of ‘f’, namely ‘print 3 print 2’
      In the expression:
        do { print 5;
             print 4 }
        `f` print 3 print 2

Глядя на строки списка, мы обнаруживаем, как GHCi анализировал код, который печатается с явными фигурными скобками и точками с запятой.

Там мы видим, что часть `f` закрывает блок do! Это делает весь блок do первым аргументом f. Далее, следующие строки, уже не находящиеся в блоке, теперь образуют единое выражение print 4 print 2, которое используется как второй аргумент для f. Это вызывает ошибку типа, поскольку вызывает print с тремя аргументами.

Действительно, фигурная скобка } была вставлена ​​перед `f` из-за правила, упомянутого в OP: когда что-то не анализируется в блоке, мы добавляем } и продолжаем.

Подводя итог, если `f` имеет отступ больше, блок анализируется как

do print 5
   print 4 `f` print 3
   print 2

Если `f` имеет отступ как от предыдущей строки или меньше, блок анализируется как

(do { print 5
    ; print 4 }) `f` print 3 print 2

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

person chi    schedule 26.02.2018

Следующее на самом деле относится не к коду, который вы разместили, а к вопросу, который вы, кажется, задали. Вместо этого он обращается к следующему коду:

do download i inputFile
   onException
     (callProcess (List.head args) (List.tail args))
     (removeFileIfExists name)
   ░`finally` removeFileIfExists inputFile

ответ chi относится к фактически опубликованному коду, где `finally` не имеет отступа дальше download и onException. Я бы заметил, что это плохой стиль: при написании этого обязательно делайте отступ меньше, т.е.

  do download i inputFile
     onException
       (callProcess (List.head args) (List.tail args))
       (removeFileIfExists name)
 `finally` removeFileIfExists inputFile

Как заметил Виллем Ван Онсем, функциональные приложения всегда имеют приоритет перед инфиксными приложениями. Это верно как для истинных инфиксных операторов, таких как + или >>=, так и для обратных инфиксов... если на самом деле между этими типами операций нет четкого различия в том, что касается приоритета: для всех инфиксов приоритет определяется объявление фиксации, например

infixl 6 +
infix 4 `elem`

Фиксация — это число в диапазоне от 0 до 9, а фиксированность может быть либо лево-право-, либо неассоциативной. Если фиксированность не объявлена ​​(как для большинства именованных функций, в то время как для символов-инфиксов настоятельно рекомендуется указать фиксированность), используется значение по умолчанию infixl 9, т. е., по сути, максимально допустимая фиксированность, которую вы можете назначить вручную.

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

do (download i inputFile)
   (  (onException
        (callProcess (List.head args) (List.tail args))
        (removeFileIfExists name))
    `finally`
      (removeFileIfExists inputFile) )
person leftaroundabout    schedule 26.02.2018
comment
Если я уменьшу `` finally ``, теперь это будет относиться ко всему блоку do? - person Matt Joiner; 26.02.2018
comment
Вы имеете в виду, если вы уменьшите его отступ, чтобы он начинался слева от download и onException? Да, это сделает его синтаксическим анализом как (do {...}) `finally` (...). - person leftaroundabout; 26.02.2018
comment
На самом деле это не решает вопрос о том, как оператор взаимодействует с конструкцией do. - person luqui; 27.02.2018
comment
Совершенно верно, я отвлекся. Тем не менее, оставив этот ответ здесь, с заголовком сверху, относящимся к другому. - person leftaroundabout; 27.02.2018