Trả lại coi có hại? Mã có thể được chức năng mà không có nó?


45

OK, vì vậy, tiêu đề là một chút clickbaity nhưng nghiêm túc tôi đã nói, đừng hỏi đá một lúc. Tôi thích cách nó khuyến khích các phương thức được sử dụng làm thông điệp theo kiểu hướng đối tượng thực sự. Nhưng điều này có một vấn đề dai dẳng đã được giải quyết trong đầu tôi.

Tôi đã nghi ngờ rằng mã được viết tốt có thể tuân theo các nguyên tắc OO và nguyên tắc chức năng cùng một lúc. Tôi đang cố gắng dung hòa những ý tưởng này và điểm hấp dẫn lớn mà tôi đã đạt được là return.

Một hàm thuần túy có hai phẩm chất:

  1. Gọi nó nhiều lần với cùng một đầu vào luôn cho kết quả giống nhau. Điều này ngụ ý rằng nó là bất biến. Trạng thái của nó chỉ được đặt một lần.

  2. Nó không tạo ra tác dụng phụ. Sự thay đổi duy nhất gây ra bằng cách gọi nó là tạo ra kết quả.

Vì vậy, làm thế nào để một người hoàn toàn hoạt động nếu bạn tuyên thệ sử dụng returnnhư cách bạn truyền đạt kết quả?

Các nói, đừng hỏi tác phẩm ý tưởng bằng cách sử dụng những gì một số sẽ xem xét một tác dụng phụ. Khi tôi làm việc với một đối tượng, tôi không hỏi nó về trạng thái bên trong của nó. Tôi nói với nó những gì tôi cần phải làm và nó sử dụng trạng thái bên trong của nó để tìm ra những việc cần làm với những gì tôi đã bảo nó làm. Khi tôi nói với nó, tôi không hỏi nó đã làm gì. Tôi chỉ mong nó đã làm một cái gì đó về những gì nó được bảo phải làm.

Tôi nghĩ về Tell, Đừng hỏi nhiều hơn là một tên khác để đóng gói. Khi tôi sử dụng returntôi không có ý tưởng gì gọi tôi. Tôi không thể nói đó là giao thức, tôi phải buộc nó xử lý giao thức của mình. Mà trong nhiều trường hợp được thể hiện là trạng thái nội bộ. Ngay cả khi những gì được phơi bày không chính xác trạng thái, nó thường chỉ là một số tính toán được thực hiện trên trạng thái và đối số đầu vào. Có một giao diện để đáp ứng thông qua cơ hội để đưa các kết quả vào một cái gì đó có ý nghĩa hơn trạng thái nội bộ hoặc tính toán. Đó là thông điệp truyền qua . Xem ví dụ này .

Quay trở lại vào ban ngày, khi các ổ đĩa thực sự có ổ đĩa trong đó và một ổ ngón tay cái là những gì bạn đã làm trong xe khi bánh xe quá lạnh để chạm vào ngón tay của bạn, tôi được dạy cách mọi người khó chịu khi xem xét các chức năng có thông số. void swap(int *first, int *second)Có vẻ rất tiện dụng nhưng chúng tôi được khuyến khích viết các hàm trả về kết quả. Vì vậy, tôi lấy điều này để thuộc về đức tin và bắt đầu theo nó.

Nhưng bây giờ tôi thấy mọi người xây dựng kiến trúc nơi các đối tượng cho phép cách họ được xây dựng kiểm soát nơi họ gửi kết quả của họ. Đây là một ví dụ thực hiện . Việc tiêm đối tượng cổng đầu ra có vẻ hơi giống với ý tưởng tham số out một lần nữa. Nhưng đó là cách các đối tượng không được hỏi nói với các đối tượng khác những gì họ đã làm.

Khi tôi lần đầu tiên biết về tác dụng phụ, tôi nghĩ về nó giống như tham số đầu ra. Chúng tôi đã được khuyên đừng làm mọi người ngạc nhiên khi có một số công việc xảy ra theo một cách đáng ngạc nhiên, đó là bằng cách không tuân theo return resultquy ước. Bây giờ chắc chắn, tôi biết có một đống vấn đề phân luồng không đồng bộ song song mà tác dụng phụ gây ra nhưng trả lại thực sự chỉ là một quy ước khiến bạn bỏ kết quả lên ngăn xếp để bất cứ điều gì được gọi là bạn có thể tắt nó sau. Đó là tất cả những gì nó thực sự là.

Điều tôi thực sự đang cố gắng hỏi:

