Đôi điều về coding style

Đôi điều về coding style
Photo by Max Chen from Unsplash

Coding style (programming style) là bộ các quy tắc hoặc hướng dẫn được sử dụng bởi các lập trình viên khi viết mã nguồn phần mềm. Các lập trình viên thường được yêu cầu tuân thủ các quy tắc này để đảm bảo tính dễ đọc và dễ hiểu của mã nguồn, phòng tránh lỗi tiềm ẩn. Bởi vì vòng đời của một phần mềm, thường quá trình maintain nhiều hơn là quá trình viết code. Nên việc đọc, hiểu mã nguồn là rất quan trọng.

Tuy nhiên, rất nhiều quy ước đặt ra được các lập trình viên chấp nhận, nhưng không hiểu lý do vì sao. Bài viết này sẽ tìm hiểu một số quy ước như vậy.

Line length: why always 80?

Một quy ước được áp dụng vô cùng rộng rãi, đó là giới hạn số ký tự trên mỗi dòng không quá 80 ký tự. Tôi đã làm việc với rất nhiều coding style khác nhau, nhưng về cơ bản, số ký tự trên mỗi dòng luôn được giới hạn là 80. Riêng PEP 8 của Python thì giới hạn số ký tự là 79. 79 hay 80 cũng không khác nhau nhiều lắm. Tuy nhiên, rất hiếm khi tôi thấy ai đó đưa ra quy tắc này mà giải thích về nó. Tại sao lại là 80, mà không phải 100, hay lớn hơn nữa.

Thỉnh thoảng cũng có người giải thích, đại ý rằng 80 ký tự sẽ giúp lập trình viên nhìn được toàn bộ code với các màn hình nhỏ. Với màn hình lớn, họ có thể nhìn 2 file song song với nhau mà không có phần code nào bị che khuất. Ngoài ra 80 ký tự cũng làm việc tốt với các công cụ review code.

Theo tôi, lý do này thật không thuyết phục một chút nào. Các công cụ để lưu trữ và review code như Github và Bitbucket cho phép hiển thị một dòng dài hơn nhiều. Bitbucket có thể hiển thị một dòng dài tới 130 ký tự. Github có thể hiện thị dòng dài 125 ký tự (ngày trước là 119). Hay việc view code trên máy cá nhân cũng vậy. Màn hình laptop của tôi thuộc lại nhỏ cũng hiển thị được hơn dòng hơn 100 ký tự. Màn hình lớn hơn, khi chia đôi vẫn có thể hiện thị dòng gần 100 ký tự. Vậy, lý do giới hạn số ký tự trên một dòng là 80 để có thể nhìn thấy toàn bộ code không hợp lý cho lắm.

Sau khi tìm hiểu, thì tôi biết rằng, giới hạn 80 ký tự này có nguồn gốc từ bìa đục lỗ của IBM.

IBM punched card

Ngược dòng lịch sử

Bìa đục lỗ của IBM được phát minh vào năm 1928 bởi Herman Hollerith và nó có 80 cột. Một câu hỏi đặt ra là: tại sao tấm bìa này lại có 80 cột. Tấm bìa này được thiết kế dựa trên kích thước của đồng tiền vào thời điểm năm 1880 (lúc đó nó to hơn bây giờ), và Hollerith muốn tái sử dụng máy thống kê được phát minh vào năm 1890.

Tầm bìa này được sử dụng vô cùng rộng rãi, tới mức nó có ảnh hưởng rất lớn đến các terminal, ví dụ VT52 (DEC). Phần lớn chúng đều được thiết kế để hiển thị với kích thước 80x24 (24 dòng, mỗi dòng 80 ký tự). Tới tận bây giờ, những ứng dụng mô phỏng terminal bật lên vẫn có kích thước mặc định là 80x24. Và tất nhiên, nó ảnh hưởng đến cả coding style của chúng ta.

