Nguyên tắc KISS áp dụng cho thiết kế ngôn ngữ lập trình?


14

KISS ("giữ cho nó đơn giản, ngu ngốc" hoặc "giữ cho nó đơn giản ngu ngốc", xem ví dụ ở đây ) là một nguyên tắc quan trọng trong phát triển phần mềm, mặc dù nó rõ ràng có nguồn gốc từ kỹ thuật. Trích dẫn từ bài viết trên wikipedia:

Nguyên tắc này được minh họa rõ nhất qua câu chuyện Johnson trao cho một nhóm kỹ sư thiết kế một số công cụ, với thách thức là máy bay phản lực mà họ thiết kế phải được sửa chữa bởi một thợ máy trung bình trong lĩnh vực trong điều kiện chiến đấu chỉ bằng những công cụ này. Do đó, 'ngu ngốc' đề cập đến mối quan hệ giữa cách mọi thứ phá vỡ và sự tinh tế có sẵn để khắc phục chúng.

Nếu tôi muốn áp dụng điều này vào lĩnh vực phát triển phần mềm, tôi sẽ thay thế "máy bay phản lực" bằng "phần mềm", "thợ máy trung bình" bằng "nhà phát triển trung bình" và "trong điều kiện chiến đấu" bằng "theo sự phát triển / bảo trì phần mềm dự kiến điều kiện "(thời hạn, hạn chế thời gian, cuộc họp / gián đoạn, các công cụ có sẵn, v.v.).

Vì vậy, một ý tưởng thường được chấp nhận là người ta nên cố gắng giữ cho một phần mềm đơn giản (hoặc đơn giản là ngu ngốc , trong trường hợp bạn bỏ qua dấu phẩy) để dễ dàng làm việc với nó sau này.

Nhưng nguyên tắc KISS có thể được áp dụng cũng cho thiết kế ngôn ngữ lập trình không? Bạn có biết bất kỳ ngôn ngữ lập trình nào được thiết kế riêng cho nguyên tắc này không, tức là "cho phép một lập trình viên trung bình trong điều kiện làm việc trung bình viết và duy trì càng nhiều mã càng tốt với nỗ lực nhận thức ít nhất"?

Nếu bạn trích dẫn bất kỳ ngôn ngữ cụ thể nào thì sẽ rất tuyệt nếu bạn có thể thêm một liên kết đến một số tài liệu trong đó ý định này được thể hiện rõ ràng bởi các nhà thiết kế ngôn ngữ. Trong mọi trường hợp, tôi sẽ quan tâm để tìm hiểu về ý định (tài liệu) của các nhà thiết kế hơn là ý kiến ​​cá nhân của bạn về một ngôn ngữ lập trình cụ thể.


4
Bạn đã khám phá họ ngôn ngữ cơ bản (hoặc ít nhất, ý định ban đầu đằng sau chúng) chưa?

4
BASIC và Pascal ... cả hai đều được thiết kế như ngôn ngữ hướng dẫn.
Michael Brown

1
Bạn có ý nghĩa gì bởi đơn giản? Hầu hết các ngôn ngữ đều khá đơn giản, trong đó không có nhiều ngôn ngữ. Không có gì phức tạp hơn trình biên dịch chương trình. Các khung thường phức tạp, nhưng chúng được thiết kế để có thể "viết và duy trì càng nhiều mã càng tốt với nỗ lực nhận thức ít nhất".
pdr

7
Lược đồ (r) đã được sử dụng làm ngôn ngữ giảng dạy trong nhiều năm (nhiều thập kỷ?) Và được phát triển bằng cách đơn giản hóa LISP và Algol (và có thể nhiều hơn). Cuốn sách SICP mất rất ít thời gian để dạy ngôn ngữ này. Ngoài ra, DSL đến với tâm trí.
vpit3833

3
@Giorgio hãy xem Haskell. Nó có rất nhiều và tôi có nghĩa là rất ít bit dựng sẵn. Hầu hết các toán tử là các hàm nằm trong thư viện chính, nhưng không cần thiết cho ngôn ngữ, nó đã mất rất nhiều thời gian để loại bỏ tất cả các phần không cần thiết
Jimmy Hoffa

Câu trả lời:


15

Khi tôi nghĩ về chủ nghĩa tối thiểu, tôi nghĩ về Lisp và Go . Với Lisp, tất cả những gì bạn có là các chức năng và danh sách, đơn giản như bạn có thể nhận được (tốt, có thêm một chút, nhưng bất cứ điều gì). Tuy nhiên, tôi nghĩ trường hợp Go thú vị hơn.

Go được thiết kế đơn giản (đó là một bài đọc khá). Thuật ngữ họ sử dụng là "tính trực giao tính năng", có nghĩa là bất kỳ tính năng nào chỉ nên được thêm vào nếu nó cung cấp một cái gì đó thực sự độc đáo. Điều này dường như xuất phát từ sự liên quan của các tác giả ( Russ CoxRob Pike trong tâm trí) với Plan9 , đó là sự tái hiện của UNIX với sự đơn giản trong tâm trí. (Nếu bạn quan tâm đến thiết kế tối thiểu, bài viết của Rob Pike trên một hệ thống cửa sổ đơn giản là một cách đọc tốt.)

Dưới đây là một số ví dụ đơn giản về cú pháp:

Chỉ có một cấu trúc lặp

Một vòng lặp có thể trông giống như một trong những điều sau đây:

Vòng lặp vô hạn

for {
}

Trong khi lặp lại

for <conditional> {
}

Vòng lặp truyền thống

for i := 0; i < 10; i++ {
}

Vòng lặp Foreach

// works for maps or arrays
for k, v := range arr {
}

Công tắc đa năng

switch {
    // cases must be evaluate to a boolean
}

