LINUX.ORG.RU

Почему в Ocaml такое неочевидное поведение вложенных блоков?

 ,


0

2

Например:

let println x = print_int x ; print_newline ()
 
let proc0a () =
	print_endline "proc0a" ;
	let x = 1
	in
		println x ;
		let x = 2
		in
			println x ;
		println x
 
let proc0b () =
	print_endline "proc0b" ;
	let x = 1
	in begin
		println x ;
		let x = 2
		in begin
			println x
		end ;
		println x
	end
 
let proc0c () =
	print_endline "proc0c" ;
	let x = 1
	in (
		println x ;
		let x = 2
		in (
			println x
		) ;
		println x
	)
 
let proc1 () =
	print_endline "proc1" ;
	let x = 1
	in
		println x ;
		( let x = 2
		in
			println x
		) ;
		println x
 
let proc p =
	p () ;
	print_newline ()
 
let _ = List.iter proc [ proc0a; proc0b; proc0c; proc1 ]

получаем:

proc0a
1
2
2

proc0b
1
2
2

proc0c
1
2
2

proc1
1
2
1

Я ожидал от proc0(a|b|c) поведения аналогичного proc1, но нет.

В SML поведение соответствует ожиданиям:

fun println x = print (Int.toString x ^ "\n")
 
fun proc0 () = (
	print "proc0\n" ;
	let val x = 1
	in
		println x ;
		let val x = 2
		in
			println x
		end ;
		println x
	end
)
 
val _ = proc0 ()

=>

proc0c
1
2
1

Как и в Haskell:

proc0a = do
	putStrLn "proc0a"
	let x = 1 in do
		print x
		let x = 2 in do
			print x
		print x
 
proc0b = do {
	putStrLn "proc0b" ;
	let x = 1 in do {
		print x ;
		let x = 2 in do {
			print x
		} ;
		print x
	}
}
 
proc1a = do
	putStrLn "proc1a"
	let x = 1 in do
		print x
		let x = 2 in do
			print x
			print x
 
proc1b = do {
	putStrLn "proc1b" ;
	let x = 1 in do {
		print x ;
		let x = 2 in do {
			print x ;
			print x
		}
	}
}
 
main = mapM_ (\p -> p >> putStrLn "") [ proc0a, proc0b, proc1a, proc1b ]

=>

proc0a
1
2
1

proc0b
1
2
1

proc1a
1
2
2

proc1b
1
2
2

Как и в любом другом языке (может, в F#, как, во-многом, наследнике Ocaml, поведение аналогично предку? Лень проверять).

Так почему разработчики Ocaml сделали так, а не как у людей?

★★★★★

всё очевидно, ты гонишь.

anonymous ()

Ну, окамл как бы «более лучший» ML, некоторые вещи получились, некоторые не очень.

Virtuos86 ★★★★★ ()

В SML поведение соответствует ожиданиям:

Так в SML ты пишешь end, а в Ocaml ;; не пишешь. и отступы неправильно ставишь

let proc0a () =
        print_endline "proc0a" ;
	let x = 1 in
		println x ;
	let x = 2 in
		println x ;
		println x

Так очевидно, что вывод будет 1, 2, 2.

monk ★★★★★ ()
Ответ на: комментарий от monk

Так в SML ты пишешь end

Потому что в SML такой синтаксис let

let <bindings>
in
    <expr> ;
    ...
end

В Ocaml'е же просто

let <bindings>
in
    <expr> ;
    ...

Без всякого терминального символа.

а в Ocaml ;; не пишешь

Так ;; завершает top-level выражение, т.е. определение функции в данном случае.

и отступы неправильно ставишь

В смысле неправильно? Я ставлю отступы в соответствии с лексической структурой

(define (proc)
  (displayln "proc")
  (let ((x 1))
    (displayln x)
    (let ((x 2))
      (displayln x))
    (displayln x)))

Синтаксически они не имеют значения, хоть вообще без отступов напиши

let proc0a () =
print_endline "proc0a" ;
let x = 1 in
println x ;
let x = 2 in
println x ;
println x

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

print_endline "proc0" ;
( let x = 1 in
    println x ;
    ( let x = 2 in
        println x ;
        println x
    )
)

а не как

print_endline "proc0" ;
( let x = 1 in
    println x ;
    ( let x = 2 in
        println x
    ) ;
    println x
)

Т.е. описать вложенный let нормальным способом нельзя, только явно оборачивая его в скобки (или begin … end), как в proc1.

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

let proc2 () =
	print_endline "proc2" ;
	for x = 1 to 1 do
		println x ;
		for x = 2 to 2 do
			println x
		done ;
		println x
	done

=>

proc2
1
2
1

В свою очередь if … then … else … принимает в качестве then- и else-выражений только атомарные выражения, а не неявные последовательности, как let … in … и for … do … done, поэтому тоже всё работает ожидаемо.

Зачем сделали такой let … in с неявной хвостовой последовательностью?

korvin_ ★★★★★ ()
Ответ на: комментарий от korvin_

В смысле неправильно? Я ставлю отступы в соответствии с лексической структурой

Так как let ограничен только концом top-level, то он не создаёт вложенности

(define (proc)
  (displayln "proc")
  (define x 1)
  (displayln x)
  (define x 2)
  (displayln x)
  (displayln x))
monk ★★★★★ ()
Ответ на: комментарий от korvin_

Зачем сделали такой let … in с неявной хвостовой последовательностью?

Затем же, зачем в Scheme сделали define. Чтобы он не создавал уровня вложенности.

monk ★★★★★ ()
Ответ на: комментарий от monk

Но тогда логичней было его и синтаксически сделать таким

let proc () =
  print_endline "proc" ;
  let x = 1 ;
  println x ;
  let x = 2 ;
  println x ;
  println x
korvin_ ★★★★★ ()
Ответ на: комментарий от korvin_

Но тогда логичней было его и синтаксически сделать таким

Нельзя. ";" — это операция над функциями с типом unit -> 'b -> 'b (примерно как в Haskell функция seq). «let x = 1» никак не может быть функцией.

monk ★★★★★ ()

Ты неправильно расставляешь отступы, прогони ocamlformat и все станет ясно. Вообще неканоничный indentation, и никто не переносит in, должно быть

let proc_a () =
   print_endline "proc_a";
   let x = 1 in
   println x;
   let x = 2 in
   println x;
   println x
Freyr69 ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.