Có phải là returncách duy nhất để tránh tất cả sự khốn khổ của tác dụng phụ đó và nhận được sự an toàn của luồng mà không cần khóa, v.v. Hoặc tôi có thể làm theo , đừng hỏi theo cách hoàn toàn có chức năng?


3
Nếu bạn chọn không bỏ qua Phân tách truy vấn lệnh, bạn sẽ xem xét vấn đề của mình đã được giải quyết chưa?
rwong

30
Hãy xem xét rằng việc tự tìm kiếm một cú đá có thể là một dấu hiệu cho thấy bạn đang tham gia vào thiết kế theo hướng giáo điều thay vì lý giải những ưu và nhược điểm của từng tình huống cụ thể.
Blrfl

3
Nói chung khi mọi người nói về "trở lại" là có hại, họ nói rằng nó chống lại lập trình có cấu trúc, không có chức năng và một tuyên bố trả lại duy nhất ở cuối thói quen (và có thể ở cuối cả hai mặt của một khối if / khác chính nó là yếu tố cuối cùng) không được bao gồm trong đó.
Random832

16
@jameslarge: Phân đôi giả. Không cho phép bản thân bị điều khiển bởi các thiết kế bởi suy nghĩ giáo điều không giống với Wild West / mọi thứ tiếp cận mà bạn đang nói đến. Vấn đề là không cho phép giáo điều xâm nhập vào mã tốt, đơn giản, rõ ràng. Luật phổ quát là dành cho thiếu; bối cảnh là dành cho các vị vua.
Nicol Bolas

4
Có một điều đối với tôi không rõ ràng trong câu hỏi của bạn: bạn nói rằng bạn đã tuyên thệ sử dụng 'return', nhưng theo như tôi có thể nói với bạn là không liên quan rõ ràng đến các khái niệm khác của bạn hoặc nói lý do tại sao bạn đã làm điều này. Khi kết hợp với định nghĩa của hàm thuần túy bao gồm tạo kết quả, bạn tạo ra thứ gì đó không thể giải quyết.
Danikov

Câu trả lời:


96

Nếu một chức năng không có bất kỳ tác dụng phụ nào và nó không trả lại bất cứ điều gì, thì chức năng đó là vô ích. Nó đơn giản như vậy.

Nhưng tôi đoán bạn có thể sử dụng một số cheats nếu bạn muốn làm theo các thư của các quy tắc và bỏ qua những lý luận cơ bản. Ví dụ, sử dụng tham số out nói đúng là không sử dụng trả về. Nhưng nó vẫn thực hiện chính xác như một sự trở lại, chỉ là theo một cách phức tạp hơn. Vì vậy, nếu bạn tin rằng lợi nhuận là xấu vì một lý do , thì việc sử dụng tham số out rõ ràng là xấu vì những lý do cơ bản tương tự.

Bạn có thể sử dụng nhiều mánh khóe hơn. Ví dụ, Haskell nổi tiếng với thủ thuật đơn nguyên IO, nơi bạn có thể có tác dụng phụ trong thực tế, nhưng vẫn không nói đúng có tác dụng phụ theo quan điểm lý thuyết. Phong cách tiếp tục là một thủ thuật khác, nó cũng cho phép bạn tránh trả lại với giá biến mã của bạn thành spaghetti.

Điểm mấu chốt là, các thủ thuật ngớ ngẩn vắng mặt, hai nguyên tắc của các chức năng miễn phí tác dụng phụ và "không trả lại" chỉ đơn giản là không tương thích. Hơn nữa, tôi sẽ chỉ ra cả hai đều là những nguyên tắc thực sự tồi tệ (giáo điều thực sự) ngay từ đầu, nhưng đó là một cuộc thảo luận khác.

Các quy tắc như "nói, đừng hỏi" hoặc "không có tác dụng phụ" không thể được áp dụng phổ biến. Bạn luôn phải xem xét bối cảnh. Một chương trình không có tác dụng phụ theo nghĩa đen là vô dụng. Ngay cả các ngôn ngữ chức năng thuần túy cũng thừa nhận rằng. Thay vào đó, họ cố gắng tách các phần thuần túy của mã khỏi các phần có tác dụng phụ. Điểm của các đơn vị Bang hoặc IO trong Haskell không phải là bạn tránh các tác dụng phụ - bởi vì bạn không thể - mà là sự hiện diện của các tác dụng phụ được biểu thị rõ ràng bằng chữ ký hàm.

