43

Haskell thường được tham chiếu như một ví dụ về ngôn ngữ thuần túy. Làm thế nào điều này có thể được biện minh cho sự tồn tại của System.IO.Unsafe.unsafePerformIO?Haskell có thực sự là một ngôn ngữ thuần túy có chức năng xem xét unsafePerformIO không?

Chỉnh sửa: Tôi nghĩ với "hoàn toàn chức năng", điều đó có nghĩa là không thể đưa mã không tinh khiết vào phần chức năng của chương trình.

+0

Về sửa đổi của bạn: Nhưng nó vẫn còn có thể tạo ra một thư viện chức năng có thể được gọi từ một chương trình bắt buộc. Cuối cùng, lập trình viên quyết định mức độ hiện diện của chức năng. –

+1

Không thể giới thiệu mã không tinh khiết, nó không phải là mặc định. Và ngôn ngữ đi đến độ dài đáng kể để ngăn cản nó. Tuy nhiên, mã không tinh khiết đôi khi hữu ích. Đặc biệt là khi ẩn an toàn sau một giao diện thuần túy. –

+5

Xem thêm http://stackoverflow.com/questions/1916692/are-side-effects-possible-in-pure-functional-programming và http://stackoverflow.com/questions/1916692/are-side-effects-possible -rong-thuần-chức năng-lập trình và http://stackoverflow.com/questions/2488646/why-are-side-effects-modeled-as-monads-in-haskell –

Trả lời

114

Các Ngôn ngữ Chúng tôi Gọi Haskell

unsafePerformIO là một phần của Chức năng Ngoại Giao diện đặc điểm kỹ thuật, chứ không phải cốt lõi Haskell 98 đặc điểm kỹ thuật. Nó có thể được sử dụng để làm các hiệu ứng phụ cục bộ không thoát khỏi phạm vi nào đó, để lộ ra một giao diện thuần túy có chức năng. Đó là, chúng tôi sử dụng nó để ẩn các hiệu ứng khi trình kiểm tra loại không thể thực hiện điều đó cho chúng ta (không giống như đơn vị ST, mà ẩn các hiệu ứng với một bảo đảm tĩnh).

Để minh họa chính xác nhiều ngôn ngữ mà chúng tôi gọi là "Haskell", hãy xem xét hình ảnh bên dưới. Mỗi vòng tương ứng với một tập hợp các tính năng tính toán cụ thể, được sắp xếp theo độ an toàn và với khu vực tương quan với sức mạnh biểu cảm (nghĩa là số lượng chương trình bạn có thể viết nếu bạn có tính năng đó).

Ngôn ngữ được gọi là Haskell 98 được chỉ định ngay ở giữa, thừa nhận toàn bộ và một phần chức năng. Agda (hoặc Epigram), nơi chỉ có tổng số chức năng được phép, thậm chí còn ít biểu cảm hơn, nhưng "tinh khiết hơn" và an toàn hơn. Trong khi Haskell như chúng ta sử dụng nó ngày nay bao gồm tất cả mọi thứ ra cho FFI, nơi unsafePerformIO sống. Tức là, bạn có thể viết bất kỳ thứ gì trong Haskell hiện đại, mặc dù nếu bạn sử dụng đồ vật từ vòng ngoài, sẽ khó thiết lập sự đảm bảo an toàn và bảo mật hơn bởi vòng bên trong.

alt text

Vì vậy, các chương trình Haskell không thường được xây dựng từ 100% mã referentially minh bạch, tuy nhiên, nó là chỉ ngôn ngữ vừa phổ biến mà là tinh khiết theo mặc định.

+0

Tôi có hiểu bạn một cách chính xác, rằng mọi người nên nói chính xác hơn "Haskell 98 là một ngôn ngữ chức năng thuần túy" và không phải "Haskell là một ngôn ngữ thuần túy chức năng"? –

+22

@Stefan Schmidt: giống như "Haskell * là * hoàn toàn có chức năng", nhưng "GHC là một triển khai của một bộ siêu Haskell bao gồm Giao diện chức năng nước ngoài không hoàn toàn chức năng" – yairchu

+1

Cảm ơn bạn vì đồ họa thú vị! –

4

Ngôn ngữ/triển khai hoàn toàn hoạt động. Nó bao gồm một vài "lối thoát hiểm", mà bạn không phải sử dụng nếu bạn không muốn.

1

Thật không may là ngôn ngữ phải làm một số công việc thực tế, và điều này ngụ ý nói chuyện với môi trường bên ngoài.

Điều tốt là bạn có thể (và nên) giới hạn việc sử dụng mã "ngoài kiểu" này đối với một số phần được ghi lại cụ thể trong chương trình của bạn.

+1