switch <value> {
}

switch t := <value>; t {
    // can use t inside
}

Nhiều lợi nhuận

return val1, val2, ...
  • Loại bỏ sự cần thiết phải ném (vượt qua lỗi như giá trị trả lại cuối cùng)
  • Loại bỏ sự cần thiết cho các tham số ra
  • Loại bỏ sự cần thiết cho bộ dữ liệu

Giao diện

type X interface {
    DoSomething()
    String() string
}
  • Giải quyết các vấn đề tương tự như thuốc generic
  • Cho phép trừu tượng hóa

Nhúng

type A struct {
    Thing string
}

type B struct {
    A // embeds A in B, so B.Thing refers to A.Thing
}
  • Giải quyết vấn đề tương tự như thừa kế
  • Nhu cầu cần thiết cho các lớp học

Kênh truyền hình

Có thể được sử dụng để thực hiện semaphores

var c = make(chan bool, 1)
c<-true // semaphore lock
<-c // semaphore free

Được sử dụng cho tin nhắn chuyển giữa các chủ đề

func produce(c chan<- bool) {
    for {
        c <- true
    }
}
func consume(c <-chan bool) {
    for {
        <-c
    }
}

var c = make(chan bool)
go produce(c)
go consume(c)

Có thể được sử dụng để xử lý các sự kiện không đồng bộ

func async() chan bool {
    var c = make(chan bool)
    go doSomethingAsync(c)
    return c
}

// wait for long async process to finish
c := async()
select {
    case _ = <-c:
}

Phần kết luận

Tôi sẽ không đi sâu vào từng phần của cú pháp, nhưng hy vọng bạn có thể thấy chủ nghĩa tối giản có thể làm gì. Ngôn ngữ này hấp dẫn không phải vì nó bổ sung vô số tính năng mới, mà bởi vì nó sử dụng các tính năng tốt nhất từ ​​các ngôn ngữ khác mà không cần thêm bất cứ điều gì.

Thường có một cách "tốt nhất" để giải quyết vấn đề. Ví dụ, trong danh sách gửi thư, rất nhiều người dùng phàn nàn về việc không có thuốc generic. Sau khi thảo luận, họ nhận ra rằng mọi thứ họ muốn làm đều có thể được thực hiện với các giao diện. Đọc về hiệu quả đi cho các ví dụ về cú pháp thành ngữ.

Lợi ích của ngôn ngữ KISS là có thể viết mã thành ngữ, vì kiểu mã bị giới hạn bởi ngôn ngữ. Ví dụ: trong Go, bạn không thể viết một cái gì đó như thế này:

if <condition>
    statement;

Bạn phải sử dụng niềng răng xoăn:

if <condition> {
    statement;
}

Có nhiều ví dụ khác về điều này trong cú pháp, giúp cho việc đọc mã người khác dễ dàng hơn.

Lợi ích của ngôn ngữ KISS so với các ngôn ngữ đặc trưng:

  • dễ hiểu mã của người khác
  • dễ dàng hơn để tìm hiểu toàn bộ ngôn ngữ (C ++ nổi tiếng là khó hiểu)
  • tập trung vào thuật toán, không phải cú pháp

5
Theo ý kiến ​​khiêm tốn của tôi, cú pháp của vòng lặp for truyền thống không phải là KISS. Tất nhiên nó quen thuộc với mọi lập trình viên giống C, nhưng tôi nhớ lần đầu tiên tôi học được điều này: tôi đã rất bối rối. BASIC có nhiều KISS : for i=1 to 10, hoặc python:for item in group
mouviciel

3
@mouviciel - Tôi hầu như không bao giờ sử dụng vòng lặp truyền thống. Vấn đề for i = 1 to 10là liệu có ibao giờ là 10. Điều này có thể phụ thuộc vào ngôn ngữ (bash bao gồm 10, python không). Vòng lặp truyền thống là phổ quát.
beatgammit

+1, đặc biệt là tóm tắt về lợi ích của chủ nghĩa tối giản.
Giorgio

1
Đi như một trường hợp nghiên cứu là thú vị bởi vì nó không được coi là ngôn ngữ KISS bởi những kẻ gièm pha của nó. Trong thực tế, đối số diễn ra như sau: một ngôn ngữ "đơn giản" không dẫn đến mã "đơn giản" hoặc dễ hiểu hơn. Đôi khi phải có sự phức tạp hơn (giả sử, hỗ trợ khái quát tốt) để mã dễ hiểu hơn.
Andres F.

13

Tôi nghĩ Zen của Python sẽ nói rõ tại sao Python là một ngôn ngữ đơn giản tốt hơn nhiều so với tôi có thể:

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Chỉnh sửa để phản hồi lại @Giorgio

Như câu hỏi của bạn,

"Bạn có biết bất kỳ ngôn ngữ lập trình nào được thiết kế riêng cho nguyên tắc này không, tức là 'cho phép một lập trình viên trung bình trong điều kiện làm việc trung bình viết và duy trì càng nhiều mã càng tốt với nỗ lực nhận thức ít nhất'"

Python là, với tôi, những gì ngay lập tức đến với tâm trí. Thiết kế của Python là phản ứng trực tiếp với phương pháp của Perl, "Có nhiều cách để làm điều đó" nổi tiếng. Mặc dù điều đó thật tuyệt vời, cho phép các lập trình viên viết mã rất dễ dàng, nhưng nó không giúp bảo trì. Trước khi quản lý một chương trình (được viết rất kém, tôi sẽ thừa nhận) được viết bằng Perl, tôi đánh giá cao Python buộc cả thực hành mã hóa tốt và ngôn ngữ súc tích xuống cổ họng của bạn.