Quy tắc nói không hỏi áp dụng cho một loại kiến ​​trúc khác - kiểu mà các đối tượng trong chương trình là các "tác nhân" độc lập giao tiếp với nhau. Mỗi diễn viên về cơ bản là tự chủ và đóng gói. Bạn có thể gửi tin nhắn và nó quyết định cách phản ứng với nó, nhưng bạn không thể kiểm tra trạng thái bên trong của diễn viên từ bên ngoài. Điều này có nghĩa là bạn không thể biết nếu một tin nhắn thay đổi trạng thái bên trong của tác nhân / đối tượng. Các tác dụng phụ và trạng thái được ẩn theo thiết kế .


20
@CandiedOrange: Liệu một phương thức có tác dụng phụ trực tiếp hay gián tiếp mặc dù nhiều lớp cuộc gọi không thay đổi bất cứ điều gì về mặt khái niệm. Nó vẫn là một tác dụng phụ. Nhưng nếu vấn đề là tác dụng phụ chỉ xảy ra thông qua các đối tượng được tiêm để bạn kiểm soát loại tác dụng phụ nào có thể xảy ra, thì điều này nghe có vẻ là một thiết kế tốt. Nó chỉ là không có tác dụng phụ miễn phí. Nó là OO tốt nhưng nó không hoàn toàn là chức năng.
JacquesB

12
@LightnessRacesinOrbit: grep ở chế độ im lặng có mã trả về quy trình sẽ được sử dụng. Nếu nó không có thứ đó, thì nó thực sự sẽ vô dụng
unperson325680

10
@progo: Mã trả về không phải là tác dụng phụ; đó là các hiệu ứng. Bất cứ điều gì chúng ta gọi là tác dụng phụ đều được gọi là hiệu ứng phụ vì đó không phải là mã trả về;)
Cuộc đua Lightness với Monica

8
@Cubic đó là điểm chính. Một chương trình không có tác dụng phụ là vô ích.
Jens Schauder

5
@mathreadler Đó là một tác dụng phụ :)
Andres F.

32

Nói, Đừng hỏi đi kèm với một số giả định cơ bản:

  1. Bạn đang sử dụng đồ vật.
  2. Đối tượng của bạn có trạng thái.
  3. Trạng thái của các đối tượng của bạn ảnh hưởng đến hành vi của họ.

Không có những điều này áp dụng cho các chức năng thuần túy.

Vì vậy, hãy xem lại lý do tại sao chúng ta có quy tắc "Nói, đừng hỏi." Quy tắc này là một cảnh báo và nhắc nhở. Nó có thể được tóm tắt như thế này:

Cho phép lớp của bạn quản lý trạng thái của chính nó. Đừng hỏi nó về trạng thái của nó, và sau đó hành động dựa trên trạng thái đó. Nói với lớp những gì bạn muốn, và để nó quyết định những gì cần làm dựa trên trạng thái của chính nó.

Nói cách khác, các lớp chỉ chịu trách nhiệm duy trì trạng thái của chính họ và hành động theo nó. Đây là những gì đóng gói là tất cả về.

Từ Fowler :

Nói-Đừng-Hỏi là một nguyên tắc giúp mọi người nhớ rằng hướng đối tượng là về việc kết hợp dữ liệu với các chức năng hoạt động trên dữ liệu đó. Nó nhắc nhở chúng ta rằng thay vì yêu cầu một đối tượng cho dữ liệu và hành động trên dữ liệu đó, thay vào đó chúng ta nên nói với một đối tượng phải làm gì. Điều này khuyến khích chúng ta di chuyển hành vi vào một đối tượng để đi cùng với dữ liệu.

Để nhắc lại, không ai trong số này có liên quan gì đến các hàm thuần túy, hoặc thậm chí là không tinh khiết trừ khi bạn phơi bày trạng thái của một lớp với thế giới bên ngoài. Ví dụ:

Vi phạm TDA

var color = trafficLight.Color;
var elapsed = trafficLight.Elapsed;
If (color == Color.Red && elapsed > 2.Minutes)
    trafficLight.ChangeColor(green);

Không phải là vi phạm TDA

var result = trafficLight.ChangeColor(Color.Green);

hoặc là

var result = await trafficLight.ChangeColorWhenReady(Color.Green);     

Trong cả hai ví dụ sau, đèn giao thông vẫn giữ quyền kiểm soát trạng thái và hành động của nó.


