2015-09-29 22 views
6

Khi tôi thực thi mãLine of OCaml sản xuất bí ẩn lỗi

let (a,p) = (2+2, Printf.printf) in p "abc"; p "%d" 3 ;; 

tôi mong đợi để xem kết quả abc3, nhưng được thay

File "f.ml", line 1, characters 46-47: 
Error: This function has type (unit, out_channel, unit) format -> unit 
     It is applied to too many arguments; maybe you forgot a `;'. 

Các phần thú vị là nếu tôi thay đổi 2+2-2 , nó chạy.

Tại sao mã tạo ra lỗi như vậy, nhưng không bị xóa với +2?

+0

Tôi cũng quan tâm đến câu trả lời, đây là dấu hiệu ban đầu prima lẻ. –

Trả lời

6

Kết hợp thủ thuật nhập đặc biệt của OCaml cho printf và tính đa hình giá trị.

Như bạn có thể đã biết, Printf.printf không mất string nhưng loại dữ liệu format. OCaml loại kiểm tra có một quy tắc đặc biệt để gõ các chữ chuỗi cho printf: nếu nó được gõ như format nếu bối cảnh yêu cầu:

# "%d";; 
- : string = "%d" 
# ("%d" : _format);; 
- : (int -> 'a, 'b, 'a) format = ... 

OCaml hệ thống kiểu có một trick gọi là giá trị đa hình (chính xác hơn, nó là giá trị thoải mái đa hình). Mục đích của nó là để loại biểu thức với các hiệu ứng phụ một cách chính xác. Tôi không giải thích chi tiết của nó nhưng nó hạn chế đa hình: một số hình thức của biểu thức được gọi là "mở rộng" không thể có các loại đa hình:

# fun x -> x;; 
- : 'a -> 'a = <fun> 
# (fun x -> x) (fun x -> x) 
- : '_a -> '_a = <fun> 

Ở phía trên, (fun x -> x) (fun x -> x) không có kiểu đa hình, trong khi chức năng nhận diện fun x -> x có. Điều này là do hình dạng của biểu thức của (fun x -> x) (fun x -> x): nó là "mở rộng". Biến kiểu lạ '_a là biến kiểu đơn biến: nó có thể được khởi tạo tới một số loại chỉ một lần. Mặt khác, các biến đa hình như 'a có thể được khởi tạo thành một loại khác nhau cho mỗi lần sử dụng vlaue.

Hãy quay trở lại với mã của bạn:

# let (a, p) = (2, Printf.printf);; 
val a : int 
val p : ('a, out_channel, unit) format -> 'a 

Ở đây, p có một kiểu đa hình ('a, out_channel, unit) format -> 'a. 'a có thể được khởi tạo thành nhiều loại do đó p "abc"; p "%d" 3 có thể đánh máy: loại đa hình có thể được khởi tạo thành (unit, out_channel, unit) format -> unit cho lần sử dụng đầu tiên p(int -> unit, out_channel, unit) format -> int -> unit cho lần sử dụng thứ hai là p.

Khi bạn thay đổi hằng số 2-2+2, đó là mở rộng, toàn bộ biểu hiện trở nên rộng quá, và những thay đổi gõ:

# let (a, p) = (2+2, Printf.printf);; 
val a : int 
val p : ('_a, out_channel, unit) format -> '_a 

Ở đây, p không có người còn đa hình biến 'a nhưng monomorphic '_a. Biến đơn biến đổi này được hợp nhất (tức thời) thành unit khi sử dụng lần đầu p và kết quả là loại p trở thành (unit, out_channel, unit) format -> unit. Nó có thể chỉ mất 1 đối số, do đó việc gõ lần sử dụng thứ hai là p với 2 đối số không thành công.

Một cách dễ dàng để tránh tình trạng này là để chia định nghĩa của bạn thành hai:

let a = 2 + 2 in 
let p = Printf.printf in 
p "abc"; p "%d" 3