Dấu chấm than có ý nghĩa gì trong một tuyên bố Haskell?


262

Tôi đã bắt gặp định nghĩa sau khi tôi cố gắng học Haskell bằng cách sử dụng một dự án thực tế để lái nó. Tôi không hiểu dấu chấm than trước mỗi đối số có nghĩa là gì và sách của tôi dường như không đề cập đến nó.

data MidiMessage = MidiMessage !Int !MidiMessage

13
Tôi nghi ngờ rằng đây có thể là một câu hỏi rất phổ biến; Tôi nhớ rõ ràng tự hỏi về chính xác điều tương tự, cách trở lại khi.
cjs

Câu trả lời:


314

Đó là một tuyên bố nghiêm ngặt. Về cơ bản, điều đó có nghĩa là nó phải được ước tính theo cái gọi là "dạng bình thường đầu yếu" khi giá trị cấu trúc dữ liệu được tạo. Hãy xem xét một ví dụ, để chúng ta có thể thấy điều này có nghĩa là gì:

data Foo = Foo Int Int !Int !(Maybe Int)

f = Foo (2+2) (3+3) (4+4) (Just (5+5))

Hàm ftrên, khi được đánh giá, sẽ trả về một "thunk": đó là mã để thực thi để tìm ra giá trị của nó. Tại thời điểm đó, một Foo thậm chí chưa tồn tại, chỉ là mã.

Nhưng đến một lúc nào đó, ai đó có thể cố gắng nhìn vào bên trong nó, có thể thông qua một trận đấu mẫu:

case f of
     Foo 0 _ _ _ -> "first arg is zero"
     _           -> "first arge is something else"

Điều này sẽ thực thi đủ mã để làm những gì nó cần, và không còn nữa. Vì vậy, nó sẽ tạo ra một Foo với bốn tham số (vì bạn không thể nhìn vào bên trong mà không có nó). Đầu tiên, vì chúng tôi đang thử nghiệm nó, chúng tôi cần đánh giá tất cả các cách để 4chúng tôi nhận ra nó không phù hợp.

Thứ hai không cần phải được đánh giá, bởi vì chúng tôi không kiểm tra nó. Do đó, thay vì 6được lưu trữ ở vị trí bộ nhớ đó, chúng tôi sẽ chỉ lưu trữ mã để đánh giá sau này (3+3). Điều đó sẽ biến thành số 6 chỉ khi ai đó nhìn vào nó.

Tuy nhiên, tham số thứ ba có !phía trước nó, do đó được đánh giá nghiêm ngặt: (4+4)được thực thi và 8được lưu trữ ở vị trí bộ nhớ đó.

Tham số thứ tư cũng được đánh giá nghiêm ngặt. Nhưng đây là nơi có một chút khó khăn: chúng tôi đang đánh giá không đầy đủ, mà chỉ ở dạng đầu yếu thông thường. Điều này có nghĩa là chúng tôi tìm ra liệu nó Nothinghay Justcái gì đó, và lưu trữ nó, nhưng chúng tôi không đi xa hơn. Điều đó có nghĩa là chúng tôi lưu trữ không Just 10nhưng thực sự Just (5+5), để lại thunk bên trong không có giá trị. Điều này rất quan trọng để biết, mặc dù tôi nghĩ rằng tất cả các tác động của điều này đi ra ngoài phạm vi của câu hỏi này.

Bạn có thể chú thích các đối số hàm theo cùng một cách, nếu bạn bật BangPatternstiện ích mở rộng ngôn ngữ:

f x !y = x*y

f (1+1) (2+2)sẽ trả lại thunk (1+1)*4.


16
Điều này rất hữu ích. Tuy nhiên, tôi không thể tự hỏi liệu thuật ngữ Haskell có khiến mọi người hiểu phức tạp hơn với các thuật ngữ như "dạng đầu bình thường yếu", "nghiêm ngặt" hay không. Nếu tôi hiểu bạn một cách chính xác, nó có vẻ như! toán tử chỉ đơn giản là lưu trữ giá trị được ước tính của một biểu thức thay vì lưu trữ một khối ẩn danh để đánh giá nó sau này. Đó có phải là một giải thích hợp lý hoặc có một cái gì đó nhiều hơn cho nó?
David