Đợi một chút, đóng cửa có thể là tinh khiết. họ có nhà nước (họ gọi nó là phạm vi từ vựng). và phạm vi từ vựng đó có thể ảnh hưởng đến hành vi của họ. Bạn có chắc chắn TDA chỉ liên quan khi các đối tượng có liên quan?
candied_orange

7
Đóng cửa @CandiedOrange chỉ thuần túy nếu bạn không sửa đổi các ràng buộc đóng. Nếu không, bạn mất tính minh bạch tham chiếu khi gọi hàm được trả về từ bao đóng.
Jared Smith

1
@JaredSmith và không giống nhau khi bạn nói về đồ vật? Đây không phải là vấn đề bất biến sao?
candied_orange

1
@bdsl - Và bây giờ bạn đã vô tình chỉ ra chính xác mọi cuộc thảo luận về loại này đi với ví dụ về TrafficLight.refreshDisplay của bạn. Nếu bạn tuân theo các quy tắc, bạn sẽ dẫn đến một hệ thống rất linh hoạt mà không ai ngoài người viết mã gốc hiểu được. Tôi thậm chí đã cá rằng sau một vài năm gián đoạn, ngay cả người viết mã gốc cũng có thể không hiểu họ đã làm gì.
Dunk

1
"Ngớ ngẩn", như trong "Không mở các vật thể khác và không nhìn vào ruột của chúng mà thay vào đó hãy đổ hết ruột của bạn (nếu bạn có) vào các phương pháp của các vật thể khác; đó là Cách".
Joker_vD

30

Khi tôi làm việc với một đối tượng, tôi không hỏi nó về trạng thái bên trong của nó. Tôi nói với nó những gì tôi cần phải làm và nó sử dụng trạng thái bên trong của nó để tìm ra những việc cần làm với những gì tôi đã bảo nó làm.

Bạn không chỉ yêu cầu trạng thái nội bộ của nó , bạn cũng không hỏi liệu nó có trạng thái nội bộ nào không.

Cũng nói, đừng hỏi! không không có nghĩa không nhận được một kết quả trong hình thức của một giá trị trả về (được cung cấp bởi một returntuyên bố bên trong phương pháp này). Nó chỉ ngụ ý rằng tôi không quan tâm đến cách bạn làm điều đó, nhưng hãy xử lý nó! . Và đôi khi bạn ngay lập tức muốn kết quả xử lý ...


1
CQS mặc dù sẽ ngụ ý rằng việc sửa đổi trạng thái và nhận kết quả nên được tách ra
jk.

7
@jk. Như thường lệ: nói chung, bạn nên tách riêng thay đổi trạng tháitrả về kết quả nhưng trong những trường hợp hiếm hoi có những lý do hợp lệ để kết hợp điều đó. Ví dụ: một next()phương thức lặp không chỉ trả về đối tượng hiện tại mà còn thay đổi trạng thái bên trong của trình lặp để cuộc gọi tiếp theo trả về đối tượng tiếp theo ...
Timothy Truckle

4
Chính xác. Tôi nghĩ vấn đề của OP chỉ đơn giản là do sự hiểu lầm / hiểu sai về cách nói, đừng hỏi điều đó. Và sửa chữa sự hiểu lầm đó làm cho vấn đề biến mất.
Konrad Rudolph

@KonradRudolph Plus, tôi không nghĩ đó là sự hiểu lầm duy nhất ở đây. Mô tả của họ về "hàm thuần túy" bao gồm "Trạng thái của nó chỉ được đặt một lần." Một nhận xét khác cho thấy nó có thể có nghĩa là bối cảnh của một đóng cửa, nhưng cụm từ nghe có vẻ kỳ lạ đối với tôi.
Izkata

17

Nếu bạn coi returnlà "có hại" (để ở trong ảnh của bạn), thì thay vì thực hiện một chức năng như

ResultType f(InputType inputValue)
{
     // ...
     return result;
}

xây dựng nó theo cách truyền thông điệp:

void f(InputType inputValue, Action<ResultType> g)
{
     // ...
     g(result);
}

Miễn là fgkhông có tác dụng phụ, việc kết nối chúng lại với nhau cũng sẽ không có tác dụng phụ. Tôi nghĩ phong cách này tương tự như cái còn được gọi là phong cách Tiếp tục .

Nếu điều này thực sự dẫn đến các chương trình "tốt hơn" là điều gây tranh cãi, vì nó phá vỡ một số quy ước. Kỹ sư phần mềm người Đức Ralf Hampal đã tạo ra một mô hình lập trình toàn bộ xung quanh vấn đề này, ông gọi nó là "Các thành phần dựa trên sự kiện" với một kỹ thuật mô hình mà ông gọi là "Thiết kế dòng chảy".

