Computer >> Máy Tính >  >> Lập trình >> Ruby

Hiểu RBS, Hệ thống chú thích kiểu mới của Rubys

RBS là tên của một ngôn ngữ định dạng cú pháp kiểu mới cho Ruby. RBS cho phép bạn thêm chú thích kiểu vào mã Ruby của mình trong các tệp có phần mở rộng mới có tên là .rbs . Chúng trông như thế này:

class MyClass
  def my_method : (my_param: String) -> String
end

Bằng cách cung cấp các chú thích kiểu với RBS, bạn sẽ nhận được các lợi ích như:

  • một cách rõ ràng và ngắn gọn để xác định cấu trúc cơ sở mã của bạn.
  • một cách an toàn hơn để thêm các loại vào mã kế thừa của bạn thông qua tệp thay vì trực tiếp thay đổi các lớp.
  • tiềm năng tích hợp toàn cầu với các bộ kiểm tra loại tĩnh và động.
  • các tính năng mới để giải quyết tình trạng quá tải phương thức, gõ vịt, giao diện động và hơn thế nữa.

Nhưng đợi đã! Chưa có công cụ kiểm tra kiểu tĩnh như Sorbet và Steep?

Vâng, và chúng rất tuyệt! Tuy nhiên, sau bốn năm thảo luận và một số công cụ kiểm tra kiểu do cộng đồng xây dựng, nhóm cam kết Ruby nghĩ rằng đã đến lúc xác định một số tiêu chuẩn để xây dựng các công cụ kiểm tra kiểu.

RBS chính thức là một ngôn ngữ và nó sẽ ra đời cùng với Ruby 3.

Ngoài ra, do tính chất được nhập động của Ruby, cũng như các mẫu phổ biến như Duck Typing và quá tải phương thức, một số biện pháp phòng ngừa đang được thiết lập để đảm bảo cách tiếp cận tốt nhất có thể, chúng ta sẽ xem chi tiết hơn ngay sau đây.

Cài đặt Ruby 3

Để làm theo các ví dụ được hiển thị ở đây, chúng ta cần cài đặt Ruby 3.

Bạn có thể làm điều đó bằng cách làm theo hướng dẫn chính thức hoặc thông qua ruby-build trong trường hợp bạn cần quản lý nhiều phiên bản Ruby.

Ngoài ra, bạn cũng có thể cài đặt rbs đá quý trực tiếp vào dự án hiện tại của bạn:

gem install rbs

Nhập tĩnh so với nhập động

Trước khi đi sâu hơn, chúng ta hãy làm rõ khái niệm này. Làm thế nào để ngôn ngữ được nhập động so với ngôn ngữ tĩnh?

Trong các ngôn ngữ được nhập động, chẳng hạn như Ruby và JavaScript, không có kiểu dữ liệu được xác định trước nào để trình thông dịch hiểu cách tiến hành trong trường hợp một thao tác bị cấm xảy ra trong thời gian chạy.

Điều đó trái ngược với những gì mong đợi từ tính năng nhập tĩnh. Các ngôn ngữ được nhập tĩnh, chẳng hạn như Java và C, xác minh các kiểu trong thời gian biên dịch.

Lấy đoạn mã Java sau làm tham chiếu:

int number = 0;
number = "Hi, number!";

Điều này không thể thực hiện được khi nhập tĩnh, vì dòng thứ hai sẽ báo lỗi:

error: incompatible types: String cannot be converted to int

Bây giờ, hãy lấy ví dụ tương tự trong Ruby:

number = 0;
number = "Hi, number!";
puts number // Successfully prints "Hi, number!"

Trong Ruby, kiểu của một biến thay đổi nhanh chóng, có nghĩa là trình thông dịch biết cách chuyển đổi động từ cái này sang cái khác.

Khái niệm đó thường bị nhầm lẫn với được đánh máy mạnh so với gõ yếu ngôn ngữ.

Ruby không chỉ là một ngôn ngữ được gõ động mà còn được gõ mạnh, có nghĩa là nó cho phép một biến thay đổi kiểu của nó trong thời gian chạy. Tuy nhiên, nó không cho phép bạn thực hiện các thao tác trộn kiểu điên rồ.

Lấy ví dụ tiếp theo (trong Ruby) phỏng theo ví dụ trước:

