Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: Làm quen với Clojure 1901

JVM
ngocdaothanh.myopenid.com 172
Updated about 1 year ago

Java Virtual Machine (JVM) là nền tảng hấp dẫn vì có nhiều thư viện nhất trong các loại nền tảng và tốc độ nhanh. Ngôn ngữ chỉ là tập hợp các qui luật logic, bất kì ngôn ngữ nào cũng chỉ cần dịch sang byte code theo đúng chuẩn JVM là chạy được trên nó, nên ngoài Java trên JVM hiện có cả trăm ngôn ngữ.

Xu hướng hiện nay là đa lõi, hiện có 2 ngôn ngữ nổi bật trên JVM hỗ trợ đa lõi là Scala và Clojure. Bài Khủng hoảng đa lõi: Scala vs. Erlang đã đề cập Scala, bài này giới thiệu Clojure bằng cách so sánh với Scala và Erlang, sau đó hướng dẫn cách thiết lập môi trường học.

So sánh với Scala và Erlang

Đa nhiệm có 2 mục tiêu: tận dụng hiệu quả sức mạnh xử lí của 1 máy và của nhiều máy. Do hạn chế của JVM nên Clojure và Scala chỉ làm tốt mục tiêu đầu, còn Erlang làm tốt cả 2. JVM dùng native thread của hệ điều hành nên Clojure và Scala chỉ chạy song song hiệu quả chừng vài ngàn công việc, vì các hệ điều hành chỉ chạy hiệu quả tối đa chừng vài ngàn thread, hơn nữa thì hệ điều hành sẽ không ổn định thậm chí treo máy. Erlang dùng green thread nên tốc độ tạo thread cực nhanh cỡ nano giây và số lượng thread tạo ra có thể lên đến cả triệu. Khi tải lên cao (nhiều công việc đến dồn dập cùng lúc), chương trình Erlang chỉ chạy chậm lại chứ ít có khả năng chết vì hệ thống hết tài nguyên (CPU và bộ nhớ) như chương trình Clojure và Scala. Tuy vậy với loại chương trình chuyên về tính toán số học thì Clojure và Scala nhanh hơn Erlang, do đó:

  • Nên dùng Clojure hoặc Scala nếu chương trình thiên về tính toán chỉ cần chạy tốt trên 1 máy, mỗi máy chỉ cần chạy song song chừng vài ngàn công việc, và ít cần truyền thông tin giữa nhiều máy.
  • Nên dùng Erlang khi chương trình cần độ song song cao trên 1 máy và nhiều máy, chịu được tải và độ tin cậy cao.

Erlang ra năm 1986, Scala ra năm 2003, Clojure ra sau Scala 4 năm nên chưa phát triển bằng. Tuy vậy hiện Clojure có động lực phát triển mạnh hơn Scala vì một số lí do:

  • Scala kết hợp cả lập trình hướng đối tượng (OOP) và lập trình hàm (FP), cú pháp phức tạp còn hơn cả Java. Trong khi đó Clojure chỉ là một biến thể (dialect) của Lisp, thuần FP nên cú pháp đơn giản hơn rất nhiều.
  • Scala là ngôn ngữ tĩnh, phải dịch rồi mới chạy được. Clojure là ngôn ngữ động, không cần dịch trước khi chạy (Ruby cũng động như cú pháp phức tạp hơn Clojure nhiều nên JRuby chậm hơn Clojure do mã Ruby khó dịch hiệu quả ra mã JVM hơn Clojure).
  • Yếu tố lai giữa OOP và FP của Scala vừa là ưu và là nhược điểm. Ưu vì cú pháp giống Java nên dễ học với người đã biết Java, nhược vì người trước nay chỉ quen viết OOP sẽ có xu hướng viết Scala theo kiểu OOP, ít tận dụng được tính năng xử lí đa lõi của Scala. 
  • Clojure là biến thể của Lisp, mà số người biết ít nhất một trong những biến thể của Lisp (vì được dạy ở trường) rất đông đảo, nhất là trong giới nghiên cứu khoa học máy tính.