Để xem một số ví dụ, hãy bắt đầu trong phần "Dịch cho các sự kiện" của mục blog này . Đối với cách tiếp cận đầy đủ, tôi giới thiệu cuốn sách điện tử của mình "Nhắn tin như một mô hình lập trình - Thực hiện OOP như thể bạn muốn nói" .


24
If this really leads to "better" programs is debatableChúng ta chỉ phải xem mã được viết bằng JavaScript trong thập kỷ đầu tiên của thế kỷ này. Jquery và các plugin của nó có xu hướng gọi lại mô hình này ... cuộc gọi lại ở khắp mọi nơi . Tại một thời điểm nhất định, quá nhiều cuộc gọi lại lồng nhau , khiến việc gỡ lỗi trở thành một cơn ác mộng. Mã vẫn phải được đọc bởi con người bất kể độ lệch tâm của công nghệ phần mềm và "nguyên tắc" của nó
Laiv

1
thậm chí sau đó tại một số điểm bạn cần cung cấp một hành động thực hiện tác dụng phụ hoặc bạn cần một số cách để thoát khỏi phần CPS
jk.

12
@Laiv CPS được phát minh như một công nghệ tối ưu hóa cho trình biên dịch, không ai thực sự mong đợi các lập trình viên viết mã theo cách này.
Joker_vD

3
@CandiedOrange Ở dạng khẩu hiệu, "trả lại chỉ là nói tiếp tục phải làm gì". Thật vậy, việc tạo ra Scheme đã được thúc đẩy bằng cách cố gắng tìm hiểu mô hình Diễn viên của Hewitt và đi đến kết luận rằng các diễn viên và đóng cửa là điều tương tự.
Derek Elkins

2
Theo giả thuyết, chắc chắn, bạn có thể viết toàn bộ ứng dụng của mình dưới dạng một loạt các lệnh gọi hàm không trả về bất cứ thứ gì. Nếu bạn không nhớ nó được kết nối chặt chẽ, bạn thậm chí không cần các cổng. Nhưng hầu hết các ứng dụng lành mạnh sử dụng các chức năng trả lại mọi thứ, bởi vì ... tốt, chúng là lành mạnh. Và như tôi tin rằng tôi đã thể hiện đầy đủ trong câu trả lời của mình, bạn có thể returndữ liệu từ các chức năng và vẫn tuân thủ Tell Don't Ask, miễn là bạn không chỉ huy trạng thái của đối tượng.
Robert Harvey

7

Thông điệp truyền đi vốn có hiệu quả. Nếu bạn bảo một đối tượng làm điều gì đó, bạn mong đợi nó có ảnh hưởng đến điều gì đó. Nếu trình xử lý tin nhắn là thuần túy, bạn sẽ không cần gửi tin nhắn.

Trong các hệ thống diễn viên phân tán, kết quả của một hoạt động thường được gửi dưới dạng tin nhắn lại cho người gửi yêu cầu ban đầu. Người gửi tin nhắn hoặc được cung cấp ngầm định bởi thời gian chạy của tác nhân hoặc nó (theo quy ước) được truyền một cách rõ ràng như là một phần của tin nhắn. Trong truyền tin nhắn đồng bộ, một phản hồi giống như một returncâu lệnh. Trong truyền tin nhắn không đồng bộ, sử dụng tin nhắn phản hồi đặc biệt hữu ích vì nó cho phép xử lý đồng thời trong nhiều tác nhân trong khi vẫn cung cấp kết quả.

Truyền "người gửi" mà kết quả sẽ được gửi một cách rõ ràng về cơ bản mô hình kiểu truyền tiếp tục hoặc các tham số đáng sợ - ngoại trừ việc nó gửi tin nhắn cho họ thay vì trực tiếp thay đổi chúng.


5

Toàn bộ câu hỏi này gây ấn tượng với tôi như là một 'vi phạm cấp độ'.

Bạn có (ít nhất) các cấp độ sau trong một dự án lớn:

  • Cấp độ hệ thống, ví dụ như nền tảng thương mại điện tử
  • Cấp độ hệ thống phụ, ví dụ: xác thực người dùng: máy chủ, AD, giao diện người dùng
  • Cấp độ chương trình cá nhân, ví dụ như một trong các thành phần ở trên
  • Cấp độ Diễn viên / Mô-đun [điều này trở nên âm u tùy theo ngôn ngữ]
  • Mức phương thức / hàm.

