1 điểm bởi GN⁺ 2023-10-05 | 1 bình luận | Chia sẻ qua WhatsApp
  • Từ kinh nghiệm viết phần mềm hơn 20 năm, kiểu tĩnh mạnh gần như luôn đáng để lựa chọn, ngoại trừ các trường hợp như REPL hoặc script dùng một lần
  • Kiểu dữ liệu lưu lại hợp đồng giữa bên gọi và bên được gọi trong mã, giúp phát hiện tham số hoặc giá trị trả về sai ngay ở thời điểm biên dịch/kiểm tra kiểu
  • Ví dụ chuỗi "20" từ input HTML được dùng như số và trở thành "201" cho thấy sự khác biệt giữa lỗi bị bắt trước runtime và lỗi bị lộ ra cho khách hàng
  • Svix đưa khóa Redis, giá trị cache, các định danh như PersonId·PetId, và xác thực input API vào hệ thống kiểu để giảm lỗi gõ nhầm và truyền nhầm ID
  • Bỏ qua kiểu có thể giúp triển khai ban đầu nhanh hơn, nhưng chi phí tài liệu hóa, kiểm thử và debug sẽ tăng; với suy luận kiểu và hỗ trợ IDE, việc refactor và onboarding trở nên dễ hơn

Vì sao kiên quyết dùng kiểu tĩnh

  • Kiểu tĩnh mạnh không chỉ là một ý tưởng hay, mà trong hầu hết phần mềm gần như là mặc định đúng đắn
  • Ngôn ngữ không có kiểu hoặc các biến thể của chúng vẫn hữu dụng
    • Dùng REPL
    • Script dùng một lần trong môi trường vốn gần như không có kiểu, chẳng hạn shell
  • Trong hầu hết các trường hợp còn lại, nên ưu tiên kiểu mạnh
  • Nếu không dùng kiểu, tốc độ phát triển trước mắt có thể nhanh hơn, nhưng điều đó giống như “lao hết tốc lực về phía vách đá”
  • Cuối cùng chỉ có hai lựa chọn
    • Làm nhiều hơn để kiểm tra các bất biến ở thời điểm biên dịch hoặc kiểm tra kiểu
    • Làm ít hơn và kiểm tra ở runtime, hoặc thậm chí không kiểm tra cả ở runtime
  • Lỗi runtime không phải lúc nào cũng được phát hiện trong quá trình phát triển, và ngay cả khi bị phát hiện, chúng có thể xảy ra theo cách lộ ra cho khách hàng
  • Kiểm thử có ích, nhưng khó kiểm thử mọi kiểu tham số hàm sai có thể xảy ra; ngăn kiểu sai bằng hệ thống kiểu là cách dễ hơn

Kiểu dữ liệu gắn trực tiếp với hợp đồng mã và giảm lỗi

  • Kiểu dữ liệu vừa là chú thích mã hữu ích cho cả con người lẫn công cụ, vừa là cơ chế làm cho hợp đồng giữa các đoạn mã chặt chẽ hơn
  • Ngay cả với cùng một hàm chúc mừng sinh nhật, độ rõ ràng của hợp đồng cũng khác nhau rất lớn
    • birthdayGreeting1(...params) thậm chí không cho biết số lượng tham số, nên khó biết cách hoạt động nếu không đọc tài liệu
    • birthdayGreeting2(name, age) gợi ý rằng có tên và tuổi, nhưng không có kiểu
    • birthdayGreeting3(name: string, age: number): string đưa cả kiểu input và kiểu trả về vào hợp đồng
  • Nếu hàm được đổi sang dùng age + 1, phiên bản không có kiểu sẽ gặp vấn đề với input dạng chuỗi
    • Giá trị đến từ input HTML luôn có thể là chuỗi
    • birthdayGreeting2("John", "20") trả về "John will turn 201 next year!"
    • Phiên bản có kiểu yêu cầu age phải là số, nên lời gọi sai sẽ không biên dịch được
  • Hợp đồng giữa bên gọi và bên được gọi càng quan trọng khi codebase lớn dần
    • Có thể biết bên gọi sẽ bị ảnh hưởng thế nào khi bên được gọi thay đổi
    • Điều này đặc biệt quan trọng trong các trường hợp như thư viện mã nguồn mở, nơi những người khác nhau viết bên gọi và bên được gọi
  • Nếu không có những hợp đồng như vậy, sẽ khó xác định một thay đổi ảnh hưởng đến đâu

Lợi ích trong trải nghiệm phát triển, refactor và onboarding

  • Thông tin kiểu được IDE và công cụ phát triển tận dụng để cải thiện mạnh trải nghiệm phát triển
  • Trong lúc viết mã, bạn biết ngay khi giả định của mình sai, nhờ đó giảm tải nhận thức
  • Lập trình viên không cần nhớ kiểu của mọi biến và hàm trong ngữ cảnh hiện tại; compiler sẽ chỉ ra phần nào không khớp
  • Refactor cũng dễ hơn
    • Khi thay đổi phần triển khai của hàm, compiler có thể cho biết liệu bạn có phá vỡ giả định ở nơi khác hay không
  • Kỹ sư mới cũng dễ làm quen với codebase hoặc thư viện hơn
    • Có thể lần theo định nghĩa kiểu để hiểu chúng được dùng ở đâu
    • Khi thay đổi, lỗi biên dịch sẽ xuất hiện, nên dễ thử nghiệm
  • Ví dụ về hàm nhận kiểu Person cho thấy sự khác biệt
    • birthdayGreeting3(person: Person) giúp dễ tìm các điểm dùng Person bằng IDE
    • Với birthdayGreeting2(person) không có kiểu, việc nó thực sự kỳ vọng Person chỉ có thể biết sau khi đọc toàn bộ codebase
  • Tài liệu có thể bù đắp một phần, nhưng tài liệu dễ lỗi thời, còn kiểu là tài liệu nằm ngay trong mã
  • Có thể xem kiểu như một dạng mạnh hơn của tên biến hữu ích

