Порядок оценки вводимых выражений с кортежами

Мои старые заметки по ML говорят, что

let (𝑣₁, … , 𝑣ₙ) = (𝑡₁, … , 𝑡ₙ) in 𝑡′

является синтаксическим сахаром для

(λ 𝑣ₙ. … (λ 𝑣₁. 𝑡′)𝑡₁ … )𝑡ₙ

и что

let (𝑣₁, 𝑣₂) = 𝑡 𝑡′ in 𝑡″

эквивалентно

let 𝑣 = 𝑡 𝑡′ in 
let 𝑣₂ = snd 𝑣 in 
let 𝑣₁ = fst 𝑣 in 
𝑡″

где

  • каждый 𝑣 (с индексом или без) обозначает переменную,
  • каждый 𝑡 (с нижним или верхним индексом или без него) обозначает термин, и
  • fst и snd доставляют первый и второй компоненты пары соответственно.

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

Связанный: stackoverflow.com/questions/43883010/…

coredump 03.01.2023 09:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
109
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Неважно, что это:

let 𝑣 = 𝑡 𝑡′ in 
let 𝑣₂ = snd 𝑣 in 
let 𝑣₁ = fst 𝑣 in 
𝑡″

Или:

let 𝑣 = 𝑡 𝑡′ in  
let 𝑣₁ = fst 𝑣 in
let 𝑣₂ = snd 𝑣 in 
𝑡″

Так как ни fst, ни snd не имеют побочных эффектов. Побочные эффекты могут существовать при оценке 𝑡 𝑡′, но это делается до того, как произойдет привязка let.

Кроме того, как в:

let (𝑣₁, 𝑣₂) = 𝑡 𝑡′ in 𝑡″

Ни 𝑣₁, ни 𝑣₂ не зависят от значения, связанного с другим, чтобы определить его значение, поэтому порядок, в котором они связаны, снова, по-видимому, не имеет значения.

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

Практический тест

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

# let f () = print_endline "f"; 1 in
let g () = print_endline "g"; 2 in
let h () = print_endline "h"; 3 in
let (a, b, c) = (f (), g (), h ()) in a + b + c;;
h
g
f
- : int = 6
# let f () = print_endline "f"; 1 in
let g () = print_endline "g"; 2 in
let h () = print_endline "h"; 3 in
let (c, b, a) = (h (), g(), f ()) in a + b + c;;
f
g
h
- : int = 6
# let f _ = print_endline "f"; 1 in
let g () = print_endline "g"; 2 in
let h () = print_endline "h"; 3 in
let a () = print_endline "a" in
let b () = print_endline "b" in
let (c, d, e) = (f [a (); b ()], g (), h ()) in
c + d + e;;
h
g
b
a
f
- : int = 6

В SML (SML/NJ v110.99.3) наблюдается обратное: вычисление выражений слева направо.

- let
=   fun f() = (print "f\n"; 1)
=   fun g() = (print "g\n"; 2)
=   fun h() = (print "h\n"; 3)
=   val (a, b, c) = (f(), g(), h())
= in
=  a + b + c
= end;
f
g
h
val it = 6 : int
- let
=   fun f() = (print "f\n"; 1)
=   fun g() = (print "g\n"; 2)
=   fun h() = (print "h\n"; 3)
=   val (c, b, a) = (h(), g(), f())
= in
=   a + b + c
= end;
h
g
f
val it = 6 : int
- let
=   fun f _ = (print "f\n"; 1)
=   fun g() = (print "g\n"; 2)
=   fun h() = (print "h\n"; 3)
=   fun a() = print "a\n"
=   fun b() = print "b\n"
=   val (c, d, e) = (f [a(), b()], g(), h())
= in
=   c + d + e
= end;
a
b
f
g
h
val it = 6 : int

Ух ты! Спасибо! Такой же порядок оценки кортежа справа налево в OCaML 4.11.1 и порядок оценки кортежа слева направо в SMLNJ 110.79. Есть идеи о том, предназначены ли эти два порядка оценки (справа налево для OCaML и слева направо для SMLNJ) для двух компиляторов, и должны ли эти порядок оценки быть универсальными для двух компиляторов?

Albert Nash 05.01.2023 02:15

Я также протестировал MosML 2.10 (от ppa.launchpad.net/kflarsen/mosml/ubuntu/pool/main/m/mosml/…) на ваших примерах и получил порядок оценки кортежа слева направо, такой же что касается SMLNJ.

Albert Nash 05.01.2023 02:30

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

jthulhu 05.01.2023 12:59

@jthulhu Первые два примера на процитированной вами веб-странице по-прежнему дают тот же результат с OCaML 4.11.1: я получаю «World!Hello» и «argument». Итак, предложение «Сегодня все реализации Objective CAML оценивают аргументы слева направо». это просто неправда сейчас.

Albert Nash 05.01.2023 19:33

@jthulhu Ваш «порядок оценки намеренно оставлен неуказанным», однако, поддерживается «В Objective CAML порядок оценки аргументов не указан». с указанной вами веб-страницы. Немного натянуто распространить неспецификацию порядка вычисления аргументов функции на построение кортежа.

Albert Nash 05.01.2023 19:36

@AlbertNash действительно; как уже говорилось, некоторые реализации были слева направо, но это не так, как это реализовано в настоящее время.

jthulhu 05.01.2023 20:29

Имейте в виду, что в OCaml из-за (ослабления the) ограничения значения let a = b in c не эквивалентно (fun a -> c)b. Контрпример

# let id = fun x -> x in id 5, id 'a';;
- : int * char = (5, 'a')
# (fun id -> id 5, id 'a')(fun x -> x)
Error: This expression has type char but an expression was expected of type int
#

Это означает, что они семантически не являются одной и той же конструкцией (let ... = ... in ... строго более общая, чем другая).

Это происходит потому, что, как правило, система типов OCaml не допускает типы формы (∀α.α→α) → int * char (поскольку их разрешение сделало бы ввод текста неразрешимым, что не очень практично), который был бы типом fun id -> id 5, id 'a'. Вместо этого он прибегает к менее общему типу ∀α.(α→α) → α * α, который не делает проверку типов, потому что вы не можете унифицировать как α с char, так и с int.

@AlbertNash Я не слишком уверен в том, где вы видите опечатку, но, действительно, хотя люди обычно думают, что let a = b in c определяется как (λa. c) b, в OCaml это не так. Однако обратите внимание, что хотя эти две конструкции и не являются строго равными, они все же очень похожи: просто иногда OCaml не может проверить тип формы лямбда-оценки, но может проверить тип другой. Кроме того, на практике ни один язык программирования фактически не реализует привязки как оценку функции по соображениям производительности.

jthulhu 05.01.2023 12:54

Разница между let 𝑎 = 𝑏 in 𝑐 и (fun 𝑎 -> 𝑐) 𝑏 заключается в проверке типов, а не в β-редукции (которая не зависит или, по крайней мере, должна быть независима от типов).

Albert Nash 05.01.2023 19:39

@AlbertNash ах да, я увидел опечатку, я исправил ее.

jthulhu 05.01.2023 20:32

Другие вопросы по теме