Skip to main content

Bài 1- Giới thiệu về Haskell và không cụ

TÓM TẮT
  • Haskell là gì?
  • Ngôn ngữ lập trình Hàm
    • Ghép nối Hàm
  • Hiệu ứng rõ ràng (Thuần túy)
  • Cú pháp cơ bản
    • Thụt lề và diễn giải (comment)
    • Định nghĩa và sử dụng hàm
  • Hệ thống Kiểu Haskell
  • Tính lười biếng
  • Công cụ: GHC (GHCi), Cabal, Stack
Video bài giảng
Chúng ta đang dịch thuyết minh bài giảng sang tiếng Việt

Cách sử dụng JupyterLab

  • Mỗi bài học là một sổ tay Jupyter.
  • Mỗi sổ ghi chép Jupyter là một loạt các ô.
  • Để thực hiện một ô, hãy nhấp vào ⇧⏎ (Shift + Enter).
  • Bạn có thể làm việc xung quanh với mã code bên trong các ô.
  • Khi bạn đóng tab, mọi thay đổi sẽ bị mất.

Haskell là gì?

Chúng ta sẽ xem xét riêng từng thuộc tính của Haskell và trả lời câu hỏi này ở cuối bài giảng.

Ngôn ngữ lập trình hàm

Haskell là một ngôn ngữ lập trình hàm.

Trong các ngôn ngữ lập trình mệnh lệnh, các định nghĩa hàm là một chuỗi các câu lệnh mệnh lệnh.

Trong các ngôn ngữ lập trình hàm, định nghĩa hàm là cây biểu thức ánh xạ giá trị này sang giá trị khác.

Các chương trình được xây dựng bằng cách Áp dụng và Ghép nối các hàm .

Ghép nối hàm

Ghép nối hàm là hành động chuyển kết quả của một hàm sang đầu vào của một hàm khác, tạo ra một hàm hoàn toàn mới

Giống như thành phần thông thường của các hàm trong toán học, kết quả của mỗi hàm được truyền dưới dạng đối số của hàm tiếp theo và kết quả của hàm cuối cùng là kết quả của tổng thể.

Ví dụ: giả sử chúng ta có hai hàm f và g:

y = f(x) z = g(y)

Soạn chúng có nghĩa là trước tiên chúng ta tính toán f(x) để nhận được y, sau đó sử dụng y làm đối số để tính toán g(y) để nhận được z.

Tạo một hàm đi từ x đến z:

z = g(f(x))

Và đó là cách chúng ta có thể tạo các hàm phức tạp tùy ý bằng cáchGhép nốicác hàm đơn giản.

Ví dụ: nếu chúng ta có:

  • Một hàm lấy một bảng tính và trả về danh sách những người làm việc mà nó chứa.
  • Một hàm lấy danh sách người làm việc và trả về danh sách tương tự được sắp xếp theo điểm số.
  • Và một hàm lấy danh sách người làm việc và trả về 3 người đầu tiên.

Chúng ta có thể tạo một hàm duy nhất lấy một bảng tính và trả về 3 người làm việc giỏi nhất bằng cách kết hợp ba hàm đó.

Ngoài ra, Haskell có các hiệu ứng rõ ràng (còn được gọi là 👼 thuần túy)!

Hiệu ứng rõ ràng (hàm thuần túy)

Các ngôn ngữ lập trình hàm thuần túy coi tất cả các tính toán là đánh giá các hàm toán học .

Trong toán học, biểu thức y = x + 1 có nghĩa là giá trị của y là một hàm phụ thuộc vào x.

Đối với một x cụ thể, giá trị của y sẽ luôn giống nhau.

Bất kể bạn đang ở Ý hay Tây Ban Nha, nếu đó là năm 1994 hay 2022 hoặc nếu bạn có các phương trình khác trong sổ ghi chép. y sẽ quan tâm đến giá trị của x và không quan tâm đến giá trị nào khác.