Cách Svix đưa thông tin vào hệ thống kiểu

  • Svix cố đưa càng nhiều thông tin càng tốt vào hệ thống kiểu để giảm lỗi có thể bắt ở thời điểm biên dịch, đồng thời cải thiện trải nghiệm phát triển
  • Redis về bản chất là giao thức dựa trên chuỗi và không có kiểu tích hợp, nên các lợi ích của kiểu có thể biến mất ở tầng Redis
  • Ví dụ cache đơn giản có hai lỗi
    • Có lỗi gõ sai tên khóa như person-{id}preson-{id}
    • Cố tải dữ liệu người dưới kiểu Pet
  • Để tránh các vấn đề này, Svix áp dụng hai điều
    • Yêu cầu khóa là một kiểu cụ thể, không phải chuỗi thông thường
    • Ép khóa và giá trị phải đi thành cặp
  • Ví dụ, nếu dùng khóa được tạo bằng PersonCacheKey::new(id), mã cố nhận kết quả của cache.get(PersonCacheKey::new(id)) dưới dạng Pet sẽ không biên dịch được
  • ID kiểu String đơn giản cũng dễ gây nhầm lẫn
    • do_something(id: String) không nói rõ cần nhận ID nào
    • Có thể mắc lỗi truyền pet.owner trong khi nơi đó thực ra cần truyền pet.id
  • Svix dùng kiểu riêng cho từng ID
    • PersonId(String)
    • PetId(String)
    • owner của PetPersonId
  • Tính hợp lệ của ID nhận từ API cũng được gắn với việc tạo kiểu
    • Ví dụ, pet ID có dạng gồm tiền tố pet_ theo sau là Ksuid
    • Không cho phép tạo PetId nếu chưa xác thực
    • Với cách này, khi không tìm thấy pet trong cơ sở dữ liệu và trả về 404 Not Found, có thể chắc chắn rằng bản thân định dạng ID là hợp lệ
    • ID không hợp lệ đã được xử lý ở API handler bằng 422 hoặc 400

Các luận điểm phản đối và vai trò của công cụ

  • Các luận điểm phản đối chính với kiểu dữ liệu là tốc độ phát triển, đường cong học tập và độ phức tạp của kiểu, công sức và boilerplate
  • Prototype không dùng kiểu chắc chắn có thể nhanh hơn
    • Có thể comment mã mà không bị compiler phàn nàn
    • Có thể đưa giá trị sai vào trường trước khi xác định giá trị đúng
  • Nhưng đây được xem là nợ kỹ thuật mang tính tấn công và không cần thiết, rồi sẽ phải trả giá nhiều lần khi debug ở local, test suite và production
  • Đường cong học tập có tồn tại, nhưng hầu hết mọi người không cần trở thành chuyên gia về kiểu
    • Chỉ cần các biểu diễn kiểu đơn giản là đã đủ để làm việc
    • Khi bị kẹt thì có thể nhờ trợ giúp
  • Lập trình viên vốn đã phải học rất nhiều thứ như coding, React, các framework như Axum, nên gánh nặng học kiểu bị cho là phóng đại
  • Học kiểu là chi phí một lần, còn lợi ích kiểu mang lại khi onboarding vào một codebase cụ thể lớn hơn nhiều
  • Nếu không dùng kiểu, cần tài liệu hóa và kiểm thử đáng kể để có được mức ổn định cơ bản
    • Tài liệu và test có thể lỗi thời
    • Thêm kiểu đúng được xem là tốn ít công sức hơn
  • Trong các ngôn ngữ không có suy luận kiểu, việc gõ kiểu có thể phiền phức
    • Ví dụ Java có sự lặp lại như Person person1 = newPerson();
    • Bài viết sau đó có bổ sung đính chính rằng Java có suy luận kiểu
  • Các ngôn ngữ có suy luận kiểu như Rust ngắn gọn hơn, chẳng hạn let person1 = new_person();
  • Để hưởng lợi từ kiểu, cần trình soạn thảo mã hoặc IDE hỗ trợ tính năng hoàn thành mã hiện đại hiểu được ngôn ngữ
  • Khác với các tranh luận theo sở thích như vim so với emacs, tab so với space, lập trường ở đây là kiểu có lợi ích lớn so với chi phí, nên khó hiểu vì sao lại không dùng
  • Có bài viết tiếp theo là using the type system effectively