Vấn đề là, tại sao công nghệ đang phát triển từng ngày, từng giờ mà một quy ước có lịch sử gần một thế kỷ vẫn được áp dụng. Thật khó có câu trả lời cho câu hỏi này. Tuy nhiên, theo tôi, lý do mà các lập trình viên vẫn tuân theo quy tắc này có liên quan đến tâm sinh lý của con người:

  • Nếu dòng quá ngắn, chúng ta sẽ phải xuống dòng rất nhiều. Điều đó khiến chúng ta khó theo dõi code hơn do nội dung bị ngắt quãng liên tục.
  • Nếu dòng quá dài, việc chúng ta phải nhìn chúng khá mỏi cổ. Hơn nữa, dòng dài có thể khiến chúng ta nhầm lẫn các dòng với nhau. Tất nhiên, việc này có thể phòng tránh bằng cách tăng khoảng cách giữa các dòng, nhưng không phải editor nào cũng làm được.

Và mỗi dòng được khuyến cáo là nên có từ 40-90 ký tự. (Tham khảo Markus Itkonen: Typography and readability.) Và trung bình là 65 ký tự có vẻ là giá trị tốt nhất. Ngoài ra code còn phải có indent, căn chỉnh, giá trị này cần tăng lên một chút. Có lẽ vì lý do này, mà độ dài 80 vẫn được duy trì từ thời các bậc tiền bối của chúng ta tới tận bây giờ.

Một vài quy ước khác về độ dài dòng

132 ký tự một dòng là một định dạng cũng có truyền thống khá lâu. Có lẽ nó bắt nguồn từ VT100 (DEC) có chế độ màn hình 132 ký tự, và có liên quan đến máy in kim với 132 cột. 132 có vẻ cũng là giới hạn trên của độ dài mỗi dòng trong ngôn ngữ Fortran. Trên thực tế, đây cũng là độ dài lớn nhất được chấp nhận sử dụng trong cộng đồng các lập trình viên. Mặc dù có một số chỗ vẫn có người viết những dòng dài hơn, ví dụ như mã nguồn của Scala.

120 ký tự một dòng nhiều khi cũng được chấp nhận, cũng như 100 ký tự một dòng tùy vào nhu cầu của team. Lý do chọn những giá trị này một phần cũng là do chúng chẵn chục.

72 ký tự một dòng là giá trị thường được đề nghị với các nội dung dài như sách, báo. Ngoài ra, PEP 8 cũng đề nghị 72 ký tự với các comment hoặc docstring. Hơn nữa, 72 cũng là đồ dài dòng mặc định của ngôn ngữ Fortran.

Indentation: tabs vs. spaces

Tôi đã làm việc với nhiều ngôn ngữ, và đều được hướng dẫn coding style là sử dụng toàn bộ dấu cách để indent code của mình. Tuy nhiên, thỉnh thoảng tôi lại gặp tình huống người ta khuyên nên dùng tab, ví dụ như Wordpress coding style hay Linux kernel coding style. Vậy tại sao có người lại bảo dùng tab, người khác lại bảo space. Dùng tab, hay space, cái nào tốt hơn cái nào?

Bây giờ tôi biết rằng, thực ra cả thế giới vẫn còn cãi nhau về việc này, dùng tab hay space. Cái nào tốt hơn cái nào. Ai cũng có những lý thuyết riêng của mình, cổ vũ việc dùng tab (hoặc space) và chê bai những người đối lập. Một số lý do chính về việc dùng tab hay space có thể nói ngắn gọn lại như sau:

Tại sao dùng tab:

  • Đây là ký tự đúng nghĩa dùng để indent. Một ký tự tab nghĩa là indent 1 mức, trong khi dấu cách có nhiều nghĩa khác nhau.
  • Nó cho phép các lập trình viên với những tùy chọn khác nhau có thể thay đổi để nhìn indent to nhỏ khác nhau.
  • Không thể có indent thiếu với ký tự tab, nên bạn không phải lo vấn đề indent khi copy, indent bị lệch do nguồn dùng 3 dấu cách, bạn dùng 4 dấu cách.
  • Indent bằng ký tự tab cho kích thước file nhỏ hơn.
  • Việc di chuyển con trỏ qua ký tự tab nhanh hơn.
  • Nó là cách duy nhất để căn thẳng các cột nếu dùng font proportional.

