Tự học lấy Haskell

Learn You a Haskell for Great Good

Miran Lipovača

A chào bạn! Đây là Learn You a Haskell, cách sành điệu nhất để học Haskell, ngôn ngữ lập trình hàm hay nhất hiện nay. Bạn có thể đã nghe nói về nó. Quyển hướng dẫn này dành cho những người trước đây đã lập trình, nhưng chưa thử lập trình hàm.

Cả cuốn sách có sẵn để bạn đọc tự do trên mạng, nhưng cũng có sách in và tôi khuyến khích bạn mua được càng nhiều càng tốt tùy theo túi tiền của mình.

Để liên lạc với tôi, hãy bắn email đến địa chỉ: bonus a còng learnyouahaskell chấm com! Bạn cũng có thể tìm thấy tôi lượn quanh #haskell dưới tên gọi BONUS.

Mục lục

Chương 1: Giới thiệu
Chương 2: Xuất phát
Chương 3: Kiểu và lớp chứa kiểu
Chương 4: Cú pháp dùng trong hàm
Chương 5: Đệ quy
Chương 6: Hàm cấp cao
Chương 7: Module
Chương 8: Tự lập nên các kiểu và lớp riêng
Chương 9: Đầu vào và đầu ra
Chương 10: Giải bài toán theo phong cách lập trình hàm
Chương 11: Functor, Functor áp dụng, và Monoid
Chương 12: Một số vấn đề về Monad
Chương 13: Bổ sung về Monad
Chương 14: Khóa kéo

FAQ (Những câu hỏi thường gặp)

turtle???

Tôi có thể đưa quyển hướng dẫn này lên trang mạng của mình hay sửa đổi nó hay làm bất cứ điều gì được không?

Dĩ nhiên là có, vì nó được phát thành theo giấy phép creative commons vì vậy bạn có thể chia sẻ và thay đổi nội dung quyển này miễn là bạn làm với nụ cười trên môi và cho mục đích phi lợi nhuận.

Bạn có gợi ý tài liệu tham khảo nào khác để học Haskell không?

Có cả chồng tài liệu tuyệt vời, nhưng tôi muốn chỉ ra cho bạn thấy Real World Haskell tuyệt vời ra sao. Nó thực sự rất tuyệt. Tôi cảm thấy rằng nó bổ sung hợp lý cho quyển này. Quyển sách này tập trung chủ yếu vào việc dùng ví dụ đơn giản để cho người mới học tiếp thu được Haskell, trong khi Real World Haskell thực sự chỉ cho ta thấy cách làm những việc có ích bằng ngôn ngữ lập trình này.

Một nguồn tài liệu Haskell có ích khác là Try Haskell, cho phép thử Haskell ngay trong trình duyệt web và cung cấp những chỉ dẫn tương tác rất tuyệt.

Làm thế nào tôi liên lạc được với bạn?

Cách tốt nhât là bắn cho tôi một email đến bonus a còng learnyouahaskell chấm com. Dù vậy bây giờ tôi đang ngập đầu trong email nên có gì bạn hãy thông cảm nếu tôi không hồi âm kịp thời!

Sách bạn viết rất hay nhưng tôi vẫn muốn có bài tập để làm!

Sắp có đấy! Nhiều bạn đã hỏi tôi thêm bài tập vào, nôn tôi sẽ đưa một số bài vào trong thời gian sớm nhất.

Bạn hãy tự giới thiệu mình?

Tôi tên là Miran Lipovača, tôi sống ở Ljubljana, Slovenia. Phần lớn thời gian tôi dành cho những việc không tên, nhưng ngoài lúc đó ra thì tôi hoặc là lập trình, hoặc vẽ, đấm bốc hay chơi bass. Thậm chí tôi còn có một trang web bass tabs khá sành điệu. Tôi còn sưu tập chim cú nhồi bông và đôi khi tôi nói chuyện với chúng.

Chương 1: Giới thiệu

Về quyển hướng dẫn này…

Chào mừng bạn đến với Tự học lấy Haskell (“Learn you a Haskell for Great Good”)! Nếu bạn đang đọc dòng chữ này thì có khả năng là bạn muốn học Haskell. E hèm, bạn đã đến đúng chỗ rồi, nhưng trước hết ta hãy nói về cuốn sách hướng dẫn này đã.

