- vừa được xem lúc

[Functional Programming + Elm] Bài 3 - Type Variable & Type Class

0 0 11

Người đăng: Semi Dev

Theo Viblo Asia

Trong bài viết này, chúng ta sẽ nói về các khái niệm liên quan tới định kiểu dữ liệu Type. Những khái niệm mới mà chúng ta sẽ đề cập ở đây không hẳn thuộc về Functional Programming mà là những khái niệm phổ biến trong mảng lập trình nói chung. Đó là Kiểu Biến Thiên hay Biến Định Kiểu Type Variable và Lớp Định Kiểu Type Class.

Type Variable

Khái niệm Type Variable xuất phát từ môi trường của các ngôn ngữ định kiểu tĩnh static-typing, khi người ta bắt đầu nghĩ tới khả năng sử dụng một kiểu dữ liệu trừu tượng a để biểu trưng cho nhiều kiểu dữ liệu khác nhau. Như vậy, khi một biến được định kiểu với kiểu a sẽ có thể lưu trữ được một giá trị thuộc bất kỳ kiểu dữ liệu nào.

Do Type Variable đã được đề cập trước đó trong một bài viết của Sub-Series Declarative nên ở đây mình sẽ chỉ trích dẫn lại phần nội dung đã thực hiện để chúng ta có thể di chuyển nhanh tới khái niệm tiếp theo.

Các ngôn ngữ thuần Declarative hầu hết đều được xây dựng với một tinh thần chung - đó là khả năng định kiểu rất mạnh mẽ và nghiêm ngặt strong-typing. Và ở đây chúng ta có Elm là một trong số đó.

Cụ thể là thông báo lỗi như ví dụ phép tính + giữa một giá trị Int và một giá trị Float mà chúng ta đã nhìn thấy ở phần đầu của bài viết. Mặc dù trình biên dịch compiler của Elm đã có đủ thông tin về các giá trị nhận được trước khi thực hiện phép tính, tuy nhiên chỉ đơn giản là Elm không hỗ trợ tự động chuyển đổi kiểu ngầm định trong trường hợp này. Và chúng ta sẽ cần phải thực hiện việc chuyển kiểu dữ liệu trong code của mình -

(toFloat (round 1.0)) + 2.0
-- 3 : Float

Ồ thế nhưng tại sao phép tính 1 + 2.0 lại không có thông báo lỗi ?

Giá trị 1 trả về bởi round được định kiểu là Int. Còn giá trị 1 mà chúng ta viết trực tiếp vào trong tệp code của chúng ta thì lại chưa được định kiểu cố định, do đó nên Elm sẽ xem là một giá trị thuộc kiểu biến thiên number.

Khái niệm kiểu biến thiên Type Variable, có thể hiểu đơn giản là một kiểu dữ liệu bất kỳ mà trình biên dịch compiler không tìm thấy thông tin định kiểu rõ ràng trong code. Và sẽ cố gắng tìm ra một logic xử lý thành công phù hợp nhất.

Chính xác thì một kiểu biến thiên a được hiểu là một kiểu Union bao gồm tất cả các kiểu dữ liệu mà trình biên dịch thu thập được trong code định nghĩa của toàn bộ chương trình project. Tuy nhiên, Elm cũng có tạo ra một vài Type Variable với khả năng hữu hạn hơn so với a. Đó là -

  • number - là một giá trị số học; Vì vậy nên có thể là Float hoặc Int.
  • comparable - là một giá trị có thể so sánh được bằng chương trình compare; Bao gồm Int, Float, Char, String, và List/Tupple của các kiểu đó.
  • appendable - là một giá trị có thể thực hiện các thao tác nối ghép nội dung; Vì vậy nên có thể là String hoặc List.
  • compappend - là một giá trị vừa thuộc comparable và vừa thuộc appendable.

Như vậy khi trình biên dịch đọc phép tính 1 + 2.0 thì giá trị 2.0 đã có đủ thông tin định kiểu rõ ràng là Float do có dấu phẩy thập phân .; Còn giá trị 1 thì chưa có thông tin định kiểu cụ thể nên sẽ là number. Logic thành công phù hợp nhất là Float + Float và chúng ta có logic được biên dịch là 1.0 + 2.0. Phép tính được thực hiện và không có thông báo lỗi.

Type Class