Tại sao dùng space:

  • Đảm bảo việc hiển thị thống nhất giữa tất cả các thiết bị, editor với các thiết lập khác nhau. Dấu cách luôn có độ rộng là 1 cột.
  • Tác giả viết code có thể hoàn toàn làm chủ việc hiển thị indent.
  • Tab là ký tự không hiển thị được, nó thường được nhìn thấy giống như nhiều dấu cách liên tục.

Gần đây mới có thêm một phương pháp indent và căn chỉnh mới, đó là Elastic tabstop. Tuy nhiên, có vẻ nó vẫn chưa được đón nhận bởi các lập trình viên, nên trong bài viết này, chúng ta tạm thời chưa đề cập đến nó.

Phần tiếp theo đây, tôi sẽ trình bày những tình huống cụ thể hơn, về việc sử dụng tab và space, và tất nhiên, cả những hạn chế của chúng.

Khi nào thì việc sử dụng tab trở thành vấn đề?

Việc sử dụng tab để indent, về cơ bản là không có vấn đề gì. Tuy nhiên, nó chỉ trở thành vấn đề nếu yêu cầu chúng ta phải biết trước độ rộng tab. Có nghĩa là, code có thể trông rất khác nếu độ rộng tab thay đổi, thì khi đó, việc sử dụng tab mới là vấn đề. Tất cả mọi trường hợp khác, sử dụng tab hay space đều không có vấn đề gì cả.

Sau đây, chúng ta sẽ tìm hiểu các trường hợp khác nhau về việc sử dụng tab và space trong việc định dạng code.

Editor xử lý indent như thế nào

Tab và căn chỉnh

Thực ra, độ rộng tab (khoảng cách giữa các tab stop) và độ rộng indent offset (số ký tự mà dòng đó thụt vào) có thể có những giá trị khác nhau. Tuy nhiên, ở đây chúng ta chưa nhắc đến trường hợp phức tạp như vậy. Trong ví dụ sau, chúng ta xét trường hợp cả hai giá trị trên đều bằng nhau và bằng 4. Và chúng ta ký hiệu ---> tương ứng với 1 tab cho 1 mức indent.

int foo(char bar[]) {
--->int i = 0;
--->while(bar[i] != '\0')
--->--->i++;
--->return i;
}

Việc indent như trên, bằng tab, hoạt động rất tốt với đoạn code C ở trên. Kể cả khi chúng ta thay đổi độ rộng tab tùy theo sở thích cá nhân, mọi thứ vẫn hoạt động bình thường, code vẫn được định dạng rất đẹp.

Vấn đề chỉ xảy ra với việc ngắt dòng. Khi một biểu thức bị ngắt ra thành nhiều dòng, chúng ta thường căn chỉnh chúng cho dễ nhìn hơn:

int f(int x,
      int y) {
    return g(x,
             y);
}

Chúng ta sẽ gọi những khoảng trắng của dòng thứ 2 và dòng thứ 4 là dùng để “căn chỉnh”. Thật không may, rất nhiều editor sử dụng tab cho việc căn chỉnh này, và chỉ thêm dấu cách (dưới đây sẽ ký hiệu là .) với phần trống còn lại không đủ độ rộng của 1 tab. Việc này sẽ khiến những dòng bị ngắt phụ thuộc hoàn toàn vào độ rộng tab. Như ví dụ dưới đây, nếu chúng thay đổi tab từ 4 thành 2, mọi thứ sẽ trở nên lộn xộn.

int f(int x,
--->..int y) {
--->return g(x,
--->--->--->.y);
}
int f(int x,
->..int y) {
->return g(x,
->->->.y);
}

Tuy nhiên, bạn có thể thấy rằng, dòng thứ 3 (return ...), đây là dòng mà chúng ta chỉ indent mà không căn chỉnh gì. Nó không hề bị lệch lạc gì. Đó chính là một trong số các ý tưởng để giải quyết vấn đề này: bỏ hoàn toàn việc căn chỉnh.