number = 2;
sum = "2" + 2;
puts sum

Lần này, chúng tôi đang thử tính tổng của hai số thuộc các kiểu khác nhau (Integer và một String ). Ruby sẽ gặp lỗi sau:

main.rb:2:in `+': no implicit conversion of Integer into String (TypeError)
  from main.rb:2:in `<main>'

Nói cách khác, Ruby nói rằng công việc thực hiện các phép tính phức tạp liên quan đến (có thể) các loại khác nhau là của bạn.

Mặt khác, JavaScript yếu được nhập động, cho phép cùng một mã với một kết quả khác:

> number = 2
  sum = "2" + 2
  console.log(sum)
> 22 // it concatenates both values

RBS khác với Sorbet như thế nào?

Đầu tiên, từ cách tiếp cận mà mỗi người thực hiện để chú thích mã. Trong khi Sorbet hoạt động bằng cách thêm các chú thích rõ ràng trong toàn bộ mã của bạn, RBS chỉ yêu cầu tạo một tệp mới với .rbs phần mở rộng.

Ưu điểm chính của việc này là khi chúng ta nghĩ về việc di chuyển các cơ sở mã kế thừa. Vì các tệp gốc của bạn sẽ không bị ảnh hưởng nên việc áp dụng các tệp RBS vào các dự án của bạn sẽ an toàn hơn nhiều.

Theo những người tạo ra nó, mục tiêu chính của RBS là mô tả cấu trúc của các chương trình Ruby của bạn. Nó chỉ tập trung vào việc xác định chữ ký lớp / phương thức.

Bản thân RBS không thể gõ kiểm tra. Mục tiêu của nó được giới hạn trong việc xác định cấu trúc làm cơ sở cho những người kiểm tra kiểu (như Sorbet và Steep) thực hiện công việc của họ.

Hãy xem một ví dụ về thừa kế Ruby đơn giản:

class Badger
    def initialize(brand)
      @brand = brand
    end

    def brand?
      @brand
    end
end

class Honey < Badger
  def initialize(brand: "Honeybadger", sweet: true)
    super(brand)
    @sweet = sweet
  end

  def sweet?
    @sweet
  end
end

Tuyệt quá! Chỉ hai lớp với một vài thuộc tính và kiểu suy luận.

Dưới đây, chúng ta có thể thấy một biểu diễn RBS có thể có:

class Brand
  attr_reader brand : String

  def initialize : (brand: String) -> void
end

class Honey < Brand
  @sweet : bool

  def initialize : (brand: String, ?sweet: bool) -> void
  def sweet? : () -> bool
end

Khá giống nhau, phải không? Sự khác biệt chính ở đây là các loại. initialize phương thức của Honey chẳng hạn, lớp nhận một String và một boolean tham số và không trả về.

Trong khi đó, nhóm Sorbet đang làm việc chặt chẽ trong việc tạo ra các công cụ để cho phép khả năng tương tác giữa RBI (phần mở rộng mặc định của Sorbet cho định nghĩa loại) và RBS.

Mục tiêu là tạo nền tảng cho Sorbet và bất kỳ trình kiểm tra kiểu nào để hiểu cách sử dụng các tệp định nghĩa kiểu của RBS.

Công cụ giàn giáo

Nếu bạn đang bắt đầu với ngôn ngữ và đã có một số dự án đang diễn ra, có thể khó đoán được vị trí và cách bắt đầu nhập những thứ xung quanh.

Với ý nghĩ này, nhóm Ruby đã cung cấp cho chúng tôi một công cụ CLI rất hữu ích có tên là rbs sang các loại giàn giáo cho các lớp hiện có.

Để liệt kê các lệnh có sẵn, chỉ cần nhập rbs help trong bảng điều khiển và kiểm tra kết quả:

Hiểu RBS, Hệ thống chú thích kiểu mới của Rubys Các lệnh có sẵn trong công cụ CLI.

Có lẽ lệnh quan trọng nhất trong danh sách là prototype vì nó phân tích AST của tệp mã nguồn được cung cấp dưới dạng tham số để tạo mã RBS "gần đúng".