Trong các ngôn ngữ lập trình hàm thuần túy, các hàm thuần túy chỉ phụ thuộc vào các đối số của chúng và không tương tác với bất kỳ trạng thái toàn cục hoặc cục bộ nào. (Điều này được gọi là "không có tác dụng phụ (side effects) .")

Điều này có nghĩa là, đối với một đầu vào cụ thể, một hàm sẽ luôn trả về cùng một giá trị. Luôn luôn như vậy.

Nghe có vẻ là một ý tưởng tồi, nhưng nếu bạn nghĩ về nó, nó có một số hậu quả cực kỳ tiện lợi:

  • Nó cho phép bạn dễ dàng suy luận và chứng minh rằng một hàm là đúng.
  • Trong Haskell, người ta luôn có thể “thay bằng bằng bằng”, giống như bạn đã học trong lớp đại số.
  • Làm cho mã code của bạn ít bị lỗi hơn đáng kể.
  • Việc tính toán song song/đồng thời dễ dàng hơn. (Nếu không có sự phụ thuộc dữ liệu giữa hai biểu thức thuần túy, thì chúng có thể được thực hiện song song và chúng không thể can thiệp lẫn nhau.)

Haskell hoạt động như một ngôn ngữ thuần túy, nhưng vẫn cho phép các tác dụng phụ (giao tiếp mạng, I/O, v.v.) bằng cách gắn thẻ rõ ràng cho chúng trong hệ thống Kiểu. Chúng ta sẽ xem làm thế nào trong các bài học trong tương lai. (Điều này được gọi là có " hiệu ứng rõ ràng ").

Trước khi tiếp tục với nhiều thuộc tính hơn, hãy xem Haskell thực sự trông như thế nào.

Cú pháp cơ bản

Diễn giải mã code

-- Use double dash to comment within a single line of code.

{-
Use curly bracket with a single dash to
open and close
multi-line comments.
-}

Thụt đầu dòng

Haskell nhạy cảm với thụt lề. Điều đó có nghĩa là khoảng trắng, tab và dòng mới đều quan trọng.

Nguyên tắc vàng là:

Mã là một phần của một số biểu thức nên được thụt vào sâu hơn so với phần đầu của biểu thức đó (ngay cả khi biểu thức không phải là phần tử ngoài cùng bên trái của dòng).

Chúng ta sẽ xem xét các ví dụ trong các bài học sau.

Định nghĩa hàm

Haskell là một ngôn ngữ lập trình hàm có nghĩa là bạn sẽ viết rất nhiều hàm. Vì vậy, đó là nơi chúng ta sẽ bắt đầu.

Đây là biểu thức để xác định hàm kiểm tra xem một số có lớn hơn 18 hay không:

greaterThan18 x = x > 18
  • greaterThan18 là tên của hàm. Nên chọn một cái tên giúp bạn dễ dàng biết nó làm gì.
  • x là một tham số.
  • Toán  tử = gán biểu thức x > 18 cho tên greaterThan18.

Bên trái dấu = ta viết tên hàm và các tham số. Và ở bên phải, biểu thức sẽ được chứa trong hàm này.

Sử dụng hàm

Để sử dụng greaterThan18 hàm, chúng ta chỉ cần viết tên, một dấu cách và viết một số:

greaterThan18 3

Hàm được thực thi, Haskell thay thế tất cả x bằng 30 và greaterThan18 30 trở thành 30 > 18 . Sau đó, nó đánh giá biểu thức, trả về True .

Thêm ví dụ

-- Hàm cộng 6 số:
add6numbers u v w x y z = u + v + w + x + y + z
add6numbers 1 2 3 4 5 6 -- 21

-- Hàm tính thể tích khối trụ
volumeOfACylinder r h = pi * r^2 * h -- pi represents the number π, and it comes with Haskell
volumeOfACylinder 3 10

-- Một hàm lấy nhiệt độ tính bằng Fahrenheit và trả về nhiệt độ bằng Celsius
fToC x = (x - 32) * 5 / 9
fToC 212 -- 100