Cả Clojure và Scala đều có thể gọi trực tiếp thư viện viết bằng Java và ngược lại (tuy vậy cú pháp gọi Java của Clojure tốt hơn). Nếu phải chọn học 1 trong 2 ngôn ngữ để học, thì có lẽ nên:

  • Chọn Scala nếu cần làm gấp project gì đó vì Scala nhiều tài liệu hơn, cú pháp giống Java nên khi mới bắt đầu học thì tốc độ học nhanh hơn Clojure. Scala có web framework Lift (đã có ebook) được quảng cáo là tốt nhất trong các web frame work chạy trên JVM hiện nay, vượt cả Rails/JRuby. Ví dụ Twitter đã chuyển xử lí cần tốc độ thật nhanh lúc đầu viết bằng Ruby sang Scala.
  • Nên chọn Clojure nếu chưa biết FP là gì. Người ta bảo ngôn ngữ nếu không làm thay đổi suy nghĩ của mình thì không đáng học. Clojure dựa trên Lisp mà Lisp là ngôn ngữ FP cổ xưa thứ 2 (sau Forth) và có lẽ là duy nhất còn sống khoẻ đến ngày nay. Scala lai giữa OOP và FP nên mặc dù dễ làm quen hơn nhưng nếu trước giờ chỉ quen mỗi OOP thì khi viết chương trình bạn sẽ có xu hướng viết theo kiểu OOP (vậy thì dùng Java quách cho xong{#emotions_dlg.tongue_out}), không tạo được thay đổi gì trong suy nghĩ cho cái đầu của mình.

2 mô hình xử lí song song được hiệu quả nhất hiện nay là actor và STM. Erlang và Scala theo mô hình actor, Clojure theo mô hình STM. Actor giải quyết được vấn đề xử lí song song ở cả 2 trường hợp trên cùng một máy và trên nhiều máy khác nhau. STM thì chỉ giải quyết được vấn đề trên một máy. Tuy vậy, chủ trương của Clojure là dùng STM để tận dụng tối đa đặc tính của việc việc chạy trên một máy là bộ nhớ có thể dùng chung, giải quyết thật tốt vấn đề một máy trước, rồi trong tương lai mới tìm cách giải quyết vấn đề nhiều máy sau (dùng JMS chẳng hạn).

Học Scala sẽ có cảm giác nó tham lam tích hợp đủ thứ tính năng hầm bà lằng. Học Clojure sẽ có cảm giác nó đẹp. Nếu thích Windows hơn Mac thì có lẽ bạn sẽ thích Scala. Nếu ngược lại thì có lẽ bạn sẽ thích Clojure. Còn nếu chưa biết Mac là gì thì bạn nên học Clojure{#emotions_dlg.tongue_out}.

Làm quen

Clojure gồm phần core ngôn ngữ (giống corelib của Ruby) và phần contrib gồm các thư viện đa dạng (giống stdlib của Ruby). Thư viện contrib nào liên quan nhiều đến bản thân ngôn ngữ thì sẽ được đưa vào core. Thư viện nào tốt được xử dụng nhiều thì sẽ được đưa vào contrib. Hiện lí do duy nhất để nhiều người bám víu vào Java chỉ vì JVM nhiều thư viện. Do đó phần contrib cực kì quan trọng, hãy để ý tập dùng được càng nhiều thư viện trong contrib càng tốt.

Mặc dù Clojure đã ra bản 1.0, nhưng có lẽ tốt nhất nên lấy cả core và contrib từ repository trên GitHub. Ta sẽ cài đặt trên Linux đã có sẵn Java và Ant theo cấu trúc thư mục sau:

~/opt
|- clojure
| |- clojure.jar, jline.jar
| \- clj.sh, repl.sh
|
\- clojure-contrib
\- clojure-contrib.jar

Cài core

$ git clone git://github.com/richhickey/clojure.git
$ cd clojure
$ ant

Tập tin clojure.jar sẽ được tạo ra. Chạy thử chương trình tương tác từ bàn phím (mã ta viết không cần biên dịch trước khi chạy, giống Ruby):

$ java -cp clojure.jar clojure.main
user=> (println "Hello World")
Hello World
nil

Thêm JLine

REPL (Read Evalucate Print Loop, nghĩa là vòng lặp để chờ dịch những gì ta gõ từ bàn phím xong, chạy rồi in kết quả ra màn hình) ở trên hơi kém là không hỗ trợ các phím mũi tên. Ta download jline.jar về rồi tạo tập tin tiện ích repl.sh như sau:

java -cp $(dirname $0)/jline.jar:$(dirname $0)/clojure.jar jline.ConsoleRunner clojure.main

Thường ta muốn lưu chương trình (println "Hello World") thành tập tin hello.clj rồi chạy từ dòng lệnh. Ta tạo tập tin tiện ích clj.sh như sau:

java -server -cp $(dirname $0)/clojure.jar clojure.main $1

Nhớ chmod +x cho 2 tập tin trên và đặt đường dẫn đến thư mục chứa chúng vào biến môi trường PATH. Sau đó từ bất kì thư mục chứa hello.clj nào chỉ cần gõ lệnh clj hello.clj là xong.

Để ý tham số -sever:

  • Tham số này giúp tận dụng tối đa khả năng đa lõi. JVM sẽ khởi động lâu hơn nhưng bù lại mã máy sinh ra từ byte code sẽ tối ưu hơn.
  • REPL không cần tham số này vì thường khi tương tác bàn phím là lúc ta cần khởi động nhanh.

Thêm contrib

$ git clone git://github.com/richhickey/clojure-contrib.git
$ cd clojure-contrib
$ ant -Dclojure.jar=../clojure/clojure.jar

clojure-contrib.jar sẽ được tạo ra. Chạy thử:

$ java -cp ../clojure/clojure.jar:clojure-contrib.jar:../clojure/jline.jar jline.ConsoleRunner clojure.main
user=> (use 'clojure.contrib.json.read)
nil
user=> (read-json "{\"x\": 1}")
{"x" 1}

Unicode

Trên một số platform, ví dụ Mac OS, để gõ và hiển thị đúng Unicode tiếng Việt tiếng Nhật v.v. cần thêm tham số cho lệnh java: -Dfile.encoding=UTF-8

Cách học

Trước tiên, nên biết Java trước khi học Clojure. Sau đó, nếu đã biết Lisp rồi thì quá khoẻ vì Clojure chỉ là một phiên bản của Lisp, giống JRuby so với Ruby. Phần hướng dẫn cách học ở dưới giả sử bạn chưa biết Lisp.

Hiện nay (thời điểm tháng 10/2009) chỉ mới có một quyển sách về Clojure là Programming Clojure. Khi đọc sách mà không hiểu gì lắm thì lỗi thường do người viết hơn là bản thân người đọc, quyển Programming Clojure rơi vào trường hợp này. Từ đầu đến cuối sách người đọc chỉ được cưỡi ngựa xem hoa, đọc xong không nắm được Clojure một cách có hệ thống.

Một cách thực dụng có hệ thống, cần tìm hiểu kĩ các chủ để sau khi học Clojure:

  • Cú pháp ngoặc ()
  • Các form căn bản như def, defn, fn, if
  • Sequence và list, vector, hash, set
  • Lập trình concurrent và var, atom, ref, và agent
  • Cách liên kết với Java

Nắm kĩ được các chủ đề trên là yếu tố quyết định để có căn bản Clojure vững chắc. Ở đây ta chỉ bàn thêm về chủ đề căn bản nhất là cú pháp ngoặc, các chủ đề khác trên mạng có đầy.

Cú pháp ngoặc có 3 điểm quan trọng:

  • Khi list được quote thì nó chỉ là giá trị literal (khối dữ liệu).
  • Khi list không được quote thì nó là lời gọi hàm: (1) list sẽ được evaluate và (2) phần tử đầu tiên gọi là form sẽ quyết định list sẽ được evaluate ra giá trị gì.
  • Dấu ngoặc chỉ để dành cho máy, thụt đầu dòng (indent, tuy đơn vị thụt không cố định là 2 hay 4 khoảng trắng như đa số ngôn ngữ) mới dành cho người. Khi nhìn mã nguồn, hãy nhìn thụt đầu dòng và làm như không thấy sự tồn tại của dấu ngoặc. Lúc đạt đến cảnh giới nhìn mà không thấy dấu ngoặc nữa là lúc bạn đã lên đến niết bàn của Lisp{#emotions_dlg.wink}. Việc nhìn mà không thấy này chính ra lại là điều quan trọng nhất đối với người chưa quen với cú pháp của Lisp. Khi đã làm được việc này rồi, thì sẽ thấy mã nguồn của Clojure cũng dễ đọc như mã nguồn của các ngôn ngữ khác. Để dễ dàng làm được việc này, cần giúp sức của trình soạn thảo văn bản, ví dụ Emacs hoặc Eclipse.
  • (a b c d) thì có thể coi a là tên hàm, những cái còn lại là tham số. Để dễ hiểu thường phải đọc theo thứ tự từ phải sang trái: tìm hiểu xem d là gì trước, sau đó đến c v.v.

Đọc thêm

Comments

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content