Python cũng không bắt bạn phải tuân theo một phương pháp cụ thể khi viết chương trình. Tôi có thể chọn theo phong cách lập trình hướng đối tượng nghiêm ngặt hoặc tôi có thể viết một tập lệnh đơn giản để thực hiện tuần tự. Không phải khởi tạo một lớp, sau đó gọi main()phương thức như bạn buộc phải làm trong Java là rất hay. (Ngoài ra, in ra thiết bị xuất chuẩn trong Python là tuyệt vời, nhưng đó là một điểm khá null).

Cuối cùng, phương pháp "Bao gồm pin" bao gồm một thư viện tiêu chuẩn mở rộng với ngôn ngữ là tuyệt vời. Thay vì săn lùng một số kho lưu trữ bên ngoài cho các gói, hầu hết những gì tôi cần đã được đưa vào ngôn ngữ. Thật tuyệt khi có repo bên ngoài đó, nhưng không phải tìm hiểu kỹ để thực hiện một thao tác cơ bản thực sự tiện dụng.


3
Cảm ơn các trích dẫn hữu ích. Đây có phải là ý định chính thức của (các) nhà thiết kế của Python không? Ngoài ra, lưu ý rằng không phải tất cả các độc giả có thể đồng ý với ý kiến ​​của bạn về Python, vì vậy có lẽ hơi quá mạnh để nói rằng "Python là một ngôn ngữ đơn giản" là một thực tế nổi tiếng và được chấp nhận. Liệu có thể định nghĩa nó là "Python được thiết kế đơn giản trong tâm trí" không?
Giorgio

@Giorgio - Đó là kinh nghiệm của nhiều nhà phát triển quanh đây rằng python thực sự là một ngôn ngữ đơn giản. Đơn giản để học, đơn giản để viết và đơn giản để đọc. Về báo giá, vì python là "pin đi kèm", bạn có thể lấy nó trực tiếp từ ngôn ngữ bằng import thislệnh.
mouviciel

1
TCL cũng là một câu trả lời tốt cho điều này và IIRC cũng là một câu trả lời cho sự phức tạp của PERL.
jk.

@mouviciel Sau khi bắt gặp một số mã spaghetti Python phức tạp đáng kinh ngạc ở nhiều nơi, tôi không còn nghĩ Python thành công trong các mục tiêu của mình ở đây. Python không tốt hơn hầu hết các ngôn ngữ khi nói đến sự đơn giản - nó có thể rất tốt trong việc vô tình làm xáo trộn.
Izkata

1
Python đơn giản miễn là mọi người tránh xa hầu hết các __magic_fifts __ () và @decorators.
weberc2

3

Chìa khóa chính để duy trì "sự đơn giản" là nhận ra khi nào sự phức tạp sẽ là cần thiết và có các bộ phận của hệ thống có thể giải quyết tốt nhất, hãy làm như vậy. Có một loại tham chiếu đối tượng duy nhất không phân biệt giữa các giá trị bất biến, giá trị có thể thay đổi và thực thể, làm cho Java "đơn giản", không loại bỏ sự cần thiết phải phân biệt giữa các giá trị và thực thể; nó chỉ đơn thuần tước đi các lập trình viên của các công cụ để giúp họ làm như vậy.

Tổng quát hơn, nếu một người đang cố gắng có ngôn ngữ lập trình hoặc tính năng khung hỗ trợ nhiều trường hợp sử dụng, thì người ta phải đảm bảo rằng sẽ không có bất kỳ tình huống nào mà các hành vi khác nhau sẽ phù hợp trong các trường hợp sử dụng khác nhau, nhưng trình biên dịch sẽ không thể nói họ xa nhau. Thêm nhiều loại vào một ngôn ngữ hoặc khung có vẻ như là một sự phức tạp, nhưng trong nhiều trường hợp nó thực sự có thể làm cho mọi thứ dễ dàng hơn.

Ví dụ, hãy xem xét sự lộn xộn của các quy tắc xung quanh các loại đã ký và không dấu trong C. Độ phức tạp của các quy tắc này xuất phát từ thực tế là một số mã sử dụng các kiểu số nguyên không dấu để biểu diễn các số, trong khi mã khác sử dụng chúng để biểu thị các thành viên của vòng đại số gói (cụ thể là tập hợp các số nguyên mod 2ⁿ). Các quy tắc chuyển đổi loại hành xử theo cách đôi khi phù hợp với một cách sử dụng và đôi khi theo cách phù hợp với cách sử dụng khác. Có các kiểu gói và không gói riêng biệt sẽ tăng gấp đôi số loại số nguyên, nhưng có thể đơn giản hóa các quy tắc liên quan đến chúng:

  • Bất kỳ loại số nguyên không vòng nào có kích thước bất kỳ có thể được gán cho một vòng có kích thước bất kỳ; một chiếc nhẫn chỉ có thể được gán cho một chiếc nhẫn có kích thước bằng hoặc nhỏ hơn .

  • Các hoạt động khác với các toán tử quan hệ liên quan đến số nguyên không vòng và vòng sẽ ngầm chuyển đổi số nguyên thành loại vòng.

  • Nhẫn có thể được đúc rõ ràng thành số hoặc vòng lớn hơn; giá trị của vòng không dấu là số nguyên không âm nhỏ nhất, khi được thêm vào số 0 của vòng, sẽ mang lại giá trị vòng. Giá trị của vòng đã ký là số nguyên có độ lớn nhỏ nhất, khi được thêm vào số 0 của vòng, sẽ mang lại giá trị vòng, với giá trị âm được ưu tiên trong trường hợp hòa.

  • Ngoại trừ như đã đề cập ở trên, các vòng được giới hạn hoạt động với các vòng có cùng kích thước hoặc nhỏ hơn.

  • Các thao tác trên các số nguyên không thuộc các loại khác nhau sẽ làm đảo lộn các số thành một loại có thể chứa tất cả các giá trị của toán hạng hoặc bị trình biên dịch từ chối nếu không có loại phù hợp tồn tại