Những điểm chính

  • Các tham số được phân tách bằng dấu cách.
  • Mọi thứ sau the = là phần thân của hàm.
  • Chữ cái đầu tiên của tên hàm phải là chữ thường.
  • Chúng ta sử dụng dấu ngoặc đơn để ưu tiên các phép tính, như trong toán học.

Hệ thống kiểu của Haskell

Chúng ta sẽ tìm hiểu sâu về hệ thống Kiểu Haskell trong bài 2. Ở đây bạn sẽ tìm hiểu một số điều cơ bản.

Các Kiểu là các thuộc tính ràng buộc các giá trị mà một đoạn mã code có thể có. Ví dụ: nếu bạn chỉ ra rằng một số dữ liệu là một số, thì dữ liệu đó có thể có bất kỳ giá trị nào sau đây:

  32

9999695939294

0.5

Nhưng, nếu bạn cố gắng thêm một ký tự vào đó, như thế này: 6A3 (thay vì 63 ), trình biên dịch/thông dịch viên sẽ hét vào mặt bạn.

Trình biên dịch/thông dịch viên của bạn đã làm gì ở đó được gọi là " kiểm tra kiểu ." Một số ngôn ngữ có Kiểm tra Kiểu được thực thi mạnh mẽ hơn, một số thì ít hơn.

  6A3

Kiểm tra Kiểu

Kiểm tra kiểu là quá trình xác minh và thực thi các ràng buộc của kiểu.

Điều đó có nghĩa là gì? Điều đó có nghĩa là mỗi Kiểu có các ràng buộc riêng (Ví dụ: bạn không thể làm toán với các chữ cái). Và quy trình này kiểm tra xem các ràng buộc đó có được tôn trọng hay không.

Tại sao bạn sẽ làm điều này? Để tránh những sai lầm có thể ngăn ngừa được.

Ngôn ngữ kiểu động (Dynamically typed languages)

Nếu tiếp tục trong chương trình của bạn, bạn muốn cộng một vài số và một trong số chúng có một chữ cái, chương trình sẽ không biết phải làm gì và nó sẽ khiến bạn gặp sự cố. Đó là những lỗi có thể ngăn ngừa được và trình biên dịch/thông dịch viên sẽ giúp bạn tránh chúng.

Thông thường, điều này được thực hiện tự động. Nhưng không phải tất cả các ngôn ngữ đều làm điều này theo cùng một cách. Có hai điểm khác biệt chính liên quan đến KHI các Kiểu được kiểm tra: Ngôn ngữ được kiểu động và Ngôn ngữ được kiểu tĩnh.

Các ngôn ngữ được kiểu động kiểm tra các Kiểu trong thời gian chạy .

Thời gian chạy là điều cuối cùng mà bạn làm với một chương trình. Đó là giai đoạn bạn chạy chương trình của mình để kiểm tra hoặc sử dụng nó.

Các ví dụ phổ biến về ngôn ngữ được kiểu động bao gồm JavaScript, Python, Objective-C và PHP.

Ngôn ngữ kiểu tĩnh (Statically typed languages)

Các ngôn ngữ được kiểu tĩnh kiểm tra các Kiểu tại thời điểm biên dịch .

Có nghĩa là bạn sẽ biết nếu kiểu của bạn sai ngay khi bạn biên dịch chương trình của mình. Điều này dẫn đến một mã code an toàn hơn và được tối ưu hóa hơn.

Các ví dụ phổ biến về ngôn ngữ ngôn ngữ kiểu tĩnh bao gồm Java, C và C++.

Hệ thống kiểu của Haskell

Haskell là Ngôn ngữ kiểu tĩnh. Và, trong Haskell, mọi biểu thức đều có một Kiểu .

Nhưng đừng lo, bạn không cần phải xác định kiểu của mọi biểu thức theo cách thủ công vì trình biên dịch của Haskell rất giỏi trong việc suy luận kiểu .

Loại suy luận cho phép Haskell tự suy luận các Kiểu .