Và như vậy xuống các mã thông báo cá nhân.

Thực sự không cần bất kỳ thực thể nào ở cấp phương thức / hàm không trả về (ngay cả khi nó chỉ trả về this). Và không có (trong mô tả của bạn) bất kỳ nhu cầu nào về một thực thể ở cấp Diễn viên để trả lại bất cứ điều gì (tùy thuộc vào ngôn ngữ thậm chí có thể không khả thi). Tôi nghĩ rằng sự nhầm lẫn là trong việc kết hợp hai cấp độ đó, và tôi sẽ lập luận rằng chúng nên được lý luận về sự khác biệt (ngay cả khi bất kỳ đối tượng cụ thể nào thực sự kéo dài nhiều cấp độ).


2

Bạn đề cập rằng bạn muốn tuân thủ cả nguyên tắc OOP là "nói, không hỏi" và nguyên tắc chức năng của các hàm thuần túy, nhưng tôi hoàn toàn không thấy điều đó dẫn đến việc bạn tránh khỏi tuyên bố trả về.

Một cách khác tương đối phổ biến để tuân theo cả hai nguyên tắc này là sử dụng tất cả các câu lệnh hoàn trả và chỉ sử dụng các đối tượng bất biến với getters. Cách tiếp cận sau đó là để có một số getters trả về một đối tượng tương tự với trạng thái mới, trái ngược với việc thay đổi trạng thái của đối tượng ban đầu.

Một ví dụ về cách tiếp cận này là trong các kiểu dữ liệu tuplevà nội dung của Python frozenset. Đây là một cách sử dụng điển hình của một froundredet:

small_digits = frozenset([0, 1, 2, 3, 4])
big_digits = frozenset([5, 6, 7, 8, 9])
all_digits = small_digits.union(big_digits)

print("small:", small_digits)
print("big:", big_digits)
print("all:", all_digits)

Điều này sẽ in như sau, chứng minh rằng phương thức hợp nhất tạo ra một bộ khung mới với trạng thái riêng mà không ảnh hưởng đến các đối tượng cũ:

nhỏ: froundredet ({0, 1, 2, 3, 4})

lớn: froundredet ({5, 6, 7, 8, 9})

tất cả: froundredet ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

Một ví dụ khác về các cấu trúc dữ liệu bất biến tương tự là thư viện Immutable.js của Facebook . Trong cả hai trường hợp, bạn bắt đầu với các khối xây dựng này và có thể xây dựng các đối tượng miền cấp cao hơn theo cùng các nguyên tắc, đạt được cách tiếp cận OOP chức năng, giúp bạn đóng gói dữ liệu và lý do về nó dễ dàng hơn. Và tính bất biến cũng cho phép bạn gặt hái lợi ích của việc có thể chia sẻ các đối tượng như vậy giữa các luồng mà không phải lo lắng về khóa.


1

Tôi đã nghi ngờ rằng mã được viết tốt có thể tuân theo các nguyên tắc OO và nguyên tắc chức năng cùng một lúc. Tôi đang cố gắng dung hòa những ý tưởng này và điểm hấp dẫn lớn mà tôi đã đạt được là sự trở lại.

Tôi đã cố gắng hết sức để điều hòa một số lợi ích của, cụ thể hơn là lập trình chức năng và bắt buộc (tự nhiên không nhận được tất cả lợi ích nào, nhưng cố gắng để có được phần của sư tử), mặc dù returnthực sự là cơ bản để thực hiện điều đó một thời trang đơn giản cho tôi trong nhiều trường hợp.

Liên quan đến việc cố gắng tránh các returntuyên bố hoàn toàn, tôi đã cố gắng nghiên cứu vấn đề này trong khoảng một giờ qua và về cơ bản, chồng chất lên não tôi nhiều lần. Tôi có thể thấy sự hấp dẫn của nó trong việc thực thi mức độ đóng gói và thông tin mạnh mẽ nhất có lợi cho các đối tượng rất tự trị chỉ đơn thuần là nói phải làm gì và tôi thích khám phá những điểm cực đoan của ý tưởng nếu chỉ cố gắng cải thiện hiểu về cách họ làm việc.