Việc xác định các loại vòng dường như sẽ tăng gấp đôi số loại số nguyên, nhưng sẽ đơn giản hóa rất nhiều việc viết mã di động. Sử dụng cùng loại không dấu cho số và vòng làm giảm số lượng loại, nhưng dẫn đến các quy tắc phức tạp khiến cho việc viết mã di động hiệu quả gần như không thể.


+1 Hoàn toàn đồng ý. Một ngôn ngữ "đơn giản" (đơn giản theo nghĩa là có một thông số nhỏ) không nhất thiết dẫn đến mã đơn giản, hoặc dễ dàng lý luận cho lập trình viên.
Andres F.

Nhận xét tuyệt vời. Thêm sự phức tạp nên được xem xét về chi phí và lợi ích.
dùng1071847

2

Có thể cho rằng, Tcl đã được phát triển dọc theo những dòng này. Toàn bộ ngôn ngữ có thể được mô tả chỉ trong 12 quy tắc trên một trang người đàn ông . Nó rất đơn giản và nhất quán.

Người tạo ra Tcl tuyên bố như sau là các mục tiêu ban đầu:

  • Ngôn ngữ phải có thể mở rộng: mỗi ứng dụng phải dễ dàng thêm các tính năng riêng vào các tính năng cơ bản của ngôn ngữ và các tính năng dành riêng cho ứng dụng sẽ xuất hiện tự nhiên, như thể chúng đã được thiết kế thành ngôn ngữ ngay từ đầu.
  • Ngôn ngữ phải rất đơn giản và chung chung, để nó có thể hoạt động dễ dàng với nhiều ứng dụng khác nhau và do đó nó không hạn chế các tính năng mà ứng dụng có thể cung cấp.
  • Vì hầu hết các chức năng thú vị sẽ đến từ ứng dụng, mục đích chính của ngôn ngữ là tích hợp hoặc "kết dính" các phần mở rộng. Do đó, ngôn ngữ phải có cơ sở tốt để tích hợp.

Từ " Lịch sử của Tcl " - http://www.tcl.tk/about/history.html


2

Nếu một ngôn ngữ được cố định trong thiết kế của nó vì KISS, nó không thể phát triển. Một ngôn ngữ không thể phát triển sẽ chết.

Trong video sau đây Guy Steele giải thích một cách khéo léo rằng một ngôn ngữ lập trình phải được phép phát triển và tại sao. Nếu bạn áp dụng KISS, thì làm thế nào một ngôn ngữ có thể phát triển vì một khi được phát hành, bộ công cụ này đã được sửa và không bao giờ được phép thay đổi.

Bài phát biểu của Guy Steele tại hội nghị ACM OOPSLA năm 1998 về "Phát triển ngôn ngữ" thảo luận về tầm quan trọng và các vấn đề liên quan đến việc thiết kế một ngôn ngữ lập trình có thể được phát triển bởi người dùng.

Đó là một video dài một giờ nhưng đáng xem. Nếu bạn không biết Guy Steele là ai thì bạn thực sự nên nói về thiết kế ngôn ngữ.

Tôi chọn video này làm câu trả lời vì tôi tin rằng việc áp dụng KISS vào thiết kế ngôn ngữ nói chung là sai và hy vọng rằng việc xem một bài phát biểu từ một người lưu ý về thiết kế ngôn ngữ sẽ giúp mở rộng hiểu biết của bạn về tương lai của thiết kế ngôn ngữ.

Vì bạn đến đây để tìm hiểu về thiết kế ngôn ngữ và tôi đã cho bạn một lý do để không sử dụng KISS, thật công bằng khi tôi chỉ ra điều gì đó mà tôi tìm thấy sự giúp đỡ trong thiết kế ngôn ngữ. Kích thước nhận thức của các ký hiệu

BIÊN TẬP

Khi tôi viết câu trả lời trên, nó dựa trên lý do: nếu một động cơ chỉ có thể được duy trì với một bộ công cụ cố định thì việc cải cách ý nghĩa của tôi đối với thiết kế ngôn ngữ là ngôn ngữ không thể thay đổi một khi được phát hành. Các ý kiến ​​chỉ ra rằng đó không phải là ý nghĩa. Vì vậy, hãy để tôi đưa ra câu trả lời sửa đổi này sẽ phù hợp hơn với sự hiểu biết sửa đổi.

Nếu bạn nhìn vào các chiều kích nhận thức của các ký hiệu thì người ta sẽ học được rằng có nhiều chiều cạnh tranh liên quan đến thiết kế ngôn ngữ hơn là sự đơn giản và nếu bạn tập trung quá nhiều vào một thứ, bạn sẽ phải chịu đựng những thứ khác. Với câu hỏi tập trung nhiều vào tính đơn giản (KISS), và được hỗ trợ bởi những người lưu ý về thiết kế ngôn ngữ, tôi đã gửi bài phát biểu của Guy Steele để cho thấy rằng cố gắng giữ một thiết kế đơn giản sẽ tác động đến các chiều khác. Quan trọng hơn tôi đang cố gắng truyền đạt rằng bạn cần nhìn vào nhiều chiều và cân nhắc những ưu và nhược điểm của chúng, không chỉ đơn giản.


3
Vì vậy, điều gì xảy ra nếu video trở nên không có sẵn? Hoặc nếu nó không thể truy cập ở một số nước? Câu trả lời của bạn phải đầy đủ và khép kín, vui lòng cập nhật nó để ít nhất cung cấp cho chúng tôi bản tóm tắt ngắn gọn về video, liên kết chỉ các câu trả lời không thực sự hữu ích và có thể bị xóa bất cứ lúc nào.
yannis