Thật không may? Tôi nói “may mắn thay” :) Chúng tôi không còn “tránh thành công bằng mọi giá” nữa. – Artyom

34

Tôi nghĩ với "hoàn toàn chức năng" nó đã có nghĩa rằng không thể để giới thiệu đang bất tịnh ...

Câu trả lời thực sự là unsafePerformIOkhông phần của Haskell, nhiều hơn bất kỳ nói rằng, bộ thu gom rác hoặc hệ thống thời gian chạy là một phần của Haskell. unsafePerformIO là có trong hệ thống để những người xây dựng hệ thống có thể tạo ra một trừu tượng chức năng thuần túy trên phần cứng rất hiệu quả. Tất cả các ngôn ngữ thực đều có sơ hở khiến cho các nhà xây dựng hệ thống có thể hoàn thành công việc theo những cách hiệu quả hơn việc giảm xuống mã C hoặc mã lắp ráp.

Đối với bức tranh rộng lớn hơn về cách các tác dụng phụ và I/O phù hợp với Haskell qua IO đơn nguyên, tôi nghĩ rằng cách dễ nhất để nghĩ về Haskell là nó là một ngôn ngữ thuần túy mà mô tả tính effectful. Khi tính toán được mô tả là main, hệ thống thời gian chạy thực hiện các hiệu ứng đó một cách trung thực.

unsafePerformIO là cách để có hiệu ứng theo cách không an toàn; nơi "không an toàn" có nghĩa là "sự an toàn phải được đảm bảo bởi người lập trình" — không có gì được trình biên dịch kiểm tra. Nếu bạn là một lập trình viên hiểu biết và sẵn sàng đáp ứng các nghĩa vụ bằng chứng nặng, bạn có thể sử dụng unsafePerformIO. Nhưng tại thời điểm đó bạn không lập trình trong Haskell nữa; bạn đang lập trình bằng một ngôn ngữ không an toàn trông rất giống Haskell.

+0

http://www.haskell.org/ghc/docs/6.12.2/html/libraries/ "Một phần của Haskell" trong sự hiểu biết của bạn là gì? –

+3

@StefanSchmidt: Điều gì đó đã được xác định trong tiêu chuẩn ngôn ngữ. Chỉ vì một cái gì đó là một phần của ghc, nó không phải là một phần của haskell nữa hơn bất cứ điều gì trong gcc là một phần của ngôn ngữ C. – sepp2k

+9

@Stefan: Đối với các câu hỏi như thế này, "Haskell" có nghĩa là tiêu chuẩn * Haskell 2010 *, hoặc có thể là tiêu chuẩn * Haskell 98 * do Nhà xuất bản Đại học Cambridge xuất bản năm 2003. Trong các ngữ cảnh khác, nhiều lập trình viên sử dụng từ "Haskell" để mô tả bất cứ điều gì GHC xảy ra để thực hiện tuần đó. –

0

Tôi có cảm giác mình sẽ rất không được ưa chuộng khi nói những gì tôi định nói, nhưng cảm thấy tôi phải trả lời một số thông tin (theo ý kiến ​​của tôi sai) được trình bày ở đây.

Mặc dù đúng là không an toànPerformIO đã được thêm chính thức vào ngôn ngữ như một phần của phụ lục FFI, lý do cho điều này phần lớn là lịch sử hơn là logic. Nó tồn tại không chính thức và được sử dụng rộng rãi lâu trước khi Haskell từng có FFI. Nó không bao giờ chính thức là một phần của tiêu chuẩn Haskell chính bởi vì, như bạn đã quan sát, nó chỉ là một sự bối rối. Tôi đoán hy vọng là nó sẽ biến mất vào một thời điểm nào đó trong tương lai, bằng cách nào đó. Điều đó đã không xảy ra, cũng không theo ý kiến ​​của tôi.