Tabs và không căn chỉnh

Đây là phương án mà Visual Studio lựa chọn. Khi một biểu thức bị ngắt ra dòng mới, dòng đó chỉ đơn giản là được indent thêm một hoặc hai mức nữa:

int f(int x,
--->int y) {
--->return g(x,
--->--->y);
}

Nói một cách khác, số lượng khoảng trắng đầu dòng luôn là bội số của indent offset. Do đó, miễn là độ rộng tab và indent offset có giá trị bằng nhau, code của chúng ta sẽ luôn được hiển thị một cách chính xác.

Dùng cách và không căn chỉnh

Một phương án khác, khi đã bỏ việc căn chỉnh đi, chúng ta có thể convert toàn bộ dấu tab thành dấu cách mà code không bị ảnh hưởng gì.

int f(int x,
....int y) {
....return g(x,
........y);
}

Tuy nhiên, code này chưa được đẹp lắm. Và những người cổ vũ việc sử dụng dấu cách thường sử dụng phương án sau đây.

Dùng cách và căn chỉnh

Việc không căn chỉnh khiến code trông không đẹp và được cho là khó đọc. Và lý do những người dùng cách cho rằng không nên dùng tab, đó là tab không thể đảm bảo việc căn chỉnh luôn luôn đúng, trong khi cách có thể làm được việc này.

Nếu chúng ta chỉ sử dụng dấu cách cho cả indent và căn chỉnh, như ví dụ dưới đây:

int f(int x,
......int y) {
....return g(x,
.............y);
}

Kết quả là, code của chúng ta sẽ luôn được hiển thị chính xác với mọi hệ thống và editor. Bởi vì dấu cách là như nhau ở mọi nơi.

Tuy nhiên, việc làm này cũng có những vấn đề của riêng nó. Việc dùng dấu cách sẽ cố định indent offset. Và khi làm việc với những file này, chúng ta sẽ phải thiết lập lại editor cho phù hợp. Đây sẽ là một trở ngại lớn nếu một người làm việc với nhiều project có kiểu indent khác nhau. Nếu bạn muốn indent offset tăng hay giảm, bạn sẽ phải sửa lại toàn bộ file. Dùng tab có thể giải quyết vấn đề này, tuy nhiên, lại gặp vấn đề căn chỉnh, và chúng ta có thể giải quyết với phương án sau đây.

Sử dụng smart tabs

Có một cách để chúng ta có thể sử dụng tab và vẫn đảm bảo việc căn chỉnh hoạt động đúng, đó là sử dụng tab để indent và cách để căn chỉnh (còn tên gọi khác là smart tabs – tab thông minh). Ví dụ, chúng ta có thể sử dụng smart tabs như ví dụ dưới đây:

int f(int x,
......int y) {
--->return g(x,
--->.........y);
}

Việc sử dụng tab chỉ thực sự có vấn đề nếu chúng ta dùng lẫn lộn cả tab và space. Cách giải quyết là, sử dụng space để căn chỉnh các ký tự và dùng tab để indent. Như ví dụ trên, khi chúng chỉnh độ rộng tab về 2, code sẽ trông như dưới đây:

int f(int x,
......int y) {
->return g(x,
->.........y);
}

Nó hoàn toàn bình thường, vẫn được indent và căn chỉnh tốt. Đó là bởi vì, các dòng bị ngắt được bắt đầu bằng indent ngang với dòng trước đó, sau đó sử dụng dấu cách để căn chỉnh. Nói một cách khác, việc dùng smart tabs khiến file có thể có indent offset là bất cứ giá trị nào.

Hạn chế của việc dùng tab

Đương nhiên, kể cả khi dùng smart tabs thì chúng ta vẫn có thể gặp phải những vấn đề nhất định. Đó là khi chúng ta muốn căn chỉnh comment cho các dòng với indent khác nhau.