3
@AndreasScheinert Cách trả lời hướng dẫn cho thấy rõ rằng các liên kết phải luôn đi kèm với tóm tắt và / hoặc trích dẫn có liên quan.
Thomas Owens

3
Tôi không chắc tôi có thể đồng ý với đánh giá của bạn. Tcl, ví dụ, là đơn giản đáng kể. Nó tồn tại trong nhiều năm chỉ với 11 quy tắc điều chỉnh việc sử dụng nó. Tuy nhiên, đơn giản như vậy, nó đã phát triển đáng kể trong hơn 20 năm tồn tại. Bây giờ nó có 12 quy tắc, điều đó chứng tỏ rằng không chỉ thư viện của nó phát triển, mà bản chất cơ bản của nó cũng đã được phép phát triển trong khi vẫn giữ được tất cả sự đơn giản của nó.
Bryan Oakley

1
"Nếu bạn áp dụng KISS, thì làm thế nào một ngôn ngữ có thể phát triển vì một khi được phát hành, bộ công cụ đó đã được sửa và không bao giờ được phép thay đổi.": Nó phụ thuộc vào ý nghĩa của bạn bằng cách đơn giản và bằng cách phát triển. Bạn có nghĩa là thêm thư viện mới (bằng tiếng Anh, thêm từ mới vào từ vựng) hoặc thêm cấu trúc ngôn ngữ mới (trong tiếng Anh, điều này có nghĩa là thêm, ví dụ các thì của động từ mới, giới từ mới, v.v.). Ngôn ngữ tự nhiên cuối cùng dừng thêm quy tắc ngữ pháp mới trong khi chúng tiếp tục thêm từ mới. Vì vậy, trong trực giác của tôi, đơn giản đề cập đến số lượng quy tắc ngữ pháp, thay vì số lượng thư viện / từ.
Giorgio

3
@Guy Coder: Mặt khác, video bạn đã đăng một liên kết dường như nói điều gì đó khác với những gì bạn nói khi bắt đầu câu trả lời của bạn, cụ thể là một ngôn ngữ sẽ cung cấp một số tính năng cốt lõi cho phép người dùng (lập trình viên) của nó mở rộng ngôn ngữ (thêm thư viện mới). Nó không nói rằng ngôn ngữ cốt lõi sẽ phát triển vô tận.
Giorgio

1

Đây là một trích dẫn lớn từ Hardcore Visual Basic của Bruce McKinney , từ đó đưa lời nói vào miệng của các nhà thiết kế BASIC, KemenyKurtz . Nhấn mạnh của tôi.

Mỗi ngôn ngữ máy tính có một cảm nhận riêng, bầu không khí riêng, tinh thần riêng. Bạn thực sự không thể xác định tinh thần này, nhưng bạn biết nó là gì khi bạn nhìn thấy nó. Tôi nghĩ về Basic như là phản đề của một tuyên bố được gán cho Albert Einstein:

Làm cho mọi thứ đơn giản nhất có thể, nhưng không đơn giản.

Nếu trích dẫn đó được viết bởi các nhà thiết kế ban đầu của Basic, John Kemeny và Thomas Kurtz, thì nó sẽ được đơn giản hóa hơn nữa:

Làm cho mọi thứ đơn giản hơn có thể.

Đó là mâu thuẫn với các lập trình viên Visual Basic. Chúng tôi muốn mọi thứ trở nên đơn giản, thanh lịch và trực quan nhưng không phải vậy. Chúng tôi muốn các chương trình của chúng tôi mô hình hóa thực tế nhưng họ không làm. Chúng tôi muốn ngôn ngữ của chúng tôi hoạt động theo cách chúng tôi nghĩ, không phải cách máy tính hoặc hệ điều hành muốn chúng tôi nghĩ đến nhưng chúng tôi không sẵn sàng trả giá .


Trên một lưu ý liên quan, việc một ngôn ngữ xây dựng "có thể" được tạo ra để phục vụ nhiều mục đích không có nghĩa là một thiết kế như vậy tốt hơn là có các cấu trúc khác nhau cho các mục đích khác nhau đó. Ví dụ, C sử dụng các loại không dấu cả để biểu diễn các đại lượng số và để biểu diễn các thành viên của vòng đại số bao bọc. Thêm một số lượng bất kỳ kích thước hoặc độ ký vào một vòng sẽ mang lại một thành viên của cùng một vòng, trong khi thêm hai số có kích thước bất kỳ và độ ký sẽ mang lại kết quả đủ lớn để xử lý bất kỳ giá trị nào của toán hạng. Sử dụng các loại không dấu cho cả hai mục đích ...
supercat

... Có nghĩa là các quy tắc của C đôi khi phải coi chúng là số và đôi khi là thành viên của vòng, theo cách khiến cho việc viết mã di động sạch sẽ gần như không thể.
supercat

1

Nhưng nguyên tắc KISS có thể được áp dụng cũng cho thiết kế ngôn ngữ lập trình không? Bạn có biết bất kỳ ngôn ngữ lập trình nào được thiết kế riêng cho nguyên tắc này không, tức là "cho phép một lập trình viên trung bình trong điều kiện làm việc trung bình viết và duy trì càng nhiều mã càng tốt với nỗ lực nhận thức ít nhất"?