Nếu chúng ta sử dụng ví dụ về đèn giao thông, thì ngay lập tức một nỗ lực ngây thơ sẽ muốn cung cấp kiến ​​thức về đèn giao thông như vậy về toàn bộ thế giới xung quanh nó, và điều đó chắc chắn sẽ không mong muốn từ góc độ khớp nối. Vì vậy, nếu tôi hiểu chính xác thì bạn trừu tượng hóa và tách rời để ủng hộ việc khái quát các cổng I / O để truyền bá thêm thông điệp và yêu cầu, không phải dữ liệu, thông qua đường ống và về cơ bản tiêm các đối tượng này với các tương tác / yêu cầu mong muốn lẫn nhau Trong khi lãng quên nhau.

Đường ống nút

nhập mô tả hình ảnh ở đây

Và sơ đồ đó là về chừng nào tôi đã cố gắng phác thảo điều này (và trong khi đơn giản, tôi phải tiếp tục thay đổi nó và suy nghĩ lại về nó). Ngay lập tức tôi có xu hướng nghĩ rằng một thiết kế với mức độ tách rời và trừu tượng này sẽ trở nên rất khó để lý giải về hình thức mã, bởi vì (các) nhà soạn nhạc kết nối tất cả những điều này cho một thế giới phức tạp có thể rất khó để theo dõi tất cả các tương tác và yêu cầu này để tạo ra đường ống mong muốn. Tuy nhiên, ở dạng trực quan, có thể rất đơn giản khi chỉ vẽ những thứ này ra dưới dạng biểu đồ và liên kết mọi thứ lên và xem mọi thứ diễn ra tương tác.

Về mặt tác dụng phụ, tôi có thể thấy điều này không có "tác dụng phụ" theo nghĩa là các yêu cầu này có thể, trên ngăn xếp cuộc gọi, dẫn đến một chuỗi lệnh cho mỗi luồng thực hiện, ví dụ: (Tôi không tính điều này như là một "tác dụng phụ" theo nghĩa thực dụng vì nó không làm thay đổi bất kỳ trạng thái nào có liên quan đến thế giới bên ngoài cho đến khi các lệnh như vậy thực sự được thực hiện - mục tiêu thực tế đối với tôi trong hầu hết các phần mềm không phải là loại bỏ tác dụng phụ mà là trì hoãn và tập trung chúng) . Và hơn nữa, việc thực thi lệnh có thể tạo ra một thế giới mới trái ngược với việc làm biến đổi thế giới hiện có. Bộ não của tôi thực sự bị đánh thuế chỉ khi cố gắng hiểu tất cả những điều này, tuy nhiên, không có bất kỳ nỗ lực nào trong việc tạo ra các ý tưởng này. Tôi cũng không

Làm thế nào nó hoạt động

Vì vậy, để làm rõ tôi đã tưởng tượng làm thế nào bạn thực sự lập trình này. Cách tôi nhìn thấy nó hoạt động thực ra là sơ đồ ở trên nắm bắt quy trình công việc của người dùng (lập trình viên). Bạn có thể kéo đèn giao thông vào thế giới, kéo đồng hồ bấm giờ, cho thời gian trôi qua (khi "xây dựng" nó). Đồng hồ bấm giờ có một On Intervalsự kiện (cổng đầu ra), bạn có thể kết nối nó với đèn giao thông để trên các sự kiện đó, nó báo cho ánh sáng chuyển qua màu sắc của nó.

Sau đó, đèn giao thông có thể chuyển sang một số màu nhất định, phát ra các kết quả (sự kiện) như, On Redtại thời điểm đó chúng ta có thể kéo một người đi bộ vào thế giới của chúng ta và làm cho sự kiện đó nói với người đi bộ bắt đầu đi bộ ... hoặc chúng ta có thể kéo chim vào Cảnh của chúng ta và làm cho nó trở nên như vậy khi ánh sáng chuyển sang màu đỏ, chúng ta bảo chim bắt đầu bay và vỗ cánh ... hoặc có thể khi ánh sáng chuyển sang màu đỏ, chúng ta bảo một quả bom phát nổ - bất cứ điều gì chúng ta muốn, và với các vật thể hoàn toàn không biết gì về nhau, và không làm gì ngoài việc gián tiếp nói cho nhau biết phải làm gì thông qua khái niệm đầu vào / đầu ra trừu tượng này.

Và họ gói gọn hoàn toàn trạng thái của mình và không tiết lộ gì về điều đó (trừ khi những "sự kiện" này được coi là TMI, tại thời điểm đó tôi phải suy nghĩ lại nhiều thứ), họ nói với nhau những điều cần làm gián tiếp, họ không hỏi. Và họ đang tách ra. Không có gì biết về bất cứ điều gì ngoại trừ sự trừu tượng cổng đầu vào / đầu ra tổng quát này.