Gần đúng vì nó không hiệu quả 100%. Vì mã kế thừa của bạn chủ yếu không được định kiểu, nên hầu hết nội dung được dàn dựng của nó sẽ có cùng một cách. Ví dụ:RBS không thể đoán một số loại nếu không có nhiệm vụ rõ ràng.

Hãy lấy một ví dụ khác làm tài liệu tham khảo, lần này liên quan đến ba lớp khác nhau trong một kế thừa theo tầng:

class Animal
    def initialize(weight)
      @weight = weight
    end

    def breathe
      puts "Inhale/Exhale"
    end
end

class Mammal < Animal
    def initialize(weight, is_terrestrial)
      super(weight)
      @is_terrestrial = is_terrestrial
    end

    def nurse
      puts "I'm breastfeeding"
    end
end

class Cat < Mammal
    def initialize(weight, n_of_lives, is_terrestrial: true)
        super(weight, is_terrestrial)
        @n_of_lives = n_of_lives
    end

    def speak
        puts "Meow"
    end
end

Chỉ là các lớp đơn giản với các thuộc tính và phương thức. Lưu ý rằng một trong số chúng được cung cấp giá trị boolean mặc định, điều này sẽ rất quan trọng để chứng minh RBS có khả năng gì khi tự đoán.

Bây giờ, để tạo ra những kiểu này, hãy chạy lệnh sau:

rbs prototype rb animal.rb mammal.rb cat.rb

Bạn có thể chuyển bao nhiêu tệp Ruby tùy thích. Sau đây là kết quả của việc thực thi này:

class Animal
  def initialize: (untyped weight) -> untyped

  def breathe: () -> untyped
end

class Mammal < Animal
  def initialize: (untyped weight, untyped is_terrestrial) -> untyped

  def nurse: () -> untyped
end

class Cat < Mammal
  def initialize: (untyped weight, untyped n_of_lives, ?is_terrestrial: bool is_terrestrial) -> untyped

  def speak: () -> untyped
end

Như chúng tôi đã dự đoán, RBS không thể hiểu hầu hết các loại mà chúng tôi hướng tới khi tạo các lớp.

Hầu hết công việc của bạn sẽ là thay đổi untyped theo cách thủ công tham chiếu đến những cái thật. Một số cuộc thảo luận nhằm tìm ra những cách tốt hơn để thực hiện điều này đang diễn ra ngay trong cộng đồng.

Siêu lập trình

Khi nói đến lập trình siêu hình, rbs công cụ sẽ không giúp ích nhiều do tính chất động của nó.

Lấy ví dụ về lớp sau:

class Meta
    define_method :greeting, -> { puts 'Hi there!' }
end

Meta.new.greeting

Sau đây là kết quả của loại giàn giáo này:

class Meta
end

Vịt gõ

Ruby không lo lắng quá nhiều về bản chất của đối tượng (loại của chúng) nhưng quan tâm đến khả năng của chúng (những gì chúng làm).

Gõ vịt là một kiểu lập trình nổi tiếng hoạt động theo phương châm:

"Nếu một đối tượng hoạt động như một con vịt (nói, đi, bay, v.v.), thì đó là một con vịt"

Nói cách khác, Ruby sẽ luôn coi nó như một con vịt mặc dù định nghĩa và loại ban đầu của nó không được cho là đại diện cho một con vịt.

Tuy nhiên, cách gõ vịt có thể ẩn các chi tiết của việc triển khai mã mà có thể dễ dàng trở nên phức tạp và khó tìm / đọc.

RBS đã giới thiệu khái niệm về loại giao diện , là một tập hợp các phương thức không phụ thuộc vào bất kỳ lớp hoặc mô-đun cụ thể nào.

Hãy lấy ví dụ về kế thừa động vật trước đó và giả sử rằng chúng ta đang thêm một cấp phân cấp mới cho động vật trên cạn mà từ đó Cat của chúng ta sẽ kế thừa thành:

class Terrestrial < Animal
    def initialize(weight)
        super(weight)
    end

    def run
        puts "Running..."
    end
end

Để tránh các đối tượng con ngoài mặt đất chạy, chúng ta có thể tạo một giao diện để kiểm tra loại cụ thể cho một hành động như vậy:

Giao diện
interface _CanRun
  # Requires `<<` operator which accepts `Terrestrial` object.
  def <<: (Terrestrial) -> void