Khái niệm Type Class hay còn được gọi ngắn gọn là Class cũng là một khái niệm phổ biến trong lập trình nói chung và không thuộc về riêng bất kỳ ngôn ngữ hay mô hình lập trình nào. Tuy nhiên cũng giống Type Variable, khái niệm Class được hiểu và triển khai trong các mô hình lập trình có phần khác nhau do bối cảnh áp dụng và nền móng tư duy khởi điểm.

Đối với tư duy Lập Trình Hướng Đối Tượng OOP thì một Class được sử dụng để biểu trưng cho các giá trị thuộc các kiểu dữ liệu trong cùng dòng kế thừa tính từ chính Class đó cho tới các Class mở rộng được định nghĩa sau. Tức là nếu như chúng ta có Class Person được sử dụng để định kiểu cho một biến someone : Person thì biến đó sẽ có thể lưu thực thể dữ liệu tạo ra từ chính Class Person hoặc bất kỳ Class nào khác ví dụ như Worker hay Teacher kế thừa từ Class Person.

Còn trong tư duy Lập Trình Hàm Functional Programming thì các Class lại được sử dụng để nhóm các kiểu dữ liệu khác nhau với logic giống với CSS Class. Tức là các kiểu dữ liệu được nhóm lại có thể không liên quan tới nhau về mặt logic quản lý dữ liệu của chương trình, mà có cùng giao diện chức năng. Ví dụ đơn cử như trường hợp của comparable ở phần trước của bài viết có thể được xem là một Class bởi bất kỳ kiểu dữ liệu nào thuộc comparable thì sẽ có thể tham gia vào một phép so sánh.

Như vậy chúng ta cũng có thể hiểu Class là một Kiểu Tổ Hợp Union Type biểu trưng cho nhiều kiểu dữ liệu khác nhau, nhưng được sử dụng ở khía cạnh mô tả chức năng tham gia vào một logic xử lý nào đó. Và ở điểm này thì Class của Functional lại có thể được hiểu là có chức năng tương đương với Interface trong OOP - tức là công cụ được sử dụng để tạo ràng buộc cho các kiểu dữ liệu cụ thể khi tham gia vào Class sẽ phải triển khai những chức năng mà Class đó đã khai báo.

Elm không có cú pháp hỗ trợ khai báo Class và chúng ta sẽ tham khảo một code ví dụ từ Haskell - ngôn ngữ được xem là superset của Elm. Ở đây chúng ta sẽ có Class Eq được khai báo là những kiểu dữ liệu thuộc Class này sẽ có thể tham gia vào các phép kiểm tra tính tương đương ==/=. Lưu ý trước khi đọc code ví dụ là Haskell sử dụng ký hiệu :: để định kiểu thay vì một dấu hai chấm : như Elm.

class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool

Định nghĩa này có thể được đọc là chúng ta có class Equal được khai báo là bất kỳ kiểu dữ liệu a nào thuộc class này cũng đều phải định nghĩa logic chi tiết cho các hàm so sánh tính tương đương ==/=. Như vậy sau khi định nghĩa xong một kiểu dữ liệu nào đó, chúng ta có thể thực hiện khai báo để kiểu dữ liệu đó thuộc class Equal và cung cấp logic xử lý chi tiết cho các phép so sánh.

instance Eq (Maybe m) where Just x == Just y = x == y Just x /= Just y = x /= y -- Nothing == Nothing = True Nothing /= Nothing = False -- _ == _ = False _ /= _ = True

Code ví dụ trên đã tạo ra một thực thể instance từ class Equal để trình biên dịch nạp vào thông tin rằng kiểu Maybe m được khai báo thuộc class Equal và cung cấp logic so sánh chi tiết cho các trường hợp dữ liệu Maybe a. Như vậy chúng ta đã có thể sử dụng các phép so sánh ==/= cho các giá trị thuộc kiểu Maybe a giống như khi làm việc với các giá trị số học; Hoặc có thể nói cách khác là chúng ta đã overload các hàm ==/= để hỗ trợ thêm kiểu Maybe a.

> (Just 9) == (Just 5)
False : Bool

Oh.. như vậy là Type Class là công cụ để hỗ trợ tạo ra các tên hàm phổ biến dùng chung cho nhiều kiểu dữ liệu khác nhau. Và mặc dù các phiên bản của các hàm này được định nghĩa chi tiết ở các module riêng nhưng chúng ta sẽ có thể sử dụng cú pháp gọi hàm chung mà không cần tham chiếu từ tên của các module.