Các trường hợp sử dụng thực tế?

Tôi có thể thấy loại điều này hữu ích như một ngôn ngữ nhúng dành riêng cho tên miền cấp cao trong một số miền nhất định để phối hợp tất cả các đối tượng tự trị này không biết gì về thế giới xung quanh, không tiết lộ gì về xây dựng bài đăng trạng thái nội bộ của chúng và về cơ bản chỉ truyền bá các yêu cầu giữa nhau mà chúng ta có thể thay đổi và điều chỉnh theo nội dung của trái tim mình. Hiện tại tôi cảm thấy như thế này rất đặc trưng cho miền, hoặc có lẽ tôi chưa nghĩ đủ về nó, bởi vì tôi rất khó khăn trong việc bọc bộ não của mình với những thứ tôi thường xuyên phát triển (tôi thường làm việc với mã cấp trung bình khá thấp) nếu tôi diễn giải Tell, Đừng hỏi những điểm cực đoan như vậy và muốn mức độ đóng gói mạnh nhất có thể tưởng tượng được. Nhưng nếu chúng tôi làm việc với các bản tóm tắt cấp cao trong một miền cụ thể,

Tín hiệu và Slots

Thiết kế này trông có vẻ kỳ lạ đối với tôi cho đến khi tôi nhận ra về cơ bản là tín hiệu và khe cắm nếu chúng ta không tính đến nhiều sắc thái của cách nó được triển khai. Câu hỏi chính đối với tôi là làm thế nào chúng ta có thể lập trình các nút (đối tượng) riêng lẻ này trong biểu đồ một cách hiệu quả như thế nào khi tuân thủ nghiêm ngặt Nói, Đừng hỏi, đưa ra mức độ tránh các returncâu lệnh và liệu chúng ta có thể đánh giá biểu đồ đã nói mà không bị đột biến (trong song song, ví dụ, khóa vắng mặt). Đó là nơi mà những lợi ích kỳ diệu không nằm ở việc chúng ta kết nối những thứ này với nhau như thế nào, mà là cách chúng có thể được thực hiện ở mức độ đột biến đóng gói ở mức độ này. Cả hai điều này có vẻ khả thi đối với tôi, nhưng tôi không chắc nó có thể áp dụng rộng rãi như thế nào và đó là nơi tôi hơi bối rối khi cố gắng xử lý các trường hợp sử dụng tiềm năng.


0

Tôi thấy rõ sự rò rỉ của sự chắc chắn ở đây. Có vẻ như "tác dụng phụ" là thuật ngữ nổi tiếng và thường được hiểu, nhưng thực tế thì không phải vậy. Tùy thuộc vào định nghĩa của bạn (thực sự bị thiếu trong OP), các tác dụng phụ có thể là hoàn toàn cần thiết (như @JacquesB quản lý để giải thích) hoặc không được chấp nhận. Hoặc, thực hiện một bước để làm rõ, cần phải phân biệt giữa các tác dụng phụ mong muốn mà người ta không muốn che giấu (tại thời điểm này, IO nổi tiếng của Haskell nổi lên: không có gì ngoài cách rõ ràng) và tác dụng phụ không mong muốn như một kết quả của lỗi mã và những thứ như vậy . Đó là những vấn đề khá khác nhau và do đó đòi hỏi lý luận khác nhau.

Vì vậy, tôi đề nghị bắt đầu từ việc tự đánh giá lại bản thân: "Làm thế nào để chúng ta xác định hiệu ứng phụ và định nghĩa (các) định nghĩa nói gì về mối liên hệ với tuyên bố" trả lại "?".


1
"tại thời điểm này IO nổi tiếng của Haskell nổi lên: không có gì ngoài cách rõ ràng" - nhân chứng chắc chắn là một lợi ích của IO đơn nguyên của Haskell, nhưng có một điểm khác: nó cung cấp một phương tiện để cô lập hoàn toàn tác dụng phụ bên ngoài ngôn ngữ - mặc dù các triển khai phổ biến không thực sự làm điều này, chủ yếu là do lo ngại về hiệu quả, về mặt khái niệm là đúng: đơn vị IO có thể được coi là một cách trả lại một hướng dẫn cho một môi trường hoàn toàn bên ngoài Chương trình Haskell, cùng với một tham chiếu đến một chức năng để tiếp tục sau khi hoàn thành.
Jules
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.