end

Khi ánh xạ mã RBS với phương thức chạy cụ thể, đó sẽ là chữ ký:

def run: (_CanRun) -> void

Bất cứ khi nào ai đó cố gắng chuyển bất kỳ thứ gì khác ngoài đối tượng Terrestrial vào phương thức, trình kiểm tra kiểu sẽ đảm bảo ghi lại lỗi.

Loại Liên minh

Các Rubyists cũng phổ biến khi có các biểu thức chứa các loại giá trị khác nhau.

def fly: () -> (Mammal | Bird | Insect)

RBS hỗ trợ các loại liên hiệp bằng cách chỉ cần kết hợp chúng qua pipe nhà điều hành.

Quá tải phương thức

Một thực tế phổ biến khác (trong số nhiều ngôn ngữ lập trình trên thực tế) là cho phép nạp chồng phương thức, trong đó một lớp có thể có nhiều phương thức có cùng tên nhưng chữ ký khác nhau (như kiểu hoặc số lượng tham số, thứ tự của chúng, v.v. ).

Hãy lấy một ví dụ trong đó một loài động vật có thể trả lại những người anh em họ tiến hóa gần nhất của nó:

def evolutionary_cousins: () -> Enumerator[Animal, void] | { (Animal) -> void } -> void

Bằng cách này, RBS cho phép chúng tôi xác định rõ ràng liệu một loài động vật nhất định sẽ có một người anh em họ tiến hóa duy nhất hay một nhóm chúng.

TypeProf

Song song đó, nhóm Ruby cũng đã bắt đầu một dự án mới có tên là typeprof, một trình thông dịch Ruby cấp kiểu thử nghiệm nhằm mục đích phân tích và (cố gắng) tạo nội dung RBS.

Nó hoạt động bằng cách diễn giải trừu tượng và vẫn đang thực hiện những bước đầu tiên để cải tiến tốt hơn, vì vậy hãy cẩn thận khi sử dụng nó cho mục đích sản xuất.

Để cài đặt nó, chỉ cần thêm đá quý vào dự án của bạn:

gem install typeprof

Lưu ý rằng nó yêu cầu phiên bản Ruby lớn hơn 2.7.

Sử dụng phiên bản sau của Animal lớp:

class Animal
    def initialize(weight)
      @weight = weight
    end

    def die(age)
      if age > 50
        true
      elsif age <= 50
        false
      elsif age < 0
        nil
      end
    end
end

Animal.new(100).die(65)

Dựa trên những gì đang diễn ra bên trong phương thức age và cuộc gọi tiếp theo cho cùng một phương thức, TypeProf có thể suy ra một cách thông minh các kiểu được thao tác trong mã.

Khi bạn chạy typeprof animal.rb , đó phải là đầu ra:

## Classes
class Animal
  @weight: Integer

  def initialize: (Integer weight) -> Integer
  def die: (Integer age) -> bool?
end

Đó là một công cụ mạnh mẽ có rất nhiều thứ để cung cấp cho các dự án đã có nhiều mã đang diễn ra.

Tích hợp mã VS

Hiện tại, không có nhiều plugin VS Code có sẵn để xử lý RBS để định dạng, kiểm tra cấu trúc, v.v. Đặc biệt là vì nó vẫn còn tương đối mới.

Tuy nhiên, nếu bạn tìm kiếm "RBS" trong cửa hàng, bạn có thể tìm thấy một plugin có tên là ruby-signature sẽ giúp làm nổi bật cú pháp, như được hiển thị bên dưới:

Hiểu RBS, Hệ thống chú thích kiểu mới của Rubys Đánh dấu cú pháp RBS trong VS Code.

Kết luận

RBS rất mới mẻ và đã đại diện cho một bước quan trọng hướng tới các cơ sở mã Ruby an toàn hơn.

Thông thường, theo thời gian các công cụ mới và các dự án nguồn mở sẽ hỗ trợ nó, chẳng hạn như RBS Rails để tạo tệp RBS cho các ứng dụng Ruby on Rails.

Tương lai mang đến những điều tuyệt vời cho cộng đồng Ruby với các ứng dụng an toàn hơn và không có lỗi hơn. Nóng lòng muốn xem nó!