71
@David Câu hỏi là đánh giá giá trị bao xa. Hình thức bình thường đầu yếu có nghĩa là: đánh giá nó cho đến khi bạn đạt đến hàm tạo ngoài cùng. Hình thức bình thường có nghĩa là đánh giá toàn bộ giá trị cho đến khi không còn thành phần nào được đánh giá. Bởi vì Haskell cho phép tất cả các loại độ sâu đánh giá, nó có một thuật ngữ phong phú để mô tả điều này. Bạn không có xu hướng tìm thấy những khác biệt này trong các ngôn ngữ chỉ hỗ trợ ngữ nghĩa theo giá trị.
Don Stewart

8
@David: Tôi đã viết một lời giải thích sâu hơn ở đây: Haskell: Weak Head Form Form là gì? . Mặc dù tôi không đề cập đến các mẫu bang hoặc chú thích nghiêm ngặt, chúng tương đương với việc sử dụng seq.
hammar

1
Chỉ để chắc chắn rằng tôi hiểu: sự lười biếng đó chỉ liên quan đến một ẩn số tại các đối số thời gian biên dịch? Tức là nếu mã nguồn thực sự có câu lệnh (2 + 2) , liệu nó có còn được tối ưu hóa thành 4 thay vì có mã để thêm các số đã biết không?
Hi-Angel

1
Hi-Angel, điều đó thực sự phụ thuộc vào việc trình biên dịch muốn tối ưu hóa đến mức nào. Mặc dù chúng tôi sử dụng tóc mái để nói rằng chúng tôi muốn buộc một số thứ nhất định thành dạng đầu yếu thông thường, chúng tôi không có (và cũng không cần hoặc không muốn) một cách để nói "vui lòng đảm bảo rằng bạn chưa đánh giá cái này bước xa hơn. " Từ quan điểm ngữ nghĩa, được đưa ra một "n = 2 + 2", bạn thậm chí không thể biết liệu có bất kỳ tham chiếu cụ thể nào đến n đang được đánh giá bây giờ hay đã được đánh giá trước đó.
cjs

92

Một cách đơn giản để thấy sự khác biệt giữa các đối số hàm tạo nghiêm ngặt và không nghiêm ngặt là cách chúng hành xử khi chúng không được xác định. Được

data Foo = Foo Int !Int

first (Foo x _) = x
second (Foo _ y) = y

Vì đối số không nghiêm ngặt không được đánh giá bởi second, việc chuyển vào undefinedkhông gây ra vấn đề:

> second (Foo undefined 1)
1

Nhưng đối số chặt chẽ không thể undefined, ngay cả khi chúng ta không sử dụng giá trị:

> first (Foo 1 undefined)
*** Exception: Prelude.undefined

2
Điều này tốt hơn câu trả lời của Curt Sampson vì nó thực sự giải thích các hiệu ứng có thể quan sát được của người dùng mà !biểu tượng có, thay vì đi sâu vào chi tiết triển khai nội bộ.
David Grayson

26

Tôi tin rằng đó là một chú thích nghiêm ngặt.

Haskell là một ngôn ngữ chức năng thuần túy và lười biếng , nhưng đôi khi chi phí của sự lười biếng có thể là quá nhiều hoặc lãng phí. Vì vậy, để giải quyết vấn đề đó, bạn có thể yêu cầu trình biên dịch đánh giá đầy đủ các đối số cho một hàm thay vì phân tích cú pháp xung quanh.

Có nhiều thông tin hơn trên trang này: Hiệu suất / Mức độ nghiêm ngặt .


Hmm, ví dụ trên trang mà bạn tham chiếu dường như nói về việc sử dụng! khi gọi một hàm. Nhưng điều đó có vẻ khác với việc đưa nó vào một tuyên bố loại. Tôi đang thiếu gì?
David

Tạo một thể hiện của một loại cũng là một biểu thức. Bạn có thể nghĩ về các hàm tạo kiểu như là các hàm trả về các thể hiện mới của kiểu đã chỉ định, được cung cấp các đối số được cung cấp.
Chris Vest

4
Trong thực tế, đối với tất cả các ý định và mục đích, các hàm tạo là các hàm; bạn có thể áp dụng một phần chúng, chuyển chúng cho các chức năng khác (ví dụ: map Just [1,2,3]để có được [Chỉ 1, Chỉ 2, Chỉ 3]), v.v. Tôi thấy hữu ích khi nghĩ về khả năng khớp mẫu với họ cũng như một cơ sở hoàn toàn không liên quan.
cjs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.