Tôi quyết định viết quyển sách này vì tôi muốn nắm vững kiến thức về Haskell và vì tôi nghĩ rằng tôi có thể giúp những người chưa biết gì về Haskell học ngôn ngữ này từ góc nhìn của cá nhân tôi. Vẫn còn khá ít sách hướng dẫn về Haskell trên mạng Internet. Khi tôi bắt đầu học Haskell, tôi không học từ một nguồn tài liệu duy nhất. Cách học của tôi là đọc vài quyển hướng dẫn khác nhau, cả các bài báo nữa, vì mỗi tài liệu giải thích theo cách riêng. Bằng việc đọc qua nhiều nguồn tài liệu, tôi đã có thể ghép những mảnh kiến thức lại và nội dung cuốn sách đột nhiên được hình thành. Vì vậy đây là một nỗ lực để bổ sung vào nguồn tài liệu học Haskell để bạn có thêm cơ hội tìm được nguồn tài liệu ưa thích.

bird

Cuốn sách hướng dẫn này chủ yếu dành cho những người đã có kinh nghiệm lập trình những ngôn ngữ mệnh lệnh (C, C++, Java, Python …) nhưng trước đây chưa dùng ngôn ngữ lập trình hàm (Haskell, ML, OCaml …). Dù vậy tôi vẫn dám cá với bạn rằng nếu bạn không chưa có kinh nghiệm lập trình gì đáng kể, một người thông minh như bạn vẫn có thể theo kịp nội dung và học được Haskell.

Kênh tin #haskell trên mạng freenode là một nơi tốt để bạn đặt câu hỏi mỗi khi bị bí trong lập trình. Mọi người ở đó đều tốt bụng, kiên nhẫn, và cảm thông đối với những người mới học.

Trước khi nắm được Haskell tôi đã thất bại chừng 2 lần vì có vẻ như nó quá kì quặc đối với mình và tôi không hiểu được. Nhưng rồi một khi đã “vào guồng” và vượt qua rào cản ban đầu đó, mọi chuyện đều thuận buồn xuôi gió. Tôi đoán rằng điều tôi muốn nói sẽ là: Haskell thật thuyệt và nếu bạn quan tâm đến lập trình thì bạn thực sự nên học ngôn ngữ này ngay cả khi ban đầu nó có vẻ kì quặc. Học Haskell cũng rất giống với lần đầu học lập trình — thật là vui! Nó bắt bạn phải suy nghĩ khác đi, điều mà sẽ dẫn ta đến mục kế tiếp …

Vậy Haskell là gì?

fx
Haskell là một ngôn ngữ lập trình hàm thuần túy. Trong các ngôn ngữ lập trình kiểu mệnh lệnh, bạn giải quyết vấn đề bằng cách đưa ra cho máy tính một loạt những nhiệm vụ để máy tính thực hiện chúng. Khi thực hiện, nó có thể thay đổi trạng thái. Chẳng hạn, bạn đặt biến a bằng 5 rồi làm công việc nào đó, rồi lại gán nó bằng một giá trị khác. Bạn có quyền điều khiển các cấu trúc lặp để thực hiện một thao tác vài lần. Trong ngôn ngữ lập trình hàm thuần túy, bạn không ra lệnh cho máy tính làm như vậy, mà là nói cho máy biết cần phải làm điều gì. Giai thừa của một số là tích các số nguyên từ 1 lên đến số đó; tổng của một danh sách các số thì bằng số thứ nhất cộng với tổng của tất cả các số còn lại, và cứ như vậy. Bạn thể hiện điều này dưới dạng các hàm. Bạn cũng không thể gán một biến bằng một giá trị nào đó để rồi sau này gán một giá trị khác. Nếu bạn nói rằng a bằng 5, sau này bạn sẽ không thể nói nó bằng gì khác hơn vì bạn đã nói nó bằng 5 rồi. Nếu không, chẳng phải bạn đã nói dối ư? Vì vậy trong các ngôn ngữ lập trình hàm, một hàm không có hiệu ứng phụ nào. Điều duy nhất mà hàm thực hiện là tính một thứ gì dó rồi trả lại giá trị. Thoạt đầu, điều này có vẻ hạn chế nhưng thực ra nó có một số hệ quả rất hay: nếu một hàm được gọi hai lần với các tham số giống hệt thì nó sẽ đảm bảo trả lại cùng kết quả. Điều này được gọi là sự minh bạch trong tham chiếu và không chỉ cho phép các trình biên dịch hiểu được động thái của chương trình, mà còn giúp bạn dễ dàng suy luận (và thậm chỉ cả chứng minh) ràng một hàm được viết đúng, để từ đó xây dựng những hàm phức tạp hơn bằng cách gắn kết những hàm đơn giản lại với nhau.

