Haskells Yếu hình thức bình thường


9

Tôi đã vấp phải một số điều khó chịu. Tôi biết rằng haskell hoạt động với hình thức bình thường đầu yếu (WHNF) và tôi biết đây là gì. Nhập mã sau vào ghci (Tôi đang sử dụng lệnh: sprint làm giảm biểu thức thành WHNF theo hiểu biết của tôi.):

let intlist = [[1,2],[2,3]]
:sprint intlist

mang lại intlist = _điều này hoàn toàn có ý nghĩa với tôi.

let stringlist = ["hi","there"]
:sprint stringlist 

cho stringlist = [_,_] này đã bối rối cho tôi. Nhưng sau đó:

let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist

đáng ngạc nhiên cho charlist = ["hi","there"]

Theo như tôi hiểu về Haskell, các chuỗi không có gì khác ngoài danh sách các ký tự, dường như được xác nhận bằng cách kiểm tra các loại "hi" :: [Char]['h','i'] :: [Char].

Tôi bối rối, bởi vì theo cách hiểu của tôi, cả ba ví dụ trên ít nhiều giống nhau (một danh sách các danh sách) và do đó nên giảm xuống cùng một WHNF, cụ thể là _. Tôi đang thiếu gì?

Cảm ơn


Điều này dường như có liên quan
Bergi

@Bergi những câu hỏi đó chắc chắn có liên quan, nhưng dường như không giải quyết được tại sao "bla"['b','l','a']sẽ xuất hiện khác nhau.
leftaroundabout

@leftaroundabout Vì "bla"có thể bị quá tải, nhưng ['b','l','a']được biết là a String/ [Char]?
Bergi

1
@Bergi Tôi cũng nghĩ về điều đó, nhưng nó không thực sự hợp lý vì cũng['b', 'l', 'a'] có thể bị quá tải , và tương tự như vậy "bla"chỉ là quá tải nếu -XOverloadedStringsđược bật.
leftaroundabout

2
Có vẻ liên quan đến trình phân tích cú pháp, có thể cụ thể đối với GHCi? (Tôi không biết cách bạn kiểm tra WHNF trong mã do GHC biên dịch.) Bản thân các trích dẫn dường như là trình kích hoạt.
chepner

Câu trả lời:


5

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>)
     [])

: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).

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.