int foo(char bar[]) {     // Indent level 0
--->int i = 0;            // Indent level 1
--->while(bar[i] != '\0') // Indent level 1
--->--->i++;              // Indent level 2
--->return i;             // Indent level 1
}                         // Indent level 0
int foo(char bar[]) {     // Indent level 0
------->int i = 0;            // Indent level 1
------->while(bar[i] != '\0') // Indent level 1
------->------->i++;              // Indent level 2
------->return i;             // Indent level 1
}                         // Indent level 0

Trong ví dụ trên, chỉ những comment cho chùng một mức indent mới được căn thẳng khi chúng ta thay đổi độ rộng tab từ 4 thành 8.

Với những editor mà hiển thị dấu tab là “một số lượng x cột” thì chúng ta có thể thêm dấu tab vào trước comment để căn chỉnh như ví dụ sau đây. Tuy nhiên, phần lớn các editor mà tôi sử dụng, cả Emacs hay Vim, để hiểu dấu tab là “chuyển đến cột tiếp theo là bội của x”, nên phương án này không khả thi.

int foo(char bar[]) {--->--->// Indent level 0 (difference: 2)
--->int i = 0;--->...........// Indent level 1 (difference: 1)
--->while(bar[i] != '\0')--->// Indent level 1 (difference: 1)
--->--->i++;.................// Indent level 2 (difference: 0)
--->return i;--->............// Indent level 1 (difference: 1)
}--->--->....................// Indent level 0 (difference: 2)
int foo(char bar[]) {------->------->// Indent level 0 (difference: 2)
------->int i = 0;------->...........// Indent level 1 (difference: 1)
------->while(bar[i] != '\0')------->// Indent level 1 (difference: 1)
------->------->i++;.................// Indent level 2 (difference: 0)
------->return i;------->............// Indent level 1 (difference: 1)
}------->------->....................// Indent level 0 (difference: 2)

Tuy nhiên, trong trường hợp này, việc comment inline có thể không phải là ý tưởng tốt. Việc comment như dưới đây dễ dàng hơn nhiều cho cả người viết và người đọc code.

// Indent level 0
int foo(char bar[]) {
    // Indent level 1
    int i = 0;
    while(bar[i] != '\0')
        // Indent level 2
        i++;
    return i;
}

Vậy cuối cùng: tab hay space?

Việc sử dụng tab hay space đều có những ưu và nhược điểm riêng. Việc sử dụng ký tự nào đều không phải là vấn đề. Điều quan trọng nhất là sự nhất quán. Chọn một kiểu indent và căn chỉnh cho cả project và tất cả mọi người đều tuân theo. Sự thống nhất đó sẽ đảm bảo được code của bạn sẽ dễ dàng maintain hơn sau này. Bất kể là nó dùng tab hay space.

Indentation: 2 vs. 4 vs. 8

Lại thêm một vấn đề nữa với indent. Thế mới biết, chỉ riêng việc indent thôi mà cũng lắm nhiêu khê. Và cũng vì thế mà chúng ta biết được, việc indent code quan trọng thế nào.

Tạm gác lại vấn đề tab hay space sang một bên. Cho dùng bạn dùng tab, hay space, bạn cũng sẽ gặp phải một vấn đề khác. Đó là indent offset bao nhiêu là vừa. Vấn đề này có thể ít gặp hơn với người sử dụng tab, bởi vì nếu thích họ có thể dễ dàng thay đổi giá trị của offset. Những người dùng space chắc khổ hơn. Do họ phải fix cứng giá trị này ngay từ đầu.

Tuy nhiên, ở phần này, chúng ta không nói đến những vấn đề đó, mà chỉ tập trung vào indent offset mà thôi. Indent offset cũng được đề nghị rất khác nhau giữa các ngôn ngữ, và các tổ chức.

Indent 2:

Indent 4:

Indent 8:

Theo một số nghiên cứu, mà tôi không nhớ đã đọc ở đâu, indent offset là 6 sẽ khiến code trông xấu và rất khó đọc. Các giá trị khác chắc cũng tương tự. Có lẽ vì thế mà tôi chưa thấy ai đặt offset bằng các giá trị như 3 hay 5. Ngoài ra, các lập trình viên thường bị ảnh hưởng bởi lũy thừa của hai, nên các giá trị trên thường được sử dụng