Một điều tốt là bạn đã làm rõ ý của bạn bằng cách "đơn giản", bởi vì trong tâm trí tôi, một ngôn ngữ lập trình đơn giản là một ngôn ngữ với cú pháp tối thiểu và một vài tính năng (Scheme, Forth, ML), không trực tiếp dịch theo định nghĩa của bạn. Tôi nghĩ rằng bạn đang thực sự tìm kiếm thiết kế ngôn ngữ với RAD (phát triển ứng dụng nhanh) và có khá nhiều trong số đó. Xem chủ đề StackOverflow này, ví dụ: /programming/66227/what-is-the-best-multi-pl platform-rad-ngôn ngữ


"Bởi vì trong đầu tôi, ngôn ngữ lập trình đơn giản là một ngôn ngữ có cú pháp tối thiểu và một vài tính năng (Scheme, Forth, ML)": Chà, thực sự tôi đã sao chép định nghĩa từ wikipedia nhưng tôi có xu hướng xác định hai điều. Ví dụ, Scheme có thể được học rất nhanh và sau đó tôi có thể tập trung vào vấn đề để giải quyết. Các ngôn ngữ khác (như C ++, mà tôi sử dụng tại nơi làm việc) tiếp tục phát triển và bạn phải học những thứ mới mọi lúc. Ngoài ra, mã trở nên ít bảo trì hơn vì các lập trình viên khác nhau có xu hướng sử dụng các tập hợp con khác nhau của ngôn ngữ. Vì vậy, tôi sẽ coi SML và Scheme là "đơn giản".
Giorgio

1

Tôi ngạc nhiên khi chưa có ai nhắc đến C, mặc dù nó đặt sự đơn giản lên hàng đầu theo một số cách quan trọng, đôi khi theo cách triệt để đến nỗi các lập trình viên không đánh giá cao sự đơn giản:

  • Không có phép thuật ẩn. Nếu bạn viết a + bbằng C, bạn có đảm bảo rằng nó sẽ biên dịch thành một lệnh biên dịch đơn.

    Ngay cả trong các ngôn ngữ tương đối đơn giản như Java, một câu lệnh đơn giản như a + bcó thể mất vài micro giây (nếu các biến là chuỗi). Các ngôn ngữ khác thêm nhiều, nhiều hơn nữa vào điều này với ví dụ cực đoan về C ++, nơi a + bcó thể bị quá tải để làm cho những chú voi màu hồng xuất hiện. Không như vậy trong C: Nếu đó không phải là một cuộc gọi chức năng, nó sẽ không mất nhiều hơn vài nano giây. Khả năng dự đoán hiệu suất này là một trong những tính năng chính của C, và nó chắc chắn là một hình thức đơn giản.

  • Các kiểu dữ liệu đơn giản như có thể trong khi vẫn mô tả tất cả các cấu trúc dữ liệu thú vị mà bạn có thể muốn xây dựng trong bộ nhớ: Chỉ có các loại cơ bản mà CPU có thể xử lý + tổng hợp ( struct) + lặp lại (mảng) + tham chiếu (con trỏ) ).

    Đúng là các ngôn ngữ dựa trên lisp khác nhau đơn giản hơn nhiều so với C về vấn đề này. Tuy nhiên, họ không cố gắng cho phép lập trình viên tự do thao tác bộ nhớ như C.

  • Tính trực giao của các tính năng: Bạn có thể tự do kết hợp các tính năng và chúng sẽ hoạt động cùng nhau như mong đợi. Nhiều ngôn ngữ thất bại điều này bằng cách cho phép các cấu trúc nhất định chỉ xuất hiện trong bối cảnh được xác định.

    Lấy ví dụ mảng có độ dài thay đổi trong C và C ++: C cho phép độ dài thời gian chạy ở mọi nơi trong khi các yêu cầu tự do nhất để mở rộng tiêu chuẩn C ++ chỉ cho phép chúng trong các bối cảnh nhất định như mảng tự động và thậm chí chỉ ở chiều thứ nhất. Điều này cho phép C xử lý các mảng đa chiều thực sự với các kích thước chỉ được biết đến trong thời gian chạy, trong khi các lập trình viên C ++ được giảm để viết đi viết data[k + (j + i*lineLength)*lineCount]lại nhiều lần.

    Một ví dụ khác về điều này là con trỏ: Một con trỏ dữ liệu trong C này thực sự không có gì khác hơn là một biến chứa địa chỉ bộ nhớ của một số biến khác. Vì con trỏ tự nó là một biến, nên con trỏ kép là một thứ được xác định rõ. Tức là đã cho những gì intint*được, rõ ràng những gì int**phải được. So sánh điều này với các tài liệu tham khảo C ++ trong đó bạn hoàn toàn không có ý nghĩa gì int&&về ý nghĩa của intint&!)

Đúng là chính sự đơn giản này đã mang lại cho C rất nhiều sự thù hận: Sự đơn giản của ngôn ngữ cho phép các lập trình viên tự do hơn là tốt cho họ, dẫn đến nhiều vấn đề với hành vi không xác định và con trỏ bị xử lý sai. Đặc biệt là tính đơn giản của tính trực giao cho phép lập trình viên xác định một biến int (*array[m])(struct foo* (*arg)[n])là loại có xu hướng khiến độc giả đau đầu vì những thứ thực sự phức tạp có thể được thể hiện dưới dạng rất súc tích bằng cách kết hợp tự do một vài tính năng đơn giản . Đây là loại đơn giản mà C vượt trội và mang lại cho nó danh tiếng là một ngôn ngữ khó sử dụng.

Ngoài ra, sự đơn giản của ngôn ngữ buộc người lập trình phải viết nhiều mã hơn các ngôn ngữ giàu tính năng hơn, cung cấp nhiều mảnh đất màu mỡ hơn cho các lỗi phát triển. Tuy nhiên, nó vẫn là ngôn ngữ cấp cao đơn giản nhất có thể nếu mục tiêu chính của bạn là lập trình một máy tính thực sự chứ không phải một số máy ảo.


