Introduction Crystal - Fast as C, Slick as Ruby

Bạn là một developer? Chắc hẳn bạn cũng biết đến ngôn ngữ C (sure), và bạn đã từng nghe thấy/ làm việc với Ruby on Rails? OK, nói đến C thì người ta thường nhắc đến ưu điểm là đến sức mạnh và tốc độ rất nhanh của C; còn nói đến Ruby, người ta hay nói về sự tiện dụng và cú pháp đơn giản, dễ hiểu, mỗi dòng code như một lời giải thích về ý nghĩa của nó, đơn giản là dễ hiểu.

Tuy nhiên, người ta cũng hay phàn nàn rằng, cú pháp C khó viết quá, còn Ruby thì tốc độ chậm chạp quá. Bạn đã từng ước ao có một ngôn ngữ nào đó có cả tốc độ, sức mạnh lẫn cú pháp đơn giản dễ hiểu chưa?

“Have you ever dreamed of a programming language as beautiful as Ruby but as fast as C?”

Hôm nay, tôi xin giới thiệu với các bạn ngôn ngữ Crystal - ngôn ngữ mà các bạn đang hằng mong ngóng.

Là một lập trình viên Ruby và yêu thích Ruby tuy nhiên tôi cũng không thể phủ nhận rằng tốc độ trung bình của nó khá chậm so với các ngôn ngữ khác, và Crystal đã khắc phục được điều đó!?

Khi tôi biết đến Crystal, tôi đã rất ấn tượng với lời giới thiệu “Crystal: Fast as C, Slick as Ruby.” Vậy có phải ngôn ngữ này thật sự ảo diệu như thế không?

Từng bước một, chúng ta sẽ tìm hiểu cách làm việc của Crystal.

Chương trình kinh điển đầu tiên:

crystal hello.rb
p "Hello World!"

và kết quả sau khi chạy

Hello World!

Chúng ta hãy cùng xem xét kĩ hơn các mục tiêu của Crystal

  • Có cú pháp giống như Ruby (tuy nhiên khả năng kết hợp với Ruby không phải là mục đích hướng tới)
  • Không cần phải kiểm tra kiểu của biến hoặc đối số
  • Có thời gian biên dịch đánh giá và biên dịch code, tránh soạn sẵn code.
  • Biên dịch code hiệu quả

Đây là những điểm quan trọng nhất mà chúng ta cần nhấn mạnh. Đầu tiên và quan trọng nhất:

có cú pháp tương tự như Ruby (nhưng khả năng kết hợp với Ruby không phải là mục đích hướng tới)

Điều này được giải thích: Crystal không phải là Ruby, và Crystal không thể chạy được Rails.

Mặc dù là kiểu statically typed, nhưng Crystal lại không định nghĩa kiểu của biến. hoặc đối số của method

Không giống như Ruby(dynamic typing), Crystal là loại ngôn ngữ static typing, tuy nhiên nó lại không yêu cầu định nghĩa kiểu dữ liệu đối các biến và các đối số của method ( giống như Ruby).

example:

def greet(name, age)
  "I'm #{name}, #{age} years old."
end

greet "Bap", 24 # I'm Bap, 24 years old.

Nhưng, nếu bạn muốn định nghĩa kiểu dữ liệu thì phải làm đúng những gì mình đã định nghĩa

example:

def add(x : Number, y : Number)
  x + y
end

# Ok
add 1, 2 # Ok

# Error: no overload matches 'add' with types Bool, Bool
add true, false

Đây là một trường hợp compile lỗi, chúng ta hạn chế kiểu của đối số method là Number, tuy nhiên lại truyền vào Boolean, do đó lỗi xảy ra.

Have compile-time evaluation and generation of code, to avoid boilerplate code.

Ruby nổi tiếng với khả năng metaprogramming, còn Crystal sử dụng macros để đạt được điều đó trong thời gian biên dịch code. Đây là một ví dụ từ Kemal, một trang web tuyệt vời cho framwork Crystal

HTTP_METHODS = %w(get post put patch delete options)

{% for method in HTTP_METHODS %}
  def {{method.id}}(path, &block : HTTP::Server::Context -> _)
   Kemal::RouteHandler::INSTANCE.add_route({{method}}.upcase, path, &block)
  end
{% end %}

Crystal là một ngôn ngữ compile, tôi sẽ không đi sâu vào giới thiệu về những điểm mạnh của compiler, nhưng chúng ta có thể dễ dàng nói rằng điều đó cho phép chúng ta optimation for free. Bên cạnh đó, khi Crystal là một ngôn ngữ compiler, nó có thể biên dịch từng file, naitive code.

Đây là cách ta có thể biên dịch một file Crystal:

crystal build program.cr
./program

Awsome!

Crystal’s Fantastic Standard Library

Crystal đi kèm với một thư viện chuẩn và các công cụ tuyệt vời. Nó có tất cả những thứ bạn cần để xây dựng một ứng dụng hiện đại. CSV, YAML, JSON, HTTP, và ngay cả WebSocket được bundled với cả Crystal. Điều đó làm nó super simple để xây dựng một ứng dụng gì đó.

Cần một server? No problem.

# server.cr
require "http/server"

server = HTTP::Server.new(8080) do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world, got #{context.request.path}!"
end

puts "Listening on http://0.0.0.0:8080"
server.listen

Cổng mặc định là 8080. alt

alt

Crystal’s Performance

Vấn đề thứ nhất là cú pháp giống với Ruby, chắc hẳn mọi người cũng đã hình dung ra phần nào từ phần giới thiệu bên trên.

Sau đây, tôi sẽ giải thích đến vấn đề thứ hai, đó là tốc độ, performance của Crystal.

Mục tiêu của Crystal là mượt mà như Ruby, nhưng về performance thì không.

I love benchmarks, but remember, benchmarks should be taken with a grain of salt.

Sau đây, tôi xin kể một câu chuyện về tính toán dãy số Fibonacci thực hiện cho Crystal (cũng phù hợp với ruby):

# fib.cr
def fib(n)
  if n <= 1
    1
  else
    fib(n - 1) + fib(n - 2)
  end
end

puts fib(42)

Thử chạy và xem hết bao lâu

time crystal fib.cr
433494437
crystal fib.cr  2.45s user 0.33s system 98% cpu 2.833 total

Thử test với Ruby

time ruby fib.cr
433494437
ruby fib.cr  38.49s user 0.12s system 99% cpu 38.718 total

từ kết quả trả về: Crystal hết 2.54s Ruby hết 38.49s Thật là hư cấu!?

Điều gì xảy ra nếu chúng ta compile với program với optimations turned on?

crystal build --release fib.cr

Kết quả:

time ./fib
433494437
./fib  1.11s user 0.00s system 99% cpu 1.113 total

alt

1.113 seconds. Ngày càng hư cấu!?

Điều gì xảy ra nếu chúng ta compile với program với optimations turned on?

crystal build --release fib.cr

Kết quả:

time ./fib
433494437
./fib  1.11s user 0.00s system 99% cpu 1.113 total

alt

1.113 seconds. Ngày càng hư cấu!?

Nếu bạn thực sự muốn chuẩn, hãy kiểm tra repo crystal game

Đây là điều bạn nên nhớ

The cake is a lie, and so are benchmarks. You won’t have 35x increase in performance all the time, but you can expect 5x or more in complex applications, more if it’s CPU intensive.

Concurrency in Crystal