Lưu ý rằng :sprintkhông không giảm một biểu thức để WHNF. Nếu có, thì sau đây sẽ cung cấp 4thay vì _:
Prelude> let four = 2 + 2 :: Int
Prelude> :sprint four
four = _
Thay vào đó, :sprintlấy tên của một ràng buộc, đi qua biểu diễn bên trong của giá trị của ràng buộc và hiển thị "các phần được đánh giá" (nghĩa là các phần là các hàm tạo) trong khi sử dụng _như một trình giữ chỗ cho các hàm không được đánh giá (nghĩa là hàm lười biếng bị treo các cuộc gọi). Nếu giá trị hoàn toàn không được đánh giá, sẽ không có đánh giá nào được thực hiện, thậm chí không phải với WHNF. (Và nếu giá trị được đánh giá hoàn toàn, bạn sẽ nhận được giá trị đó, không chỉ WHNF.)
Những gì bạn đang quan sát trong các thử nghiệm của mình là sự kết hợp của các kiểu số đa hình và đơn hình, các cách biểu diễn bên trong khác nhau cho các chuỗi ký tự so với các danh sách rõ ràng của các ký tự, v.v. Vì vậy, việc diễn giải các chi tiết triển khai này có liên quan đến WHNF sẽ khiến bạn bối rối. Nói chung, bạn chỉ nên sử dụng :sprintnhư một công cụ gỡ lỗi, không phải là cách để tìm hiểu về WHNF và ngữ nghĩa của đánh giá Haskell.
Nếu bạn thực sự muốn hiểu những gì :sprintđang làm, bạn có thể bật một vài cờ trong GHCi để xem cách các biểu thức thực sự được xử lý và, do đó, cuối cùng được biên dịch thành mã byte:
> :set -ddump-simpl -dsuppress-all -dsuppress-uniques
Sau này, chúng ta có thể thấy lý do bạn intlistđưa ra _:
> let intlist = [[1,2],[2,3]]
==================== Simplified expression ====================
returnIO
(: ((\ @ a $dNum ->
: (: (fromInteger $dNum 1) (: (fromInteger $dNum 2) []))
(: (: (fromInteger $dNum 2) (: (fromInteger $dNum 3) [])) []))
`cast` <Co:10>)
[])
Bạn có thể bỏ qua cuộc gọi returnIObên ngoài :và tập trung vào phần bắt đầu bằng((\ @ a $dNum -> ...
Đây $dNumlà từ điển cho các Numràng buộc. Điều này có nghĩa là mã được tạo chưa giải quyết loại thực tế atrong loại Num a => [[a]], do đó toàn bộ biểu thức vẫn được biểu diễn dưới dạng một lệnh gọi hàm lấy (từ điển cho) một Numloại thích hợp . Nói cách khác, đó là một thunk không được đánh giá cao và chúng tôi nhận được:
> :sprint intlist
_
Mặt khác, chỉ định loại là Intvà mã hoàn toàn khác nhau:
> let intlist = [[1::Int,2],[2,3]]
==================== Simplified expression ====================
returnIO
(: ((: (: (I# 1#) (: (I# 2#) []))
(: (: (I# 2#) (: (I# 3#) [])) []))
`cast` <Co:6>)
[])
và :sprintđầu ra cũng vậy:
> :sprint intlist
intlist = [[1,2],[2,3]]
Tương tự, các chuỗi ký tự và danh sách các ký tự rõ ràng có các cách biểu diễn hoàn toàn khác nhau:
> let stringlist = ["hi", "there"]
==================== Simplified expression ====================
returnIO
(: ((: (unpackCString# "hi"#) (: (unpackCString# "there"#) []))
`cast` <Co:6>)
[])
> let charlist = [['h','i'], ['t','h','e','r','e']]
==================== Simplified expression ====================
returnIO
(: ((: (: (C# 'h'#) (: (C# 'i'#) []))
(: (: (C# 't'#)
(: (C# 'h'#) (: (C# 'e'#) (: (C# 'r'#) (: (C# 'e'#) [])))))
[]))
`cast` <Co:6>)
[])
và sự khác biệt trong :sprintđầu ra đại diện cho các tạo phẩm trong đó các phần của biểu thức mà GHCi xem xét được đánh giá (các hàm tạo rõ ràng :) so với không đánh giá (các unpackCString#thunks).