Tôi coi đó là một nửa sự thật. Haskell có một khả năng trừu tượng đáng kinh ngạc, bao gồm cả việc trừu tượng hóa những ý tưởng bắt buộc. Ví dụ, Haskell không có vòng lặp while mệnh lệnh tích hợp, nhưng chúng ta có thể viết nó và bây giờ nó làm được:
while :: (Monad m) => m Bool -> m () -> m ()
while cond action = do
c <- cond
if c
then action >> while cond action
else return ()
Mức độ trừu tượng này là khó đối với nhiều ngôn ngữ mệnh lệnh. Điều này có thể được thực hiện trong các ngôn ngữ mệnh lệnh có các dấu đóng; ví dụ. Python và C #.
Nhưng Haskell cũng có khả năng (rất độc đáo) để mô tả các tác dụng phụ được phép , bằng cách sử dụng các lớp Monad. Ví dụ, nếu chúng ta có một hàm:
foo :: (MonadWriter [String] m) => m Int
Đây có thể là một hàm "mệnh lệnh", nhưng chúng tôi biết rằng nó chỉ có thể thực hiện hai việc:
- "Xuất" một luồng chuỗi
- trả lại một Int
Nó không thể in ra bảng điều khiển hoặc thiết lập kết nối mạng, v.v. Kết hợp với khả năng trừu tượng, bạn có thể viết các hàm hoạt động trên "bất kỳ tính toán nào tạo ra một luồng", v.v.
Đó thực sự là tất cả về khả năng trừu tượng của Haskell khiến nó trở thành một ngôn ngữ mệnh lệnh rất tốt.
Tuy nhiên, nửa sai là cú pháp. Tôi thấy Haskell khá dài dòng và khó sử dụng theo phong cách mệnh lệnh. Dưới đây là một ví dụ về tính toán kiểu mệnh lệnh bằng cách sử dụng while
vòng lặp trên , vòng lặp này tìm phần tử cuối cùng của danh sách được liên kết:
lastElt :: [a] -> IO a
lastElt [] = fail "Empty list!!"
lastElt xs = do
lst <- newIORef xs
ret <- newIORef (head xs)
while (not . null <$> readIORef lst) $ do
(x:xs) <- readIORef lst
writeIORef lst xs
writeIORef ret x
readIORef ret
Tất cả những gì IORef rác, việc đọc kép, phải ràng buộc kết quả của một lần đọc, fmapping ( <$>
) để hoạt động trên kết quả của một phép tính nội tuyến ... tất cả đều trông rất phức tạp. Nó có rất nhiều ý nghĩa từ góc độ chức năng , nhưng các ngôn ngữ mệnh lệnh có xu hướng quét hầu hết các chi tiết này xuống dưới tấm thảm để làm cho chúng dễ sử dụng hơn.
Phải thừa nhận rằng, có lẽ nếu chúng ta sử dụng bộ while
tổ hợp kiểu khác thì nó sẽ sạch hơn. Nhưng nếu bạn đưa triết lý đó đi đủ xa (sử dụng một bộ tổ hợp phong phú để thể hiện bản thân một cách rõ ràng), thì bạn lại đến với lập trình chức năng. Haskell kiểu mệnh lệnh không "chảy" như một ngôn ngữ mệnh lệnh được thiết kế tốt, ví dụ như python.
Tóm lại, với sự nâng cao về mặt cú pháp, Haskell có thể là ngôn ngữ mệnh lệnh tốt nhất. Nhưng, về bản chất của nâng cơ mặt, nó sẽ thay thế một thứ đẹp đẽ bên trong và thật bằng một thứ gì đó đẹp đẽ và giả tạo bên ngoài.
CHỈNH SỬA : Tương phản lastElt
với chuyển ngữ của con trăn này:
def last_elt(xs):
assert xs, "Empty list!!"
lst = xs
ret = xs.head
while lst:
ret = lst.head
lst = lst.tail
return ret
Cùng một số dòng, nhưng mỗi dòng có tiếng ồn ít hơn một chút.
CHỈNH SỬA 2
Đối với những gì nó đáng giá, đây là cách một sự thay thế thuần túy trong Haskell trông như thế nào:
lastElt = return . last
Đó là nó. Hoặc, nếu bạn cấm tôi sử dụng Prelude.last
:
lastElt [] = fail "Unsafe lastElt called on empty list"
lastElt [x] = return x
lastElt (_:xs) = lastElt xs
Hoặc, nếu bạn muốn nó hoạt động trên bất kỳ Foldable
cấu trúc dữ liệu nào và nhận ra rằng bạn không thực sự cần IO
xử lý lỗi:
import Data.Foldable (Foldable, foldMap)
import Data.Monoid (Monoid(..), Last(..))
lastElt :: (Foldable t) => t a -> Maybe a
lastElt = getLast . foldMap (Last . Just)
với Map
, ví dụ:
λ➔ let example = fromList [(10, "spam"), (50, "eggs"), (20, "ham")] :: Map Int String
λ➔ lastElt example
Just "eggs"
Các (.)
nhà điều hành là hàm hợp .