Có một lợi ích của việc indent offset lớn, đó là nó sẽ dễ dàng nhận ra các khối lệnh con của một lệnh điều khiển nào đó (như if chẳng hạn). Ví dụ, bạn có thể nhìn vào 3 ví dụ dưới đây với 3 indent offset khác nhau.

while (x == y) {
        something();
        somethingelse();

        if (some_error()) {
                do_correct();
        } else {
                continue_as_usual();
        }
}

final_thing();
while (x == y) {
    something();
    somethingelse();

    if (some_error()) {
        do_correct();
    } else {
        continue_as_usual();
    }
}

final_thing();
while (x == y) {
  something();
  somethingelse();

  if (some_error()) {
    do_correct();
  } else {
    continue_as_usual();
  }
}

final_thing();

Rõ ràng là việc indent lớn hơn, khiến chúng ta dễ dàng nhận ra các khối lệnh con bên trong ifwhile hơn là offset nhỏ. Tuy nhiên, một điều hạn chế của việc đặt offset lớn, đó là phần còn lại của dòng sẽ nhỏ đi, có nghĩa là số ký tự bạn có thể viết trên dòng đó sẽ ít đi.

Vậy phải đặt offset là bao nhiêu cho vừa, để cân bằng giữa việc dễ đọc và việc phải ngắt dòng. Có lẽ điều này phụ thuộc vào ngôn ngữ và từng project cụ thể. Ví dụ ngôn ngữ Ruby, hầu như tất cả mọi người đều sử dụng indent offset là 2. Có lẽ một phần lý do là code của ngôn ngữ này thường khá dài, nếu đặt offset lớn có thể khiến một dòng bị ngắt nhiều hơn.

Những ngôn ngữ ít bị ngắt dòng hơn nên họ sử dụng indent offset 4. Riêng offset 8 tôi mới chỉ thấy vài ví dụ trên về việc code ngôn ngữ C, mà chưa thấy nơi nào khác đề nghị việc này. Một số nơi đề nghị dùng tab để indent cũng không nói cần offset là bao nhiêu cho vừa. Họ để cho lập trình viên tự quyết định. Lý do, theo tôi, đó là các xử lý ở những phần mềm này rất phức tạp, và việc cần hiểu các khối lệnh là yêu cầu quan trọng nhất, nên việc để indent lớn cho dễ nhìn cũng là điều dễ hiểu.

Vậy, 2, 4 hay 8, indent offset bao nhiêu là vừa. Tất nhiên là phải tùy vào đặc trưng của ngôn ngữ và của project nữa. Nhưng nếu cần phải lựa chọn giữa các giá trị này, thì có vẻ 4 là một lựa chọn an toàn.

Sentence-ending period: 1 space vs. 2 spaces

Vào thời điểm này, giữa thập kỷ thứ hai của thế kỷ 21, điều này nghe có vẻ ngu ngốc. Có bao giờ bạn nghe ai đó bảo, nên thêm hai dấu cách vào sau dấu chấm câu chưa? Tôi thì đã nghe rồi. Lúc mới nghe, điều đó thật dị thường. Nhưng sau khi tìm hiểu, thì tôi đã biết rằng, điều đó được coi đã từng được coi là bình thường, rất lâu trước đây.

Máy đánh chữ xuất hiện cuối thế kỷ 19. Cơ chế hoạt động của nó dựa trên chuyển động của các bánh răng. Chính vì thế mà nó nảy sinh một vấn đề. Chữ cái i và chứ cái w luôn chiếm phần không gian trên trang giấy như nhau. Vì vậy, các font chữ monospaced (hay còn gọi là fixed width) được thiết kế để giảm bớt khoảng trống của các chữ cái hơn, so với việc sử dụng các font proportional truyền thống. Tuy nhiên, với font chữ này, các chữ cái không thể lồng vào nhau được.