lazy
Haskell có tính lười biếng. Điều này nghĩa là chỉ trừ khi bạn nói cụ thể ra, thì Haskell sẽ không thực thi các hàm và tính toán, cho đến khi nó thực sự bị buộc phải trưng kết quả ra cho bạn xem. Đặc tính này kết hợp tốt với sự minh bạch về tham chiếu; nó giúp cho bạn hình dung chương trình như là một loạt những phép biến đổi dữ liệu. Nó cho phép tồn tại những điều thú vị như cấu trúc dữ liệu vô hạn. Giả sử bạn có một danh sách cố định gồm các số xs = [1,2,3,4,5,6,7,8] và một hàm doubleMe có nhiệm vụ nhân mỗi phần tử lên 2 lần rồi trả lại một danh sách mới. Nếu ta muốn nhân danh sách này lên 8 lần, bằng cách dùng ngôn ngữ lập trình mệnh lệnh, và viết doubleMe(doubleMe(doubleMe(xs))), thì có thể nó đã duyệt qua danh sách một lần để tạo một bản sao rồi trả lại danh sách. Sau đó nó sẽ duyệt qua danh sách 2 lần nữa và trả lại kết quả. Trong một ngôn ngữ lập trình lười biếng, việc gọi doubleMe đối với một danh sách mà không yêu cầu phải trưng ra kết quả thì chương trình sẽ kiểu như đáp lời bạn “Rồi rồi, tôi sẽ làm sau!”. Nhưng một khi bạn muốn xem kết quả, thì doubleMe đầu tiên sẽ nói cho cái thứ hai biết rằng nó muốn kết quả, ngay bây giờ! Cái thứ hai sẽ nói cho cái thứ ba và cái thứ ba miễn cưỡng trả lại số gấp đôi của 1, tức là 2. Cái thứ hai nhận lấy giá trị này và đưa số 4 cho cái thứ nhất. Cái thứ nhất nhận lại và báo với bạn rằng phần tử đầu tiên cần tính là 8. Như vậy chỉ có một lần duyệt qua danh sách và chỉ khi bạn thực sự thấy cần. Bằng cách này khi bạn muốn một điều gì đó từ ngôn ngữ lập trình lười biếng, bạn có thể lấy số liệu đầu vào và chuyển đổi theo cách hiệu qủa rồi gắn lại thì sẽ được kết quả mong muốn ở đầu ra.

boat
Haskell có kiểu tĩnh. Khi bạn biên dịch chương trình vừa viết, thì trình biên dịch sẽ hiểu được đoạn mã nào là một số, đoạn mã nào là một chuỗi kí tự, vân vân. Điều này đồng nghĩa với việc rất nhiều lỗi tiềm ẩn được phát hiện lúc biên dịch. Nếu bạn cố gắng cộng một số và một chuỗi kí tự lại với nhau, trình biên dịch sẽ gào lên. Haskell sử dụng một hệ thống kiểu rất tốt và có khả năng suy luận kiểu. Nghĩa là bạn không cần phải gắn cụ thể từng đoạn mã lệnh với một kiểu riêng, vì hệ thống kiểu có thể hình dung ra điều này một cách rất thông minh. Nếu bạn nói a = 5 + 4, bạn sẽ không cần phải bảo Haskell biết rằng a là một con số; nó có thể tự hình dung ra được. Suy luận kiểu cũng giúp cho mã lệnh bạn viết được tổng quát hơn. Nếu như một hàm bạn viết ra nhận hai tham số và cộng chúng lại với nhau mà không nói rõ kiểu của các tham số này thì hàm sẽ tính được với hai tham số bất kì nào có biểu hiện giống kiểu số.