Việc phát triển phụ lục FFI cung cấp một lý do thuận tiện cho việc không an toànPerformIO để nhận được tiêu chuẩn ngôn ngữ chính thức vì nó có vẻ không quá tệ ở đây, khi so sánh thêm khả năng gọi mã nước ngoài (IE C) trong đó tất cả mọi phiên cược đều có liên quan đến việc đảm bảo độ tinh khiết và độ an toàn tĩnh). Cũng thuận tiện khi đặt nó ở đây vì những lý do chính trị cơ bản. Nó nuôi dưỡng huyền thoại rằng Haskell sẽ tinh khiết, nếu chỉ có nó không phải cho tất cả những hệ điều hành "được thiết kế kém" hoặc "được thiết kế kém" hoặc phần cứng "được thiết kế kém" hay .. bất cứ điều gì .. unsafePerformIO thường được sử dụng với mã liên quan đến FFI, nhưng lý do cho việc này thường liên quan đến thiết kế xấu của FFI và thực sự là bản thân Haskell, không phải thiết kế xấu của bất cứ điều gì Haskell đang thử giao diện. Vì vậy, như Norman Ramsey nói, vị trí chính thức cho rằng OK khi sử dụng không an toànPerformIO cung cấp một số nghĩa vụ chứng minh đã được thỏa mãn bởi bất kỳ ai sử dụng nó (chủ yếu là làm điều này không làm mất hiệu lực các biến đổi trình biên dịch quan trọng như nội tuyến và phụ thường dùng). -xóa loại trừ). Cho đến nay rất tốt, hoặc như vậy người ta có thể nghĩ. Các kicker thực sự là những nghĩa vụ bằng chứng không thể được đáp ứng bởi những gì có lẽ là trường hợp sử dụng phổ biến nhất cho unsafePerformIO, mà theo ước tính của tôi chiếm hơn 50% của tất cả các unsafePerformIOs ra khỏi đó trong tự nhiên. Tôi đang nói về các thành ngữ kinh khủng được gọi là "unsafePerformIO hack" được chứng minh (trên thực tế rõ ràng) hoàn toàn không an toàn (trong sự hiện diện của nội tuyến và cse).

Tôi thực sự không có thời gian, không gian hoặc độ nghiêng để đi vào những gì "unsafePerformIO hack" là hoặc tại sao nó cần trong thư viện IO thực, nhưng dòng dưới cùng là người làm việc trên cơ sở hạ tầng Haskells IO thường "mắc kẹt giữa một tảng đá và một nơi khó khăn". Họ có thể cung cấp API an toàn vốn không có triển khai an toàn (Haskell) hoặc có thể cung cấp API không an toàn vốn có thể được triển khai một cách an toàn, nhưng những gì họ hiếm khi làm là cung cấp an toàn trong cả hai thiết kế API .Đánh giá thông thường với "unsafePerformIO hack" xuất hiện trong mã thế giới thực (bao gồm cả các thư viện chuẩn Haskell), có vẻ như hầu hết chọn tùy chọn cũ là thấp hơn của hai tệ nạn, và hy vọng trình biên dịch sẽ không những thứ lên với nội tuyến, cse hoặc bất kỳ chuyển đổi nào khác.

Tôi ước tất cả điều này không phải như vậy. Tiếc là nó.

+3

Tôi muốn upvote câu trả lời của bạn, nhưng tôi nghĩ rằng đi vào những gì "unsafePerformIO hack" là hoặc tại sao nó cần thiết trong thư viện IO thực là những gì làm cho câu trả lời của bạn hữu ích. Hầu hết các câu trả lời của bạn chỉ là handwaving. –

+0

Tôi đoán rằng "không an toànPerformIO hack" giống như được mô tả trong http://www.haskell.org/haskellwiki/Top_level_mutable_state – hvr

2

Tôi không nghĩ rằng không an toànPerformIO có nghĩa là haskell bằng cách nào đó trở nên không tinh khiết. Bạn có thể tạo các hàm thuần túy (tham chiếu trong suốt) từ các hàm không tinh khiết.

Cân nhắc kỹ thuật viên. Để cho nó hoạt động tốt, nó yêu cầu quyền truy cập vào RNG, một hàm không tinh khiết, nhưng điều này không làm cho cấu trúc dữ liệu không tinh khiết. Nếu bạn thêm một mục và sau đó chuyển đổi nó thành một danh sách, cùng một danh sách sẽ được trả về mỗi lần cho mục bạn thêm vào.

Vì lý do này, tôi nghĩ rằng không an toànPerformIO nên được coi là promisePureIO. Một hàm có nghĩa là các hàm có các tác dụng phụ và do đó sẽ bị gắn nhãn là không tinh khiết bởi hệ thống kiểu có thể được công nhận như là minh bạch trong suốt bởi hệ thống kiểu.

Tôi hiểu rằng bạn phải có định nghĩa thuần túy yếu hơn một chút để giữ điều này. tức là các hàm thuần túy có tính minh bạch liên tục và không bao giờ được gọi là của một tác dụng phụ (như in).

1

Haskell an toàn, phần mở rộng gần đây của GHC, đưa ra câu trả lời mới cho câu hỏi này. unsafePerformIO là một phần của GHC Haskell, nhưng không phải là một phần của phương ngữ an toàn.

unsafePerformIO chỉ nên được sử dụng để xây dựng các hàm trong suốt liên quan; ví dụ, ghi nhớ. Trong những trường hợp này, tác giả của một gói đánh dấu nó là "đáng tin cậy". Một mô-đun an toàn chỉ có thể nhập các mô-đun an toàn và đáng tin cậy; nó không thể nhập các mô-đun không an toàn.

Để biết thêm thông tin: GHC manual, Safe Haskell paper

Các vấn đề liên quan