typewriter text spacing

Nhiều người thường cho rằng, việc sử dụng hai dấu cách sau chấm sau bắt nguồn từ việc sử dụng font monospaced của máy đánh chữ. Bởi các ký tự monospaced đã có sẵn khoảng cách nên cần khoảng cách lớn hơn giữa các câu để phân biệt. Tuy nhiên, thực sự, việc sử dụng dấu cách dài đã có truyền thống trước đó hàng trăm năm. Và những người đánh máy sử dụng hai dấu cách để phù hợp với truyền thống mà thôi.

Một số lưu ý về thuật ngữ, “double space” (không có gạch nối) yêu cầu hai dấu cách liên tiếp được gõ, “double-space” (có gạch nối), hoặc “wide space” là một ký tự, chỉ một khoảng trắng rộng tương đương double space, và nó cũng có thể gọi là “emspace” giống như “emdash”.

Những ví dụ sau đây cho thấy việc in ấn truyền thống (thời chưa có máy đánh chữ) sử dụng double-space – cũng là emspace – sau dấu chấm như một quy ước. Rõ ràng, một số lượng lớn các quy ước cũ trong ngành chế bản đã thay đổi theo năm tháng.

Hình ảnh dưới đây là tài liệu in năm 1787, emspace được sử dụng thường xuyên (phần khoanh đỏ). Lưu ý thêm rằng, vào thời điểm này, người ta vẫn sử dụng dấu cách trước dấu hai chấm (phần màu xanh), và đặc biệt là dấu rất lạ: dấu cách – hai chấm – emdash (phần màu xanh góc trên bên phải). Ngày nay, những kiểu trình bày văn bản như vậy đã không còn tồn tại nữa.

1787

Hình ảnh dưới đây là tài liệu được in vào những năm 1840. Dấu emspace (khoanh đỏ) và dấu cách trước chấm phẩy (khoanh xanh) vẫn tiếp tục được sử dụng.

1840s

Năm 1855, mọi thứ vẫn chưa thay đổi gì.

1855

Năm 1876, mọi thứ vẫn tiếp tục. Dấu emspace sau dấu chấm vẫn còn nguyên.

1876

Năm 1892, dấu emspace vẫn rất được tin dùng. Chú ý dấu gạch nối (khoanh xanh) giữa các từ mà ngày nay chúng được viết liền. Đến tận năm 1909, từ “today” mà chúng ta dùng ngày nay vẫn được viết là “to-day”.

1892

Năm 1928, một thời gian rất dài, mọi chuyện chưa có gì mới.

1928

Một cuối sách tiếng Tây Ban Nha năm 1959 vẫn sử dụng emspace, và cả dấu phẩy sau emdash. Không biết đây là lỗi đánh máy hay thời đó, người ta viết như vậy.

1959

Một cuốn sách năm 1960 của nhà thơ E.E. Cummings. Có vẻ như emspace vẫn được sử dụng.

1960

Vào năm 1961, mọi chuyện bắt đầu thay đổi.

1961

Vào những năm sau đó, 1963 và 1964, việc sử dụng một dấu cách sau chấm câu đã trở thành tiêu chuẩn.

1963
1964

Có thể việc chấp nhận dấu cách đơn trong ngành in ấn là một biện pháp tiết kiệm giấy?!?! Mặc dù xuất hiện từ thế kỷ 19, những cuốn sách bìa mềm mới phổ biến trên thị trường từ những năm 1930. Các nhà xuất bản phân phối các cuốn sách nhiều tới mức, họ phải in hàng chục nghìn cuốn.

Và để tiết kiệm, các biện pháp thu nhỏ lề, giảm khoảng cách các dòng, v.v đã được áp dụng. Có lẽ việc sử dụng một dấu cách cũng bắt đầu từ đây. Sự phát triển nhanh chóng của ngành công nghiệp xuất bản có thể là lý do để dấu cách đơn được chấp nhận nhanh chóng như vậy, mặc dù nó ngược lại với hàng thế kỷ truyền thống.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.