Haskell tinh tế và gọn. Vì dùng nhiều khái niệm cấp cao, những chương trình Haskell thường ngắn hơn các chương trình tương đương viết theo ngôn ngữ mệnh lệnh. Và những chương trình ngắn thì dễ bảo trì hơn và ít lỗi hơn so với những chương trình dài.

Haskell được tạo ra bởi những người rất thông minh (có bằng tiến sĩ). Việc thiết lập Haskell bắt đầu vào năm 1987 khi một hội đồng những nhà nghiên cứu hợp tác để thiết kế nên một ngôn ngữ thật ấn tượng. Năm 2003, bản Haskell Report được xuất bản, định hình phiên bản ổn định của ngôn ngữ này.

Bạn cần gì để bắt tay vào thực hành?

Một trình soạn file chữ và trình biên dịch Haskell. Bạn có thể đã có một trình soạn file chữ ưa thích vì vậy ta sẽ không mất thời gian bàn về nó nữa. Để thực hành nội dung trong cuốn sách này ta sẽ dùng GHC, trình biên dịch Haskell thông dụng nhất. Cách tốt nhất để bắt đầu là tải về Haskell Platform, nói nôm na đây là Haskell với các thư viện phụ thêm.

GHC có thể nhận một đoạn chương trình Haskell (thường có phần mở rộng .hs) để biên dịch, tuy vậy nó cũng có một chế độ tương tác vốn cho phép bạn tương tác trực tiếp với các đoạn chương trình. Tương tác. Bạn có thể gọi các hàm từ đoạn chương trình được tải và kết quả sẽ được hiển thị tức thì. Để phục vụ mục đích học tập thì cách này dễ và nhanh hơn nhiều so với phải biên dịch mỗi khi bạn sửa đổi rồi chạy lại từ dấu nhắc lệnh. Chế độ văn lệnh được khởi động bằng cách gõ vào ghci từ dấu nhắc hệ thống. Nếu bạn đã định nghĩa một số hàm trong một file có tên ví dụ như là myfunctions.hs, thì bạn sẽ tải các hàm này bằng cách gõ vào :l myfunctions; xong rồi bạn có thể thử nghiệm chúng, miễn là myfunctions.hs được đặt ở cùng thư mục nơi mà ghci được khởi động. Nếu bạn sửa đổi file chương trình .hs, thì chỉ cần gõ lại :l myfunctions hoặc gõ :r; cách làm này tương đương vì nó tải lại (reload) file chương trình hiện thời. Quy trình hoạt động quen thuộc với tôi khi thử nghiệm là định nghĩa một hàm nào đó trong một file .hs, tải nó rồi thử chạy chán chê, sau đó sửa đổi file .hs file, tải lại và cứ như vậy. Đây cũng là cách mà chúng ta sẽ làm.

34 bình luận

Filed under Haskell