1 bình luận

 
GN⁺ 2023-10-05
Ý kiến trên Hacker News
  • Điều bực bội nhất trong cuộc thảo luận này là tất cả đều là chuyện mọi người cảm thấy thế nào, còn bằng chứng thực nghiệm thì thiếu
    Các nghiên cứu hiện có cho rằng không có khác biệt có ý nghĩa giữa hai cách, và nếu không có nghiên cứu mới thì khó có thể khẳng định chắc chắn rằng phe mình thích là đúng
    Cá nhân tôi thích ngôn ngữ có kiểu, nhưng các hệ thống kiểu như TypeScript thì còn thiếu sót. Vì không thể thực sự dùng kiểu ở runtime nên bug runtime vẫn còn, và không thể mã hóa nhiều logic runtime vào hệ thống kiểu, nên vẫn phải tự kiểm tra những trường hợp bất khả thi
    Nếu hệ thống kiểu gần như loại bỏ nhu cầu phải nghĩ về bug runtime thì đó sẽ là lợi thế áp đảo, nhưng hầu hết ngôn ngữ chưa đạt mức đó mà chỉ nằm ở khoảng giữa lửng lơ giữa overhead và một số lợi ích
    Lý do số lượng bug hay tốc độ không khác biệt lớn có lẽ cuối cùng là vì chúng triệt tiêu lẫn nhau. Nếu không có lưới an toàn kiểu thì sẽ viết nhiều test hơn, còn ngược lại nếu quá tin vào hệ thống kiểu thì rốt cuộc vẫn còn một lượng bug runtime tương tự. Tôi ước có nghiên cứu vững chắc về chủ đề này, nhưng đây là vấn đề khó

    • Có lẽ đa số sẽ đồng ý rằng kiểu ngăn được nhiều bug, và trong thread này cũng đã có nghiên cứu như vậy được đăng lên
      Điểm cốt lõi gần với câu hỏi lý do chủ quan để cho rằng không đáng đầu tư vào kiểu là gì hơn
    • Rốt cuộc có lẽ phải chấp nhận rằng đây là vấn đề phán đoán
      Vài năm trước tôi đã xem qua các nghiên cứu về năng suất lập trình viên, nhưng hầu hết đều tệ hoặc chỉ áp dụng đúng cho junior. Ví dụ, người mới bắt đầu hưởng lợi lớn từ phản hồi nhanh về lỗi tĩnh
      Gần như không thể áp dụng một thiết kế thí nghiệm tốt cho chuyên gia thay vì sinh viên đại học, và vì phải tách vô số biến như khác biệt cá nhân, loại hình phát triển, cách quản lý, nên rất khó rút ra tín hiệu. Đáng buồn là nhiều thứ trong cuộc sống rất khó đo lường hiệu quả
    • Tôi cũng nghĩ gần như vậy
      Bài viết và nhiều bình luận nói về sự tiện lợi, năng suất, “tính đúng đắn” của lập trình viên, nhưng nghiên cứu hiện tại không có kết quả có ý nghĩa nào cho thấy kiểu tĩnh cải thiện hay làm xấu đi những điều này. Về cơ bản là chủ quan
      Tuy nhiên có một hiệu ứng mà kiểu tĩnh thực sự mang lại và có thể chứng minh một cách đơn giản: nó cho phép viết code hiệu quả hơn. Đây nên là trọng tâm trong thảo luận về kỷ luật kiểu, còn phần còn lại ở giai đoạn này gần như là chuyện mơ hồ
      TypeScript được nhắc trong bài thực ra không phải là kiểu mạnh; nó là kiểu tĩnh nhưng kiểu yếu. Kiểu gần giống chú thích, nên cũng không có bảo đảm về hiệu năng hay bố trí bộ nhớ. Vì vậy ngoài tài liệu hóa, nó phải trả chi phí của kiểu tĩnh nhưng gần như không nhận được lợi ích thực chất
      Thật đáng ngạc nhiên khi cộng đồng công nghệ phớt lờ bằng chứng thực tế và chấp nhận sở thích văn hóa/cá nhân như thể đó là sự thật
    • Kiểu tĩnh chỉ là một lát phô mai Thụy Sĩ trong việc tạo ra phần mềm đáng tin cậy hơn
      Giống như các kỹ thuật khác, nó có lỗ hổng, nên để đạt độ tin cậy tối đa cần kết hợp nhiều kỹ thuật. Vứt bỏ kiểu tĩnh vì nó không bắt được mọi thứ cũng giống như nói rằng vì trộm có thể đập cửa sổ nên không khóa cửa. Nếu bảo mật thực sự quan trọng thì phải vừa khóa cửa vừa lắp song sắt cho cửa sổ, chứ không phải chọn một trong hai
    • Có vẻ như họ nghĩ rằng nếu viết chương trình bằng một ngôn ngữ biểu diễn kiểu thì “bug runtime” sẽ biến mất một cách thần kỳ
      Nếu một ngôn ngữ đủ mạnh để viết chương trình thông thường, thì nó cũng đủ mạnh để tạo ra bug
      Kiểu tĩnh có thể hiệu quả trong việc bắt một số loại bug nhất định, nhưng không phải tất cả. Đôi khi nó tăng khả năng đọc giống như unit test tĩnh hoặc một DSL dành cho tài liệu thực thi được
      Nói chung, ngôn ngữ động linh hoạt hơn và cho phép viết nhiều test dễ dàng hơn. Có những test sẽ không cần viết nếu dùng ngôn ngữ kiểu tĩnh, nên kiểu vẫn hữu ích, nhưng không mạnh một cách phổ quát như người ta thường tin
  • Tách khỏi áp lực xã hội rằng ta phải thích kiểu tĩnh, rốt cuộc lý do tôi luôn rời xa kiểu tĩnh là vì quanh nó lúc nào cũng mọc lên một tháp ngà
    Tôi đã làm phần mềm 10 năm trong mỗi hệ hình, và giờ tôi thích không dùng hệ thống kiểu hơn
    Kiểu động với tôi giống như một lực ép khiến ta viết code đơn giản, giống như unit test buộc code phải có thể tổ hợp được. Ý tôi là code dễ đọc, dễ hiểu
    Tôi cũng không mấy đồng tình với lập luận rằng nó giúp lập trình viên mới dễ tiếp cận codebase hơn. Vì rất dễ khuyến khích vòng lặp chỉ xóa các dấu đỏ mà không hiểu gì. Hệ thống kiểu bắt người ta học thêm một ngôn ngữ rất đặc thù theo domain đặt lên trên ngôn ngữ của từng dự án, và nhiều khi cản trở việc hiểu hành vi thực tế
    Các vấn đề nêu trong bài có thể được giải quyết theo cách vững chắc như kiểu mà vẫn dễ hiểu. Có thể dùng kiểu một cách đơn giản, nhưng theo kinh nghiệm của tôi thì thực tế hầu như không như vậy. Tôi cũng không thích tự động hoàn thành, nên cứ hiểu như thế
    Có thể tôi chỉ là một lập trình viên già hay hô “code chính là tài liệu”, nhưng cũng có thể đó là suy nghĩ xuất phát từ sự bất mãn sâu sắc với lớp lập trình viên “ChatGPT bảo là đúng và vẫn nhận lương cao” đang tràn ngập ngành hiện nay

    • Nếu kiểu động thực sự khiến phần lớn lập trình viên viết code đơn giản hơn thì đó sẽ là một lập luận rất mạnh
      Nhưng nhìn chung bằng chứng ngược lại có vẻ mạnh hơn. Các kiểu được tạo sau này để tài liệu hóa code động thực tế thường phức tạp hơn rất nhiều so với cùng chức năng được triển khai bằng kiểu tĩnh ngay từ đầu. DefinitelyTyped trong hệ sinh thái TypeScript cung cấp vô số ví dụ
      Khó có thể nói rằng các kiểu đó được “dùng đơn giản”, nhưng độ phức tạp ấy không đến từ bản thân hệ thống kiểu hay cách cung cấp định nghĩa kiểu, mà đến từ độ phức tạp của code động mà nó đang mô tả
      Một package tương đương được làm bằng kiểu tĩnh ngay từ đầu thường có interface đơn giản hơn. Vì kiểu được định nghĩa từ trước, thay vì bị nhét vào API sẵn có về sau
      Tôi thậm chí cho rằng nếu không nêu rõ interface thì ta không thể biết interface đó đơn giản hay phức tạp. Tôi đồng ý với lý tưởng “code là tài liệu”, nhưng nếu không có code nêu rõ interface thì theo định nghĩa, interface đó thiếu tài liệu
    • Lần đầu nghe câu “hệ thống kiểu ngăn lập trình viên mới hiểu và chỉ khiến họ xóa dấu đỏ” thì tôi lại cảm thấy hoàn toàn ngược lại
      Tôi còn muốn được đứng cạnh xem cảnh đó diễn ra. Trong lĩnh vực của tôi, logic đặc thù theo domain gần như không thể hiểu nổi trong codebase kiểu động, trong khi code kiểu tĩnh dạy cho lập trình viên business logic
      Câu “code là tài liệu” cũng khiến tôi thấy rối hơn. Theo kinh nghiệm của tôi, phải có kiểu tĩnh thì code mới trở thành tài liệu. Nếu không, không có cách nào biết một object có những thuộc tính nào, hay tại sao lại kiểm tra một thuộc tính mà ta tưởng là không tồn tại. Comment thì có, nhưng tôi hầu như chưa thấy ai để lại comment có ý nghĩa
    • Tôi thấy gần như toàn bộ lập luận này bị đảo ngược nên rất khó hiểu
      Kinh nghiệm của tôi thì ngược lại. Các pattern rất động rất khó gắn kiểu cho đúng, còn một hệ thống kiểu tốt khuyến khích các pattern đơn giản hơn, nên kiểu cũng đơn giản hơn
      Xóa dấu đỏ là quan trọng. Dấu đỏ có nghĩa là có vấn đề, và nó dễ hơn rất nhiều so với việc phát hiện lỗi bằng cách khác. Tôi không hiểu vì sao lại muốn phát hiện lỗi đó muộn hơn
      Việc nói không thích cả tự động hoàn thành khiến tôi đứng về phía không tin những người phản đối kiểu tĩnh. Một lập trình viên không muốn máy tính hỗ trợ mình lập trình thì rất đáng nghi
    • Việc tháp ngà và áp lực xã hội khiến người ta ngại áp dụng một công nghệ nào đó là một nhận xét hay
      Nhưng điều đó không làm giảm giá trị kỹ thuật của nó. Một công nghệ có thể rất tốt trong khi những người xung quanh nó đầy vẻ làm màu
      Lập luận rằng kiểu động khiến ta viết code đơn giản nghe giống như “đeo bịt mắt lái xe thì sẽ lái chậm hơn nên tốt”. Nếu đó là mục tiêu thì chỉ cần dùng linter giới hạn độ dài dòng hoặc số lượng tham số, không cần tạo ra ràng buộc một cách gián tiếp
      Kiểu không phải lời giải duy nhất, nhưng hiệu quả trên chi phí đầu tư rất lớn, nên tôi xem đó là công cụ nên rút ra trước tiên. Đầu tư gần như không có, lợi ích thì lớn
      Tôi đồng ý với câu “code là tài liệu”, nhưng kiểu cũng là một phần của code. Vì vậy tôi muốn diễn đạt là “code là tài liệu, và kiểu là một phần của code”
    • Tôi cũng đang định viết gần như cùng một câu trả lời
      Lĩnh vực của chúng ta là kỹ thuật, không có một đáp án đúng duy nhất, mọi thứ đều là đánh đổi. Chính vì vậy mà công việc của chúng ta không bị tự động hóa rồi biến mất ngay lập tức
      Không khí “coi thường” người khác vì ý kiến hay kinh nghiệm của một kỹ sư khác trong thread này thật sự rất khó chịu
  • Trong bối cảnh phần lớn dữ liệu đi qua mạng dưới dạng JSON, cuộc chiến áp dụng kiểu tĩnh mạnh thường diễn ra thiếu nhất quán
    Nên dùng mọi công cụ có thể, nhưng phần lớn “dữ liệu” mềm nhũn hơn ta tưởng rất nhiều. Người ta để số điện thoại là chuỗi không phải vì lười, mà vì từng tưởng có thể biến nó thành kiểu mạnh hơn rồi bị vấp quá nhiều. Tên, địa chỉ, mã bưu chính cũng vậy
    Những giá trị này phải nhận từ người dùng, và thực chất gần như chỉ có cách phân tích cú pháp văn bản. Nếu thiết kế hệ thống không lưu văn bản gốc trước khi phân tích, một ngày nào đó gần như chắc chắn bạn sẽ hối hận
    Tôi cho rằng tốt nhất là có một lớp vừa giữ văn bản nhập ban đầu, vừa cung cấp cho người dùng backend một tập dữ liệu có kiểu, nhưng phải cân nhắc liệu trong phạm vi nhỏ của mỗi người, khoản đầu tư đó có đáng hay không
    Nếu cần đánh giá nặng, rất có thể sẽ cần một lớp chuyển đổi sang SAT hoặc mô hình số khác. Trong thế giới đó, con số là abstraction. Cố làm theo cách khác gần như chắc chắn sẽ đau đớn. Nên có một lớp dịch bài toán thành công thức hóa và dịch không gian nghiệm thành domain; kiểu có thể giúp ở đây, nhưng “kiểu” thực sự được chú ý lại quá thường không phải loại kiểu này

    • Tôi là tác giả bài viết. Có một điều trong cách chúng tôi làm ở Svix mà tôi chỉ nói lướt qua trong một đoạn và đáng lẽ nên giải thích thêm
      Nhờ các thư viện như Serde và Pydantic, chúng tôi theo cách tiếp cận giải tuần tự hóa chính là kiểm chứng. Trước khi biến dữ liệu JSON thành struct trong code, chúng tôi kiểm chứng toàn bộ
      Tương tự ví dụ Redis, dù nhận JSON qua mạng, sau khi đã kiểm chứng hoàn toàn và dữ liệu đến được code, ta có thể yên tâm rằng đó là kiểu được định dạng tốt. Vì vậy trong code có thể giả định kiểu email là email hợp lệ, kiểu ID là ID hợp lệ
    • Tên và địa chỉ, dù nhìn sâu vào thì cả hai đều là chuỗi, vẫn nên được dùng như các kiểu riêng biệt chứ không chỉ là chuỗi
      Trộn lẫn trường tên và trường địa chỉ hầu như luôn là lỗi, và hệ thống kiểu có thể cưỡng chế điều đó
    • Việc phần lớn dữ liệu đi qua dưới dạng JSON có thật sự khiến mọi thứ hỗn loạn đến vậy không? Trong một số trường hợp nhất định, Map cũng là một kiểu hoàn toàn hợp lý
  • Nếu lỗi chính tả trở thành lỗi runtime thì đó không phải là “di chuyển nhanh hơn”; và nếu mỗi lần đổi chữ ký hàm mà phải grep cả codebase để tìm mọi chỗ gọi rồi cầu mong mình đã sửa hết, thì đó cũng không phải là “năng suất hơn”
    Kiểu là tốt, nhưng cái gì quá đà cũng thành vấn đề. Nếu bạn biến việc mã hóa toàn bộ logic nghiệp vụ vào hệ thống kiểu thành mục tiêu cuộc đời, bạn sẽ tạo ra một mớ hỗn độn còn khó hiểu hơn cả việc không có kiểu nào. Nếu tên kiểu trong thông báo lỗi không vừa trên một dòng thì bạn đã đi quá xa rồi

    • Tôi từng thấy các định nghĩa kiểu TypeScript điên rồ
      Công bằng mà nói, đó là để khớp với mã JS thuần cũ, và cái biến tội nghiệp đó có thể chứa đủ thứ giá trị
      Tôi mãi biết ơn TypeScript, nhưng cũng sẽ không ngạc nhiên nếu trong tương lai có bài báo về “thời kỳ kiểu đã đi quá xa” đưa đoạn mã kiểu đó vào
    • Trước đây tôi từng nghĩ rằng vì có thể chỉnh sửa tương tác bằng from pdb import set_trace: set_trace(), nên tương tác với chương trình đang chạy tốt hơn là với compiler
      Nhưng chỉ cần chương trình phức tạp hơn một chút là tình hình khác ngay. Khi bạn truyền dữ liệu giữa các hệ thống bằng queue, dùng bất đồng bộ, thread, multiprocessing, và dùng thư viện binary đã biên dịch cho phần lõi hiệu năng, cuối cùng bạn sẽ thấy giá mà mọi thứ được viết bằng Erlang
    • Tôi thật sự tò mò những người thực sự làm việc theo kiểu đó khi thay đổi chữ ký hàm thì làm việc ra sao
      Có một phương pháp tổng thể nào đó như test cực kỳ nghiêm ngặt với độ bao phủ mã 100% không?
    • Mỗi khi thấy kiểu phàn nàn như thế này về kiểu tĩnh, tôi lại tự hỏi họ đã thiết kế định nghĩa kiểu và kiến trúc như thế nào mà chuyện này lại thành vấn đề
      Nếu ở thời điểm build hoặc compile mà không bắt được các chỗ gọi, thì bạn không thực sự đang dùng kiểu tĩnh
    • Tôi không rõ hai điều đó liên quan thế nào đến cuộc thảo luận về kiểu
      Lỗi chính tả vẫn có thể tạo ra mã sai ngay cả trong ngôn ngữ có kiểu tĩnh mạnh nhất. Nếu không thì “viết code” rốt cuộc còn nghĩa là gì? Giữa lỗi runtime và việc không có lỗi nhưng cho ra kết quả sai, cái nào tệ hơn?
      Chạy một dự án Python có thể nhanh hơn compile C++, và các ngôn ngữ kiểu động cũng có thể cung cấp cách tìm lời gọi hàm tốt hơn grep
  • Nói rằng không dùng kiểu có lợi thế là tốc độ phát triển nhanh hơn cũng không đúng với trải nghiệm của tôi. Kiểu tĩnh giúp lập trình hằng ngày nhanh hơn
    Việc IDE trở nên tốt hơn nhờ kiểu tĩnh đã được bàn ở phần sau, nhưng tôi cũng cảm nhận được trong REPL. Lỗi kiểu được bắt tĩnh đưa ra thông báo lỗi có ý nghĩa hơn nhiều, gần với nguyên nhân gốc thực sự hơn, và giúp sửa nhanh hơn lỗi runtime
    Nó cũng giảm gánh nặng phải suy nghĩ quá thận trọng về kiểu. Compiler giữ kỷ luật rồi, nên tôi có thể bớt bận tâm hơn. Tôi có thể di chuyển nhanh hơn vì tin rằng một lớp lớn lỗi sẽ được bắt ngay lập tức
    Theo trải nghiệm của tôi, hệ thống kiểu tĩnh dễ dùng, giúp phát triển nhanh hơn và tăng độ tin cậy. Cho đến nay chi phí chỉ có hai thứ: có thể khó học hơn, và khó triển khai hơn

    • Hoàn toàn đồng ý. Tranh luận về khả năng bảo trì cũng gần như được giải quyết bởi điều này
      Lập trình viên junior được tuyển sau 6 tháng sẽ chậm hơn rất nhiều khi làm quen với mã không có kiểu
      Tôi thừa nhận với một số người, lần viết đầu tiên có thể “nhanh hơn”, nhưng sau đó mọi lập trình viên đọc đoạn mã đó đều sẽ chậm lại
    • Cũng có thể lập luận rằng chính lối suy nghĩ thận trọng đó làm phần mềm sạch hơn và tốt hơn
      Thay vì một mớ hỗn độn chỉ vừa đủ qua compiler, việc suy nghĩ chính xác mình đưa gì vào, lấy gì ra, và vì sao như vậy, có thể là điều tốt
  • Tôi cho rằng tác giả sai ở gần như mọi điểm. Tôi cũng đã nghĩ như vậy trong nhiều thập kỷ, nhưng vài năm gần đây đã hoàn toàn đổi ý
    Kiểu có làm giảm bug không? Không. Có thể chỉ rất ít, nhưng không đáng kể. Cứ xem các nghiên cứu liên quan
    Kiểu có mang lại trải nghiệm phát triển tốt hơn không? Không. REPL và IDE của tôi có mọi định nghĩa và biến. Tôi có tự động hoàn thành mọi symbol, cây lời gọi, tìm nơi sử dụng, refactor tự tin, chạy riêng từng hàm, thay thế, bọc hàm ngay trong REPL và ứng dụng
    Mã hóa mọi thứ vào hệ thống kiểu? Không thể. Cần kiểm chứng runtime
    Chúc may mắn khi phải gỡ các định nghĩa kiểu lúc yêu cầu thay đổi. Đây mới là đòn quyết định. Kiểu tĩnh đóng băng quá sớm mô hình dữ liệu miền theo hiểu biết hiện tại. Mô hình đó sẽ thay đổi, và nếu không may, bạn phải hỗ trợ nhiều biến thể mô hình miền trong cùng một runtime. Đặc biệt nếu đã dùng kế thừa thì càng khổ
    Đúng là kiểu tĩnh cho compiler đòn bẩy lớn để tối ưu hóa, nhưng trong số các ngôn ngữ kiểu động cũng có ngôn ngữ cung cấp kiểu tĩnh như một tính năng bổ sung tùy chọn
    Trong nhiều use case, đặc biệt là phát triển enterprise, ngôn ngữ hàm ưu tiên bất biến với kiểu động mang lại lợi ích lớn về dài hạn
    Lỗi phạm trù mà những người ủng hộ kiểu mạnh thường mắc phải là giả định rằng người ta sẽ viết cùng một đoạn mã nhưng chỉ bỏ kiểu đi. Thực tế không viết như vậy

    • Rốt cuộc đây chắc chắn là một cuộc tranh luận gây bực bội, vì ai cũng rút ra kết luận hoàn toàn khác nhau từ trải nghiệm của mình
      Ở gần như mọi điểm, kết luận của tôi ngược hẳn. Việc cần kiểm chứng runtime tất nhiên là đúng, nhưng phần lớn kiểm chứng runtime có thể tránh được
      Về thay đổi yêu cầu, tôi cho rằng kiểu tĩnh trái lại làm việc thích nghi dễ hơn. Trong các hệ thống kiểu động tôi từng gặp, những giả định quan trọng về cấu trúc dữ liệu bị rải rác khắp nơi, đôi khi được kiểm tra động bằng tiền/hậu điều kiện, đôi khi chỉ nằm trong test, hoặc thậm chí không được kiểm tra
      Muốn thay đổi yêu cầu thì phải suy luận toàn bộ tác động lên các giả định ngầm đó, nên việc thay đổi rất đáng sợ. Khởi động app với mã mới thì dễ, nhưng biết liệu có làm hỏng một đường code hiếm gặp mà mình không nghĩ tới hay không thì rất khó
      Tôi thích hơn nhiều một bước phân tích tĩnh nói rằng: “Bạn đã đổi interface này, bạn có biết đường code ở đây từng phụ thuộc vào phần đó không?” Kiểu tĩnh không phải là cách duy nhất, nhưng tôi cho rằng gánh nặng nhẹ hơn rất nhiều so với việc có cùng mức kiểm chứng động và test
    • Việc xem định nghĩa, biến trong REPL và IDE, rồi autocomplete/refactor là tốt, nhưng chỉ hoạt động khi bạn thật sự đang chạy đoạn mã muốn kiểm tra. Theo trải nghiệm của tôi, cách đó không mở rộng tốt
      Hệ thống kiểu không loại bỏ kiểm chứng runtime, nhưng nếu dùng đúng thì giảm cực mạnh
      Khi yêu cầu thay đổi, phần hay nhất là compiler chỉ chính xác cần sửa gì để mọi thứ chạy lại. Làm cùng việc trong ngôn ngữ động thì bạn phải tự lần theo, chờ unit test fail, và cầu mong không có đường nào lọt qua
    • Với tôi thì ngược lại, phần này dễ hơn nhiều trong kiểu tĩnh
      Tôi có thể tìm mọi vị trí một kiểu cụ thể được dùng một cách tự tin hơn nhiều, rồi xem từng chỗ có cần thay đổi không. Trong môi trường kiểu động, việc này vụn vặt và phiền phức hơn nhiều
    • Đó có thể là trải nghiệm của bạn, nhưng không phải của tôi. Tôi nghĩ không có đáp án đúng cho tất cả mọi người. Chọn cái phù hợp với mình rồi tiếp tục thôi
  • Với tư cách là một lập trình viên đã viết hàng trăm nghìn dòng bằng C++, Python và JS, tôi cũng không chắc lắm. Chuyện này không rõ ràng đến thế
    Tôi làm việc hiệu quả với cả ba, nhưng nhìn chung Python thắng. Tuy vậy tôi sẽ không viết game engine hay video codec bằng Python
    JavaScript thiếu nhất quán và kỳ quặc, nhưng di sản của Netscape đã trói tất cả chúng ta vào đó rồi
    Trong phong cách rất hướng đối tượng, với nhiều lớp lồng nhau khổng lồ, kiểu tĩnh ở thời điểm biên dịch/phân tích cú pháp có thể giảm được nhiều sai sót. Nhưng tôi dần thấy hướng đối tượng nói chung gần như là một thảm họa, còn các hàm đơn giản và dữ liệu có cấu trúc hầu như luôn thắng về độ đơn giản và khả năng bảo trì
    Các language server và IDE hiện đại cũng có thể bắt được nhiều lỗi gõ khi phát triển JS/Python. Tôi khá khó tính với nhiều khía cạnh của lập trình, nhưng không có lập trường mạnh về kiểu tĩnh so với kiểu động. Cả hai đều có hàng triệu dự án thành công

    • C++ là một ngôn ngữ khá khó nhằn để đạt năng suất nói chung, không chỉ riêng hệ thống kiểu
      Tôi cho rằng mức tăng năng suất lớn nhất mà một ngôn ngữ có thể mang lại là garbage collection. Tôi tò mò hơn về việc Go, với cú pháp và kiểu đơn giản hơn nhiều, sẽ so sánh ra sao. Java cũng có thể tốt hơn. Dù dài dòng, với hầu hết công việc, tải nhận thức của nó chỉ bằng một phần của C++
    • Trong Python, tôi chủ yếu viết theo phong cách hàm, nhưng kiểu vẫn giúp ích rất nhiều
      Tôi hay gõ sai và cũng thường nhầm thứ tự đối số. Đặc biệt khi làm machine learning, kiểu giúp ích rất lớn. Điều tôi muốn tránh nhất là mất 30 phút xử lý dữ liệu rồi code huấn luyện bị chết
      Tôi nghĩ gradual typing của Python là một điểm cân bằng rất tốt giữa prototyping nhanh và việc thêm chú thích kiểu khi một hàm đã đủ trưởng thành
    • Có thể bạn sẽ thích video về lập trình hướng protocol (Swift) ở WWDC vài năm trước: https://www.youtube.com/watch?v=p3zo4ptMBiQ
  • Cuộc tranh luận rằng kiểu mạnh tốt hơn kiểu yếu thì đã ngã ngũ, nhưng liệu kiểu tĩnh có tốt hơn kiểu động hay không thì vẫn chưa
    Người ủng hộ kiểu tĩnh cho rằng trình biên dịch cần xác minh các bất biến về kiểu để kiểm tra “tính đúng đắn”, còn người ủng hộ kiểu động xem đó là lãng phí thời gian
    Tôi chắc chắn thuộc phe sau. Vì trình biên dịch chỉ có thể kiểm tra tính đúng đắn của kiểu, chứ không phải tính đúng đắn của chương trình. Tính đúng đắn của kiểu là điều cần cho tính đúng đắn của chương trình, nhưng không đủ. Những người ủng hộ kiểu tĩnh không thừa nhận điểm này, và lầm tưởng rằng kiểu tĩnh đảm bảo được nhiều thứ hơn thực tế
    Hãy xem ví dụ birthdayGreeting trong bài. Tác giả vui mừng vì kiểu tĩnh bắt được lỗi ở birthdayGreeting("John", "20"), do "20" không phải là số. Nhưng birthdayGreeting(" ", 123) thì không bị bắt. " " không phải là tên. birthdayGreeting("Anna," -12335) cũng không bị bắt. Ngược lại, birthdayGreeting("Anna" 4.5) lại bị bắt, trong khi 4.5 cũng có thể được xem là tuổi, nên có thể nói việc bắt lỗi đó mới là sai
    Điều này quan trọng. “Lỗi kiểu” thì dễ bắt một cách tầm thường, nhưng lỗi ngữ nghĩa có thể ẩn náu suốt nhiều năm. Ví dụ như tràn số ở số dư tài khoản lưu bằng uint, một số lẽ ra phải là số nguyên tố tại một vị trí nhất định nhưng lại không phải, hay danh sách không được phép rỗng. Ngay cả kiểu phụ thuộc cũng không đảm bảo được các bất biến như vậy
    Nếu khó tin, cứ tìm các lỗi lớn từng gây nổ tàu vũ trụ hoặc tai nạn ô tô. Theo tôi biết, không có trường hợp nào nguyên nhân là lỗi kiểu thật sự; đa số áp đảo là lỗi ngữ nghĩa
    [1] Phần lớn mọi người không hiểu rằng cần nhìn kiểu tối thiểu theo hai trục: mạnh/yếu và tĩnh/động, nên cứ nhầm lẫn kiểu yếu với kiểu động. C là kiểu tĩnh nhưng yếu, Python là kiểu mạnh nhưng động, còn JavaScript là kiểu yếu và động

    • Khá nhiều ví dụ trong số này hoàn toàn có thể được bắt bằng kiểu tĩnh, tùy hệ thống kiểu. Nhưng điều quan trọng hơn là đoạn “lỗi kiểu thì dễ bắt một cách tầm thường”
      Chính vì vậy tôi đứng về phía kiểu tĩnh. Vì nó quá tầm thường nên có thể xử lý theo cách khai báo, ngay cạnh đoạn mã cần kiểm tra, với phản hồi tức thì, tại mọi điểm gọi và mọi biểu thức con/câu lệnh
      Chú thích kiểu không có nghĩa là ngữ nghĩa hay logic miền là đúng; những thứ đó vẫn phải được kiểm thử. Nhưng nó có thể thay thế hàng chục bài test vụn vặt trực giao với logic mà bạn quan tâm. Thành thật mà nói, rất ít người viết đầy đủ những bài test như vậy
    • Tôi là tác giả bài viết. Tôi thấy các ví dụ này không phải phản ví dụ, mà ngược lại còn minh họa cho luận điểm của tôi
      Ở phần sau của bài, tôi đã nói rằng khi tạo kiểu từ những nơi như đầu vào người dùng thì sẽ xác thực. Vì vậy kiểu Name luôn hợp lệ, và " " không phải là tên. Vì kiểu đảm bảo đó là một tên hợp lệ, trong codebase của chúng tôi chắc chắn sẽ bắt được
      birthdayGreeting("Anna" 4.5)birthdayGreeting("Anna," -12335) trong JS thực ra là hợp lệ vì number là số dấu phẩy động. Tuy nhiên khi viết bài tôi đã nghĩ đến số nguyên. Đây là một ví dụ khác cho thấy kiểu nghiêm ngặt hơn TS, chẳng hạn Rust, giúp định nghĩa bất biến tốt hơn
      Tóm lại, khi cố đưa ra một ví dụ đơn giản, tôi đã không định nghĩa mọi kiểu nghiêm ngặt như thường lệ, và kết quả là càng lộ thêm những lỗi lẽ ra đã được kiểu bắt
    • Lý do các ví dụ này thú vị là, theo tôi, thứ cần có là kiểu Name biểu thị một tên luôn hợp lệ và kiểu Age biểu thị một tuổi luôn hợp lệ
      Đặt việc xác thực tại một nơi duy nhất là constructor của các kiểu đó, rồi nhiều phương thức như birthdayGreeting có thể dùng giá trị của kiểu đó mà không phải chịu trách nhiệm thêm
      Tôi không biết cách triển khai tốt mẫu này nếu không có kiểm tra kiểu, hoặc ít nhất là type hint tùy chọn và phân tích tĩnh. Ngược lại, việc xác thực đầu vào trong mọi phương thức là quá nặng nề, còn giả định rằng caller truyền giá trị hợp lệ rồi kiểm thử để tránh sự cố lớn cũng không khiến tôi hài lòng
    • Nhắc đến nổ tàu vũ trụ là tôi nghĩ ngay đến [1]. Tai nạn nổi tiếng này có liên quan đến kiểm tra kiểu trong đó một số kiểu được định nghĩa trước phạm vi cho phép, và cũng ăn khớp với ý kiến của bạn. Việc buộc birthdayGreeting nhận phạm vi 1~150 chẳng hạn có thể làm dễ dàng trong ADA
      Trong các vấn đề công khai liên quan đến tàu vũ trụ, cũng có vài lỗi có lẽ đã có thể bắt được bằng kiểm tra kiểu tốt hơn. Việc chuyển đổi giữa hệ mét/hệ Anh cũng có thể đưa đơn vị vào kiểu. Tuy nhiên với trường hợp [2], nhiều khả năng lỗi nằm ở phía kiểm thử tích hợp
      Tất nhiên kiểm tra kiểu không thể tìm ra mọi vấn đề trong mã, đặc biệt là vấn đề thuật toán, và cũng không thay thế kiểm thử. Dù vậy, phản hồi tức thì và type hint trong giai đoạn phát triển là vô cùng quý giá
      Trong ví dụ, cũng có thể đổi chuỗi tên thành kiểu người hoặc đối tượng
      [1]: https://en.m.wikipedia.org/wiki/Ariane_flight_V88
      [2]: https://en.m.wikipedia.org/wiki/Mars_Climate_Orbiter
    • Tôi không hiểu vì sao việc trình biên dịch chỉ kiểm tra tính đúng đắn của kiểu chứ không phải tính đúng đắn của chương trình lại là căn cứ để nói đó là lãng phí thời gian
      Không có giải pháp hoàn hảo, nhưng có rất nhiều giải pháp có giá trị
  • Vấn đề này đã có sự hội tụ đáng kể. Giờ đây hầu hết các ngôn ngữ đều cung cấp suy luận kiểu ở một mức độ nào đó ở cấp câu lệnh. C++ cũng có auto
    Nhờ vậy, boilerplate về kiểu trong code đã giảm đi rất nhiều. Thời phải viết đầy đủ các kiểu iterator dài ngoằng trong vòng lặp for của C++ đã qua rồi
    Khai báo hàm và các trường của struct là những nơi cần thông tin kiểu để đọc hiểu code. Khi chương trình vượt quá vài trăm dòng hoặc có hơn một lập trình viên, một mức chú thích nào đó là điều bắt buộc
    Sự phản đối chính đương nhiên đến từ người dùng Python và JavaScript. Python về sau gắn thêm một hệ thống kiểu mang tính khuyến nghị rất kỳ lạ, còn JavaScript về sau gắn thêm TypeScript. Cả hai đều là hệ thống kiểu được bổ sung về sau, và được dùng trong môi trường trộn lẫn code có kiểu với code không có kiểu. Điều này rất đau đớn
    LISP cũng đã gắn thêm hệ thống kiểu từ nhiều thập kỷ trước bằng “flavors” và Common LISP Object System, và chuyện đó cũng không đẹp đẽ gì. Bài học là nếu gắn thêm hệ thống kiểu về sau thì mọi thứ sẽ trở nên lộn xộn

    • Tôi nghĩ hệ thống kiểu của Python khá ổn nếu xét trong bối cảnh của nó
      Nó có những tính năng tốt như Optional buộc phải kiểm tra trước khi dùng None, hay structural subtyping thông qua typing.Protocol. Nếu Python được thiết kế với kiểu ngay từ đầu thì đã tốt hơn, nhưng xét đến yêu cầu phải tích hợp với code Python hiện có và không được làm hỏng bất kỳ code nào, thì nó đã làm khá tốt
      Vấn đề lớn hơn của static typing trong Python là hệ sinh thái và tập quán. Điều này đặc biệt tệ hơn khi nhiều lập trình viên thực chất dùng Python với vai trò nhà khoa học dữ liệu. Vì ngại viết method signature tử tế nên họ lạm dụng *args/**kwargs
      Việc các method truyền qua lại DataFrame hoặc dictionary như một cái túi tạp nham là cực kỳ phổ biến. Sẽ được cộng điểm nếu method thêm/xóa column hoặc field, khiến bạn không biết trong cái túi dữ liệu đó có gì cho đến khi chạy code hoặc đọc hết từng dòng
      Tất nhiên gần như ngôn ngữ nào cũng có thể làm tương tự. Trong C# có thể dùng dynamic cho mọi kiểu, hoặc mọi method trong Go đều nhận interface{}. Nhưng Python trong thời gian dài đã tích cực khuyến khích cách tiếp cận này, và đến nay nhiều tutorial cho người mới vẫn giới thiệu “nhận *kwargs thì không cần đổi signature của hàm” như một tính năng nâng cao dành cho người thông minh, thay vì một cái bẫy khủng khiếp
    • “Hệ thống kiểu” của Python và TypeScript được thiết kế để đưa kiểu vào dần dần trong môi trường không phải greenfield
      Nó là điều thiết yếu cho migration từng bước, và việc chúng hoạt động theo cách này là hoàn toàn dễ hiểu
    • Flavors và CLOS không phải là “hệ thống kiểu”. Chúng là “hệ thống đối tượng”, và cũng không hẳn xấu xí
      Flavors được đưa vào Lisp khi chưa có hệ thống kiểu, còn CLOS về sau được thêm vào Common Lisp, một Lisp vốn đã có hệ thống kiểu
    • Hệ thống kiểu của TypeScript thật sự tuyệt vời đến đáng kinh ngạc. Ước gì nhiều hệ thống kiểu khác cũng biểu đạt được đến mức đó
  • Con người luôn cuồng tín với những thứ họ bám víu bằng cảm xúc hơn là lý trí
    Lập luận “kiểu làm giảm bug” nghe có vẻ hợp lý hơn là một sự thật
    https://blog.metaobject.com/2014/06/the-safyness-of-static-t...
    Không phải là chưa từng có đủ nỗ lực kiểm chứng. Thậm chí có thể nói lập luận đó đã bị phản chứng
    Tuy vậy cá nhân tôi thích static typing. Chủ yếu là vì tác dụng tài liệu hóa, và có thể không phải ngẫu nhiên, đó cũng là tác dụng tích cực duy nhất thực sự có bằng chứng thực nghiệm vững chắc

    • Tôi nghĩ câu “khi thay đổi một codebase tồn tại lâu dài, kiểu giúp giảm bug” không có nhiều điểm đáng tranh cãi
      Ngăn regression có thể quan trọng hơn việc viết đúng code ngay từ đầu, và khi nhìn vào code không tiến hóa thì ta không đánh giá được phần đó
      Cụ thể, trong một dự án JavaScript thuần lớn, việc xóa field khỏi object về bản chất là một bãi mìn và trong quá khứ đã gây ra nhiều bug. Ngược lại, trong một dự án TypeScript hoàn chỉnh, có thể thực hiện thay đổi tương tự một cách tự tin
    • Vì đây là một tuyên bố rất mạnh, hãy xét một ví dụ cực đoan: phần trung gian của CompCert đã được xác minh hình thức: https://users.cs.utah.edu/~regehr/papers/pldi11-preprint.pdf
      Tôi nghĩ có thể xem đây là bằng chứng thực nghiệm rằng kiểu tĩnh rất mạnh giúp giảm bug
      Rốt cuộc, như nhiều bình luận đã nói, kiểu không phải là vấn đề nhị phân có/không, mà là một phổ lớn với nhiều trục như tĩnh/động, mạnh/yếu, v.v. Giữa các hệ thống kiểu cũng có khác biệt lớn, và cách con người áp dụng hệ thống kiểu đó vào vấn đề cũng khác nhau rất nhiều
      Ngay cả trong ngôn ngữ kiểu tĩnh mạnh, bạn vẫn có thể biểu diễn mọi thứ bằng chuỗi rồi liên tục chuyển đổi, và thực chất đó là làm việc như trong ngôn ngữ kiểu động. Ngược lại, nếu tận dụng công cụ mà hệ thống kiểu cung cấp để tạo các lớp biểu diễn giá trị hợp lệ và khẳng định các invariant quan trọng, bạn sẽ nhận được lợi ích
    • Tôi thích kiểu tĩnh mạnh hơn nhiều vì nó khiến code có tính tự tài liệu hóa
      Năng suất tăng không phải vài lần mà là theo nhiều bậc độ lớn. Tôi nói điều này từ trải nghiệm dùng nhiều ngôn ngữ bao phủ một dải rộng của phổ này như C, C++, Java, Python, JavaScript, TCL, v.v.
      Việc suy luận về code mà gần đây không đụng tới, dù là trong dự án hiện tại hay dependency, trở nên dễ hơn nhiều. Bạn có thể tập trung hơn vào vấn đề trước mắt vì không phải liên tục rẽ nhánh để tìm xem chính xác mình có thể làm gì với object mà một hàm trả về
      Cũng có cảm giác nhẹ nhõm dễ chịu khi compile thành công, nhưng đó là chuyện phụ
    • Lập luận của static typing là vì kiểu là tĩnh nên nó khiến bug về kiểu trở nên bất khả thi
      Không có sự bám víu cảm xúc nào ngoài cơn giận khi bị đáp lại bằng “nhưng nó đâu có loại bỏ mọi bug!”
    • Nếu ai đó nói “lập luận cảm tính có thể được tạo ra nhanh hơn lập luận lý tính” thì bạn sẽ cảm thấy thế nào?
      Cuộc tranh luận này đúng là mang lại cảm giác như vậy
      Tôi muốn sự hợp lý đơn giản rằng khi cố đối xử hashmap như Apple hay String, compiler sẽ nói “không được”