Người downvoter có thể để lại lời nhắn giải thích lý do bỏ phiếu của họ không?
Giorgio

C là một mớ hỗn độn. Hãy thử tìm ra những gì bạn sẽ nhận được nếu bạn thêm một chữ ký ngắn vào một chữ dài không dấu và lưu kết quả trong một int. Ngay cả khi không có "dài dài", nó có tám loại số nguyên khác nhau. Điều đó không đơn giản.
Simon B

@SimonB Bạn rõ ràng không hiểu bài viết của tôi là gì. Sự đơn giản về các kiểu số nguyên là thế này: Một kiểu số nguyên có kích thước (tính bằng byte và bit) và có thể được ký hoặc không dấu. Bạn có thể sử dụng bất kỳ sự kết hợp của hai tính năng trực giao này. Đó là tính trực giao mà tôi đang nói đến. C có tất cả các tính năng được yêu cầu để khai thác triệt để phần cứng thực và điều đó đòi hỏi một sự phức tạp nhất định. Tuy nhiên, cách C đối phó với sự phức tạp này là bằng cách kết hợp các khái niệm đơn giản theo kiểu trực giao để cho phép lập trình viên làm những việc phức tạp.
cmaster - phục hồi monica

0

Java Wikipedia - mặc dù không được nêu rõ ràng trong Wikipedia, sự đơn giản được đề cập nhiều lần và là mục tiêu thiết kế ban đầu, vì ngôn ngữ OO nghiêm túc có khả năng (Thuật ngữ được sử dụng một cách lỏng lẻo) trong những ngày đó là C ++, mà như chúng ta đều biết, là mạnh mẽ nhưng có nhiều hơn một vài cái bẫy cho người không sẵn sàng.

JVM được dự định là một cách để đơn giản hóa việc phát triển đa nền tảng và "Viết một lần chạy mọi nơi" đã trở thành một câu thần chú Java trong một vài năm - một lần nữa, ý định là khung đơn giản để triển khai.

Tôi tin rằng C # rơi vào loại đơn giản hóa ngôn ngữ.


Bạn có nghĩa là C # ban đầu cũng được thiết kế như một sự đơn giản hóa của C ++?
Giorgio

2
C ++ và OO? Alan Kay không nghĩ vậy. C # Sinplyfication ???? Tôi không biết những gì bạn có nghĩa là đơn giản. Tôi nghĩ rằng tốt nhất bạn nên nói rằng trước khi tuyên bố những điều như vậy. LISP rất đơn giản vì nó có rất ít cú pháp. C # trái lại có TẤN cú pháp và từ khóa.
AndreasScheinert

Khả năng đơn giản hóa phát triển đa nền tảng không có nghĩa là ngôn ngữ đơn giản. Một cái gì đó có thể là đa nền tảng nhưng vẫn không đơn giản.
Bryan Oakley

@Bryan Đồng ý - thiết kế của Java đã tính đến việc các chương trình đa nền tảng không đơn giản (vào thời điểm đó) và để tạo các chương trình đơn giản, bạn cần một ngôn ngữ đơn giản và cần loại bỏ sự phức tạp của việc xử lý mã cụ thể của nền tảng trong chính chương trình.
mattnz

2
@Giorgio C # là một sự mô phỏng lại về Java. Java được dự định là cả hai ít phức tạp hơn C ++ (ví dụ: không có nhiều kế thừa) và một cái gì đó lớn hơn nhiều (ví dụ thư viện tiêu chuẩn rộng lớn, bộ sưu tập rác).
Sean McS Something 22/03/13

-2

Hừm ... đây thực sự là một câu hỏi khó trả lời 'tích cực', bởi vì khi tôi nghĩ về sự đơn giản trong thiết kế ngôn ngữ lập trình (và những gì 'dễ dàng hơn' để làm việc), tôi nghĩ ngay đến:

  1. "Khả năng đọc" của mã - ngôn ngữ đóng gói các đối tượng, phương thức, v.v.
  2. Tính nhất quán của các API ngôn ngữ và giao diện.

Có một số ngôn ngữ làm tốt những điều này - nhưng thứ xuất hiện trong đầu tôi là một ngôn ngữ không làm tốt mọi thứ 'như một ngôn ngữ lập trình': PHP.

[Tuyên bố miễn trừ trách nhiệm - Tôi đã viết PHP và PHP vẫn là 'ngôn ngữ' của tôi cho các dự án web đơn giản. Đây là một lời chỉ trích về tình yêu ...]

Đầu tiên, PHP làm tốt cho môi trường mà nó thường chạy trong - các máy chủ web. Dễ cài đặt, dễ bảo trì, v.v.

Nơi tôi đấu tranh với PHP: khi bạn muốn làm điều gì đó "khác thường" - điều mà bạn thường không làm thường xuyên (trong trường hợp tôi nghĩ rằng nó đang sử dụng dịch vụ web SOAP - không phải là điều tôi đã đã làm rất nhiều với PHP), bạn phải đối mặt với hai tùy chọn:

1) Các phần mở rộng nguồn mở khác nhau cho PHP. Mô hình đối tượng PHP đủ lỏng lẻo trong đó thiết kế giao diện cũng không nhất quán khủng khiếp từ thư viện đến thư viện, nhà phát triển đến nhà phát triển. Khi đối mặt với một bộ mã nơi ai đó sử dụng một thư viện mà bạn chưa từng nghe đến, bạn đã dành rất nhiều thời gian để tìm ra "cái quái gì thế này" bởi vì ngôn ngữ cho phép tạo API / thư viện lỏng lẻo.

2) Các chức năng "tích hợp" - trong đó có rất nhiều . Tôi đã trải qua một vài lỗ thỏ để tìm thư viện khác hoặc thực hiện một chút chức năng để bằng cách nào đó sau này vấp ngãimpossiblyfound_basefunction()