34 responses to “Tự học lấy Haskell

  1. Pingback: Chương 2: Xuất phát | Blog của Chiến

  2. Pingback: Chương 3: Kiểu và lớp chứa kiểu | Blog của Chiến

  3. Pingback: Chương 4: Cú pháp dùng trong hàm | Blog của Chiến

  4. Pingback: Chương 5: Đệ quy | Blog của Chiến

  5. Pingback: Chương 6: Hàm cấp cao | Blog của Chiến

  6. Pingback: Chương 7: Module | Blog của Chiến

  7. Pingback: Chương 8: Tự lập nên các kiểu và lớp riêng | Blog của Chiến

  8. Pingback: Chương 9: Đầu vào và đầu ra | Blog của Chiến

  9. Pingback: Chương 11: Functor, Functor áp dụng và Monoid | Blog của Chiến

  10. Pingback: Một số vấn đề về Monad | Blog của Chiến

  11. Hong

    Quang Chiến có ứng dụng nào của haskell cho Hồng xin nhé. Hồng đang tìm hiểu về Haskell. Gửi cho Hồng xin qua rosexanh@gmail.com nhé!

    • Mình cũng chưa có kinh nghiệm về Haskell, chưa viết chương trình ứng dụng nào. Chủ yếu làm các bài tập của họ thôi, Cách viết các ứng dụng của nó hơi khác với những ngôn ngữ lập trình thông thường. Để tìm hiểu sâu thì có môn Lý thuyết kiểu (type theory) khá lằng nhằng. Bạn nên tìm hiểu quyển Real World Haskell.

  12. Pingback: Chương 14: Khóa kéo | Blog của Chiến

  13. monads

    Bravo bạn,
    Quả thật là mình không ngờ lại có một bản dịch ra tiếng Việt công phu như vậy về Haskell (trước đây mình học Haskell qua cuốn “Real World Haskell”). Cuốn “Learn You a Haskell for Great Good!” chắc hẳn là rất hay, mình đọc qua thấy nó trình bày khá vui nhộn.
    Có nhiều phần ở Haskell khá rắc rối, ví dụ như phần monads (trước đây mình cũng đã mất khá nhiều thời gian để hiểu nó). Nhưng quả thật Haskell rất đẹp, viết một chương trình mà như chứng minh một bài tóan trên giấy vậy.

    • Chúc mừng bạn đã đến với Haskell. Hi vọng không lâu sau này cách lập trình hàm sẽ trở nên phổ biến hơn trong giới khoa học.
      Nếu có thời gian, mong bạn vui lòng chỉ ra những thuật ngữ hoặc cách dịch cần sửa lại ở những chương cuối sách (về monad, functor). Cám ơn bạn nhiều!

  14. Pingback: Chương 13: Bổ sung về Monad | Blog của Chiến

  15. Pingback: Chương 10: Giải bài toán theo phong cách lập trình hàm | Blog của Chiến

  16. anhlt

    Gửi bác Chiến,
    Tôi có đọc và tìm hiểu theo tài liệu trên blog của bác và thấy rất hữu ích. Tôi có 1 thắc mắc nhỏ xin giải đáp, tôi có copy thử hàm trên này như sau:
    filter :: (a -> Bool) -> [a] -> [a]
    filter _ [] = []
    filter p (x:xs)
    | p x = x : filter p xs
    | otherwise = filter p xs

    largestDivisible :: (Integral a) => a
    largestDivisible = head (filter p [100000,99999..])
    where p x = x `mod` 3829 == 0
    sau khi đã copy vào file text và save với đuôi .hs, load nó trong môi trường Ghci và chạy dòng lệnh:
    ghci> largestDivisible
    hệ thống báo như sau:
    :5:1 Not in scope: `largestDivisible’
    Bác có thể giải thích và sửa giúp không ạ?
    Cảm ơn bác

    • Có khả năng bạn chưa tải được module của file nguồn vừa tạo. Tên module chính là tên file nhưng ko có phần đuôi hs.

      Trước hết phải chuyển về thư mục vùa save file:

      Prelude> :cd C:\Duong\dan

      Sau đó hãy nạp/tải module:

      Prelude> :load theModule

      lúc đó mới chạy

      *Main> largestDivisible
      99554

      • anhlt

        Tôi đã save và load như bác hướng dẫn nhưng vẫn bị, bác có thể cho nick yahoo hoặc skype để tiện liên lạc và gửi file bác xem lại giúp không ạ?

      • anhlt

        nick của tôi là ltanh75 (yahoo) và anhlt2 (skype)

  17. Minh Tan Nguyen

    em cảm ơn anh Chiến nhiều lắm,em tìm tung gg chỉ có mỗi bài của anh là hay nhất và dễ hiểu nhất 😀

    • Bản thân sách Learn You a Haskell for Great Good cũng rất hay. Ông Thủ tướng Singapore, Lý Hiển Long, trong cuộc phỏng vấn có nói rằng khi về hưu ông sẽ học Haskell bằng cuốn này.

      • Linh

        Chào anh Chiến,

        Tôi có một vài câu hỏi không biết anh có thể giúp không:
        1. Anh có contact của ai đang học tập, nghiên cứu, làm việc bằng Haskell tại Việt Nam không?
        2. Anh có thông tin về số lần tải về của bản dịch không?

        Cảm ơn anh!

      • 1. Mình không biết rõ. Trong số vài chục bạn học trước đây mình liên lạc được thì một người ở Việt Nam có quan tâm đến Haskell vào năm 2012 (nhưng chỉ là sở thích). Nay không biết được.
        2. Thống kê số lần truy cập vào trang đầu của ebook tiếng Việt là http://162.250.127.146/Haskell_stats.png . Có 1 ngày 5/5/2015 nhiều nhất với 97 lượt. Các đợt thống kê vào những trang cụ thể trong từng chương thì ít hơn.

  18. Duong Le

    Chào anh Chiến, em cũng mới tập tành tìm hiểu Haskell, em có 1 bài tập :
    ” Write a function to calculate the total of a number’s factors.
    Greatest common factor is the largest number out of list of factors of 2 (or more than 2) numbers. Write a function to return the greatest common factor of 2 positive integers ”

    với C thì e đưa ra thuật toán cho bài này rất dễ nhưng khi chyển qua Haskell, một số syntax khá lạ lẫm , a có thể làm ví dụ này cho e có thể hiểu hơn 1 số thứ dc ko ?

    • “Write a function to return the greatest common factor of 2 positive integers” –> cái này chắc bạn cũng có thể hình dung ra được. Dùng Haskell lập trình đệ quy rất tiện. Hàm gcd(a,b) hay gcf(a,b) có thể được tính theo cách đệ quy. Chẳng hạn http://stackoverflow.com/questions/7922899/gcf-lcm-in-haskell . Cách lập trình là pattern matching.
      “Write a function to calculate the total of a number’s factors.” –> Để tránh dùng vòng lặp thì người ta thường hay dùng một cái list. Ví dụ như lời giải http://stackoverflow.com/a/24983236 . Trong đó họ trình bày cách dùng đệ quy, cách dùng filter, và dùng list comprehension.

      • LeHai

        Cảm ơn bác vì cuốn sách, minh đã đọc vài lần nhưng vẫn còn lơ mơ, trình tiếng anh thì kém, có dùng gg transerlate để tìm hiểu cuốn real world thấy nó là tài liệu bổ sung tuyệt vời cho cuốn này. Bác mà bỏ công dich nốt thì em sẽ lấy tên bac dặt tên cho úng dụng đầu tiên mà em viết băng Hekell 🙂

  19. tienty muctieu

    e chào bác ạ, e tình cờ tìm hiểu về haskell thấy tài liệu này của bác hay quá, bác có thể nào cho e xin file tiếng việt này để tải về nghiên cứu dần dần không ạ? e cảm ơn bác nhiều ạ

  20. Jaken Ty

    Bạn Chiến có phải là bạn Trần Đăng Quang này ko nhỉ?


    Quang Trần Đăng
    srnSeoptdo61t5l69mf
    h
    c878hut0clafmc6
    5
    44u7imm563hh608c88a00286
    ·
    Tìm người kiểm tra bản dịch Tiếng Việt (hiệu đính)
    Hiện mình đang có một cuốn sách đã được dịch từ tiếng Anh sang Tiếng Việt cần có người chỉnh sửa lỗi chính tả và cách diễn đạt câu.
    Chi phí: 40,000đ/trang.
    Link sách: https://bit.ly/Learn-You-A-Haskell-Vietnamese
    Bạn nào có thể làm được thì inbox trực tiếp cho mình để trao đổi thêm về công việc nhé.

    bản dịch trên có vài lỗi chính tả giống bản của Chiến? một số câu cũng tương tự
    https://docs.google.com/document/d/1G2RykYetO9YhXxUtc-PtgjsRJHhmZsId/edit#

    • Chào Jaken! Bản dịch LYHGG mình đã dịch từ năm 2012, dịch và công bố ngay trên Blog của Chiến rồi. Các bạn biên tập thành sách có thể dễ dàng tìm kiếm và tham khảo các đoạn nội dung mà.

Bình luận về bài viết này