Nếu bạn viết một cái gì đó như 3 + 4 , Haskell sẽ biết rằng kết quả của biểu thức đó là một số và nó sẽ coi nó như vậy mà không cần bạn chỉ định Kiểu. (Nó cũng hoạt động với các biểu thức phức tạp hơn. Xem các ví dụ trước.)

Điều đó cho phép trình biên dịch hiểu và suy luận khá nhiều về chương trình của bạn. Cung cấp cho bạn một trợ thủ bắt lỗi khá hiệu quả.

Mặc dù nó không cần thiết cho trình biên dịch, nhưng việc viết ra chữ ký kiểu của các hàm và biểu thức cấp cao nhất được coi là một cách thực hành tốt. Để cải thiện khả năng đọc mã.

Nếu mã code quá mơ hồ để trình biên dịch suy ra Kiểu, nó sẽ yêu cầu bạn chỉ định Kiểu.

Tính Lười biếng

Haskell có tính lười biếng. Điều này có nghĩa là nó sẽ không tính toán các biểu thức cho đến khi cần kết quả của chúng

Ví dụ về sự lười biếng của Haskell trong thực tế:

  • Chúng ta có thể sử dụng danh sách vô hạn.

    giveMe x = take x [1..] -- [1..] is an infinite list of natural numbers that starts at 1. giveMe 7

  • Haskell sẽ không đánh giá các biểu thức nếu chúng không cần thiết

    cheapComputation = 7 expensiveComputation = sum [1..10000000] -- sum là một hàm lấy một danh sách và trả về tổng của tất cả các phần tử. Điều này sẽ làm sập kernel. if cheapComputation > 5 || expensiveComputation > 5 then "Done" else "This won't ever show because expensiveComputation is always bigger than 5" -- Try running this cell with cheapComputation being bigger and smaller than 5. -- When cheapComputation > 5, expensiveComputation isn't evaluated because it is not needed.

Vậy, Haskell là gì?

Haskell là một ngôn ngữ lập trình hàm, lười biếng, được ngôn ngữ kiểu tĩnh với các hiệu ứng và hàm rõ ràng giống như sau:

volumeOfACylinder r h = pi * r^2 * h 

Lưu ý: Haskell có các thuộc tính quan trọng khác (như kiểu dữ liệu đại số, lớp kiểu, suy luận kiểu, đa hình, ...) mà chúng ta sẽ đề cập trong các bài học sau.

(Hiệu ứng **lười biếng và rõ ràng là hai trong số các thuộc tính độc đáo hơn của Haskell. Đó là lý do tại sao chúng được in đậm.)

Công cụ

Đôi lời về Cabal và Stack

Khi tìm hiểu về Haskell, bạn sẽ thường gặp các thuật ngữ Cabal và Stack.

Đây là những hệ thống quản lý thư viện và chương trình. Chúng làm cho việc làm việc với các thư viện trở nên đơn giản.

Chúng ta sẽ sử dụng Cabal trong khóa học này và chúng ta sẽ giải thích cách sử dụng nó ở giai đoạn sau.

GHC và GHCi

GHC (Glasgow Haskell Compiler) là một trình biên dịch và môi trường tương tác cho Haskell. Sử dụng GHC chúng ta có thể:

  • Biên dịch chương trình và thực thi chúng giống như bất kỳ ứng dụng nào khác.
  • Đánh giá nhanh các biểu thức Haskell bằng cách sử dụng môi trường tương tác do GHC (GHCi) cung cấp.

Để sử dụng GHCi, hãy mở thiết bị đầu cuối trong môi trường từ xa GitPod mà chúng ta đã chuẩn bị và nhập ghci .

Sử dụng :l relative/path.hs bên trong GHCi để tải tệp và sử dụng tương tác nội dung của tệp và :q thoát.

LƯU Ý: Nếu bạn muốn cài đặt GHC và GHCi trên máy tính của mình, bạn có thể làm theo hướng dẫn trên www.haskell.org/ghcup/. Hướng dẫn được cung cấp cho Windows, Mac và Linux.


Nguồn bài viết tại đây


Picture