Theo tôi, sự đơn giản là sự nhất quán.


-3

Bây giờ, cách tiếp cận KISS đối với các ngôn ngữ lập trình là một khái niệm buồn cười, ít nhất là nếu bạn coi ngôn ngữ lập trình là một lớp trừu tượng cho tập lệnh của CPU, v.v. Nếu bạn không định nghĩa "KISS" chặt chẽ hơn. Tôi chỉ nói ở đây, rằng một chiếc xe bò được KISS áp dụng cho chiếc xe một cách đầy đủ nhất.

Bây giờ một cách giải thích khác về KISS có thể là một cái gì đó như "được thực hiện một cách thông minh mà không cần đồ trang trí và không quá phức tạp". Đặc biệt người ta có thể lập luận rằng, không nên có quá nhiều trường hợp cụ thể cho những điều tương tự, v.v. Thông thường toán học khá tốt trong việc làm sôi sục mọi thứ theo bản chất của nó, và - tự hỏi - các nhà toán học cũng đã dành thời gian suy nghĩ về lập trình và máy tính. .

Đối với lập trình, tồn tại 2 mô hình trừu tượng khá nổi tiếng:

  • Một là máy turing xác định một máy và một mô hình hướng dẫn đơn giản có thể tính toán mọi thứ mà máy tính có thể làm.
  • Một cái khác là Lambda-Tính của Church et. al. đó là sức mạnh tương đương

Điều thú vị là: Mặc dù máy turing khá đơn giản trong cách bố trí, nhưng nó không phải là một hệ thống dễ xử lý và tôi không nghĩ rằng nó đủ điều kiện cho "KISS thông minh". Nhưng Lambda Tính có liên quan nhiều hơn đến các ngôn ngữ lập trình mà chúng ta biết - và với các tính năng tiên phong của Lisp và Scheme của phép tính lambda, nó đã đi vào rất nhiều ngôn ngữ.

Lisp và Scheme thực sự đơn giản, ít nhất là cú pháp khôn ngoan. Cú pháp là một vấn đề lớn với các ngôn ngữ lập trình (có lẽ là lý do tại sao chúng được phát minh lại mọi lúc). Trong trường hợp của C ++, bộ não con người gần như không thể dự đoán được cách một số dòng nguồn được trình biên dịch diễn giải.)

Lisps đang giảm độ phức tạp cú pháp hoàn toàn bằng cách giới thiệu một hình thức phổ biến cho các lệnh:

(command param1 param2 ...)

Đây có thể là một cuộc gọi phương thức, chẳng hạn như

(max 1 2 3 4)

cũng như một nhánh if, vòng lặp, v.v.

(if (< 1 2) 
    (write 4)
    (write 5))

(tất cả mã ở đây là giả thuyết Lisp / Phương ngữ bất khả tri)

Hình thức

(command param1 param2 ...)

cũng có thể được hiểu là một danh sách

(item1 item2 item3)

Và đó là cơ sở cho sự đơn giản và vẻ đẹp của Lisps. Bởi vì các danh sách lồng nhau (như trong ifví dụ tuyên bố) tạo thành cây và những cái đó có thể dễ dàng hiểu được cả bằng máy và bởi con người trước máy.

Một tính năng khác của Lisps là các macro tôi sẽ không đi sâu vào chi tiết bẩn ở đây, nhưng vì không có sự khác biệt về cú pháp giữa các lệnh gọi hàm thông thường và "Cú pháp (trứng cho các vòng lặp, khai báo biến, v.v.", mọi thứ mà trình phân tích cú pháp phải xử lý khác ngôn ngữ lập trình) bạn có thể tạo cú pháp của riêng mình. Macros về bản chất là Thao tác của cây cấu thành chương trình của bạn.

Tôi nghĩ Lisp có một KISS để lập trình. Việc bạn có thể thao tác cú pháp bằng cách sử dụng macro đã dẫn đến một hiện tượng mà Lisp đã phát triển khá linh hoạt. Cần một tính năng Ngôn ngữ mới như - giả sử hướng đối tượng - chỉ cần viết hệ thống OOP bằng macro!

Trong khi C được mở rộng vòng 2 lần với các tính năng OOP (C ++, Obj. C) Lisp đã được mở rộng nhiều lần, cuối cùng đã có người chiến thắng.

Đó là một cách giải thích khác về Lisp, nó phát triển (xem Clojure và Clojurescript để biết về Đột biến mới thú vị).

Đối với các thuộc tính KISS của nó, Lisps được ưa chuộng như một số ngôn ngữ giảng dạy. Giống như Fogus phác thảo trong bản thiết kế ngôn ngữ giáo dục của mình ( http://blog.fogus.me/2013/01/21/enfield-a-programming-lingu-design-for-pedagogy/ )

Để bắt đầu, tôi tin tưởng mạnh mẽ rằng khi học mới, và đôi khi các chủ đề phức tạp sẽ gây bất lợi cho học sinh khi quá tải các quy tắc cú pháp phù phiếm. Do đó, Enfield được thiết kế với các quy tắc cú pháp tối thiểu và tối thiểu hơn cú pháp giống Lisp.

2
OP định nghĩa KISS cho bối cảnh của câu hỏi này khá rõ ràng, "cho phép một lập trình viên trung bình trong điều kiện làm việc trung bình viết và duy trì càng nhiều mã càng tốt với nỗ lực nhận thức ít nhất" . OP cũng đề cập đến "quan tâm tìm hiểu về ý định (tài liệu) của các nhà thiết kế hơn là ý kiến ​​cá nhân của bạn về một ngôn ngữ lập trình cụ thể"
gnat 22/03/13
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.