Ví dụ như khi chúng ta tạo ra tên hàm show, hay image, hay toString để chuyển các giá trị bất kỳ có thể có cấu trúc đơn giản hay phức tạp thành chuỗi mô tả. Chúng ta sẽ không cần phải gọi các hàm này theo cú pháp List.show [0, 1], Int.show 9, v.v... mà chỉ đơn giản là show [0, 1] hay show 9.

Elm không hỗ trợ Type Class ở cấp độ cú pháp của ngôn ngữ, tuy nhiên chúng ta có thể nhìn thấy rằng mục đích của Type Class là tạo ra giao diện sử dụng chung cho các hàm xuyên suốt các module. Thêm vào đó là với định hướng tiếp cận người viết code có căn bản JavaScript nên các module của Elm đang sử dụng quy ước đặt tên theo kiểu OOP để chúng ta sử dụng cú pháp gọi hàm tham chiếu từ tên module.

Như vậy chúng ta nên thuận theo quy ước sẵn có của Elm và nghĩ đến một phương thức tạo ràng buộc để trình biên dịch có thể đưa ra thông báo nhắc nhở nếu một module nào đó chưa triển khai một class mà chúng ta quy ước. Cụ thể là chúng ta có thể lưu các hàm vào các record mô phỏng class.

type alias Comparable a = { compare : a -> a -> Order , lt : a -> a -> Bool , le : a -> a -> Bool , gt : a -> a -> Bool , ge : a -> a -> Bool , min : a -> a -> a , max : a -> a -> a }

Sau đó ở các module muốn tạo ràng buộc triển khai class này, chúng ta có thể tạo một hàm implementComparable và trả về một record với định kiểu a được chỉ định cụ thể và các thuộc tính được gắn với các hàm được định nghĩa trong module đó.

module Book exposing (Book, implementComparable) type alias Book = { ... } implementComparable : Comparable Book
implementComparable = Comparable compare lt le gt ge min max

Như vậy trước khi viết logic hoạt động chính của chương trình tại Main, chúng ta có thể gọi tới các hàm implement... ở các module tự định nghĩa để kiểm tra thông báo lỗi. Do các thuộc tính của record Comparable đều được định nghĩa không sử dụng Maybe nên các hàm implement... ở các module buộc sẽ phải được truyền vào đầy đủ các tham số. Trong trường hợp chúng ta quên chưa viết code triển khai chi tiết cho một hàm nào đó ở một module bất kỳ có implement... thì trình biên dịch sẽ đưa ra thông báo lỗi.

(chưa đăng tải) [Functional Programming + Elm] Bài 4 - Functor & Applicative

Bình luận

Bài viết tương tự

- vừa được xem lúc

Closure trong Javascript - Phần 2: Định nghĩa và cách dùng

Các bạn có thể đọc qua phần 1 ở đây. Để mọi người không quên, mình xin tóm tắt gọn lại khái niệm lexical environment:.

0 0 51

- vừa được xem lúc

Var vs let vs const? Các cách khai báo biến và hằng trong Javascript

Dạo này mình tập tành học Javascript, thấy có 2 cách khai báo biến khác nhau nên đã tìm tòi sự khác biệt. Nay xin đăng lên đây để mọi người đọc xong hy vọng phân biệt được giữa let và var, và sau đó là khai báo hằng bằng const.

0 0 31

- vừa được xem lúc

VueJS: Tính năng Mixins

Chào mọi người, hôm nay mình sẽ viết về Mixins và 1 số vấn đề trong sử dụng Mixins hay ho mà mình gặp trong dự án thực. Trích dẫn từ trang chủ của VueJS:.

0 0 27

- vừa được xem lúc

Asset Pipeline là cái chi chi?

Asset Pipeline. Asset pipeline là cái chi chi. . Giải thích:.

0 0 47

- vừa được xem lúc

Tạo data table web app lấy dữ liệu từ Google Sheets sử dụng Apps Script

Google Sheets là công cụ tuyệt vời để lưu trữ bảng tính trực tuyến, bạn có thể truy cập bảng tính bất kỳ lúc nào ở bất kỳ đâu và luôn sẵn sàng để chia sẻ với người khác. Bài này gồm 2 phần.

0 0 266

- vừa được xem lúc

Học Deep Learning trên Coursera miễn phí

Bạn muốn bắt đầu với Deep Learning nhưng không biết bắt đầu từ đâu? Bạn muốn có một công việc ở mức fresher về Deep Learning? Bạn muốn khoe bạn bè về kiến thức Deep Learning của mình. Bắt đầu từ đâu.

0 0 35