First-class citizen

Trong thiết kế ngôn ngữ lập trình nhất định, một **đối tượng hạng nhất** (first-class object) là một thực thể hỗ trợ tất cả các thao tác thường có sẵn cho các thực thể khác. Các thao tác này thường bao gồm việc được truyền như một đối số, được trả về từ một hàm, và được gán cho một biến.

## Lịch sử

Khái niệm về các đối tượng hạng nhất và hạng hai được Christopher Strachey giới thiệu vào thập niên 1960. Ông không thực sự định nghĩa thuật ngữ này một cách chặt chẽ, mà so sánh đối chiếu giữa số thực và thủ tục trong ALGOL:
**Đối tượng hạng nhất và hạng hai.** Trong ALGOL, một số thực có thể xuất hiện trong một biểu thức hoặc được gán cho một biến, và một trong hai có thể xuất hiện như một tham số thực sự trong một lời gọi thủ tục. Mặt khác, một thủ tục chỉ có thể xuất hiện trong một lời gọi thủ tục khác với tư cách là toán tử (trường hợp phổ biến nhất) hoặc là một trong các tham số thực sự. Không có biểu thức nào khác liên quan đến các thủ tục hoặc có kết quả là các thủ tục. Do đó, theo một nghĩa nào đó, các thủ tục trong ALGOL là những công dân hạng hai—chúng luôn phải đích thân xuất hiện và không bao giờ có thể được đại diện bởi một biến hoặc biểu thức (ngoại trừ trường hợp của một tham số hình thức)...

Robin Popplestone đã đưa ra định nghĩa sau:
Tất cả các mục (item) đều có những quyền cơ bản nhất định.
# Tất cả các mục đều có thể là tham số thực sự của hàm
# Tất cả các mục đều có thể được trả về làm kết quả của hàm
# Tất cả các mục đều có thể là chủ thể của các câu lệnh gán
# Tất cả các mục đều có thể được kiểm tra tính bằng nhau.

Trong thập niên 1990, Raphael Finkel đã đề xuất các định nghĩa về giá trị hạng hai và hạng ba, nhưng những định nghĩa này không được áp dụng rộng rãi.

## Ví dụ

Các kiểu dữ liệu vô hướng đơn giản nhất, chẳng hạn như số nguyên và số thực dấu phẩy động, gần như luôn là hạng nhất.

Trong nhiều ngôn ngữ cũ hơn, mảng và chuỗi không phải là hạng nhất: chúng không thể được gán như các đối tượng hoặc được truyền dưới dạng tham số cho một chương trình con. Ví dụ, cả Fortran IV và C đều không hỗ trợ gán mảng, và khi chúng được truyền làm tham số, chỉ vị trí phần tử đầu tiên của chúng thực sự được truyền—kích thước của chúng bị mất đi. C có vẻ như hỗ trợ việc gán các con trỏ mảng, nhưng thực tế đây chỉ đơn giản là các con trỏ trỏ đến phần tử đầu tiên của mảng, và một lần nữa không mang theo kích thước của mảng.

Trong hầu hết các ngôn ngữ, kiểu dữ liệu không phải là đối tượng hạng nhất, mặc dù trong một số ngôn ngữ lập trình hướng đối tượng, lớp là các đối tượng hạng nhất và là thể hiện của các siêu lớp. Các ngôn ngữ thuộc họ lập trình hàm thường cũng có các kiểu hạng nhất, ví dụ như dưới dạng các kiểu dữ liệu đại số tổng quát, hoặc các tiện nghi siêu ngôn ngữ khác cho phép các chương trình triển khai các phần mở rộng cho chính ngôn ngữ triển khai của chúng.

Rất ít ngôn ngữ hỗ trợ sự tiếp nối (continuation) và nhãn GOTO như những đối tượng, chứ chưa nói đến việc là đối tượng hạng nhất.

## Hàm

Nhiều ngôn ngữ lập trình hỗ trợ việc truyền và trả về các giá trị hàm, vốn có thể được áp dụng cho các đối số. Liệu điều này đã đủ để gọi các giá trị hàm là hạng nhất hay chưa vẫn còn là vấn đề tranh cãi.

Một số tác giả yêu cầu phải có khả năng tạo ra các hàm mới trong thời gian chạy (runtime) thì mới gọi chúng là 'hạng nhất'. Theo định nghĩa này, các hàm trong C không phải là các đối tượng hạng nhất; thay vào đó, đôi khi chúng được gọi là **đối tượng hạng hai**, bởi vì chúng vẫn có thể được thao tác theo hầu hết các phương thức nêu trên (thông qua các con trỏ hàm).

Trong Smalltalk, các hàm (phương thức) là những đối tượng hạng nhất, giống hệt như các lớp của Smalltalk. Vì các toán tử của Smalltalk (+, -, v.v.) cũng là các phương thức, nên chúng cũng là những đối tượng hạng nhất.

## Phản chiếu (Reflection)

Một số ngôn ngữ, chẳng hạn như Java và PHP, có một hệ thống con phản chiếu (reflection) rõ ràng cho phép truy cập vào các cấu trúc triển khai bên trong mặc dù chúng không thể được truy cập hoặc thao tác theo cùng một cách như các đối tượng thông thường.

Trong các ngôn ngữ khác, chẳng hạn như những ngôn ngữ thuộc họ Lisp, phản chiếu là một tính năng trung tâm của ngôn ngữ, thay vì là một hệ thống con đặc biệt. Thông thường, điều này mang hình thức của một tập hợp các tính năng sau:
* macro cú pháp hoặc fexpr - cho phép người dùng viết mã xử lý mã như dữ liệu và đánh giá nó theo quyết định riêng, cho phép, ví dụ, các chương trình viết các chương trình (hoặc tự viết lại chính mình) bên trong trình biên dịch, trình thông dịch hoặc thậm chí là trình phân tích cú pháp (các macro trình đọc);
* một trình đánh giá siêu vòng (meta-circular evaluator) - cung cấp định nghĩa về trình đánh giá của ngôn ngữ như một sự trùng ngữ (tautologisation) được biên dịch của chính nó, tạo điều kiện thuận lợi cho việc sửa đổi ngôn ngữ một cách dễ dàng mà không yêu cầu một siêu ngôn ngữ nào khác với chính nó;
* một giao thức siêu đối tượng (metaobject protocol) - một dạng đặc biệt của trình đánh giá siêu vòng cho lập trình hướng đối tượng, trong đó hệ thống đối tượng tự triển khai đệ quy thông qua một hệ thống các siêu lớp và siêu đối tượng, bản thân chúng cũng là các lớp và đối tượng.
Những điều này cho phép các dạng truy cập hạng nhất khác nhau vào việc triển khai ngôn ngữ, và nhìn chung, có thể thao tác theo cùng một cách, và hoàn toàn không thể phân biệt được với các đối tượng ngôn ngữ thông thường. Do đó, việc sử dụng chúng thường đi kèm với một số quy định và lời khuyên (mang tính văn hóa), vì việc người dùng sửa đổi hệ thống lập trình cốt lõi mà chưa được kiểm tra có thể dễ dàng làm suy yếu các tối ưu hóa hiệu suất do những người triển khai ngôn ngữ tạo ra.

## Xem thêm
* Hàm hạng nhất
* Cụ thể hóa (Reification)

## Ghi chú

## Tham khảo