Là một hàm ngay lập tức không tinh khiết nếu nó lấy một hàm làm tham số?


17

Vì độ tinh khiết của một tham số đầu vào là không xác định cho đến khi thời gian chạy, nên một hàm ngay lập tức được coi là không tinh khiết nếu nó lấy một hàm làm tham số đầu vào?

Liên quan: nếu một hàm áp dụng một hàm thuần được xác định bên ngoài hàm, nhưng không được truyền vào dưới dạng tham số, liệu nó có còn thuần nếu nó đáp ứng các tiêu chí không có tác dụng phụ và đầu ra chỉ phụ thuộc vào đầu vào?

Đối với ngữ cảnh, tôi đang viết mã chức năng bằng JavaScript.


Như một ví dụ tầm thường, hãy xem xét:foo = function(function bar){ print(bar.toString()) }
David nói Phục hồi lại

1
@DavidGrinberg Đó không phải là một ví dụ phản tác dụng, tôi nghĩ, và thực sự làm nổi bật một vấn đề lớn hơn; nếu bạn có các hàm có thể bị ghi đè và không thể đảm bảo rằng việc triển khai là không có tác dụng phụ, thì bạn không thể đảm bảo rằng hầu hết các hàm lấy đối tượng và gọi phương thức của chúng là thuần túy. Có lẽ thanh toString () xóa một số tệp khỏi đĩa?
Joshua Taylor

3
@DavidGrinberg Nhưng, tôi nghĩ bạn đang nghĩ theo hướng tốt. foo = function(function bar) { return 3; } thuần túy, và lấy một hàm làm đối số.
Joshua Taylor

@JoshuaTaylor Điểm công bằng, tôi đã không nghĩ về điều đó. Nhưng về cơ bản bạn đã giải quyết được vấn đề. Là một cách khắc phục khác, chỉ cần gọi 'root' toString()(tức là cách bạn sẽ tìm thấy trên Đối tượng của Java).
David nói Phục hồi lại

Câu trả lời:


22

Miễn là tất cả các giá trị được sử dụng trong hàm chỉ được xác định bởi các tham số của nó, đó là một hàm thuần túy.

Khía cạnh đầu ra giống nhau mỗi lần cho cùng một đầu vào được kiểm soát bởi các tham số có thuần túy hay không. Nếu bạn giả sử các tham số (như một đối số hàm) cũng là thuần túy, thì nó là thuần túy.

Trong một ngôn ngữ như Javascript nơi độ tinh khiết không được thi hành, điều này có nghĩa là có thể tạo ra một hàm thuần túy khác có hành vi không tinh khiết bằng cách gọi một hàm không tinh khiết được truyền dưới dạng tham số.

Điều này có nghĩa là đối với các ngôn ngữ không thực thi độ tinh khiết (nghĩa là gần như tất cả), không thể định nghĩa một hàm thuần túy gọi các hàm được truyền dưới dạng đối số. Việc viết chúng thuần túy nhất có thể vẫn hữu ích và lý do về chúng là các hàm thuần túy, nhưng bạn phải thận trọng vì giả định rằng nó hoàn toàn sẽ bị phá vỡ nếu bạn đưa ra những lập luận sai.

Theo kinh nghiệm của tôi trong thực tế, điều này thường không phải là vấn đề lớn - tôi thấy hiếm khi các hàm không tinh khiết được sử dụng làm đối số hàm cho các hàm thuần túy.


Về tuyên bố của bạn rằng "Miễn là tất cả các giá trị được sử dụng trong hàm chỉ được xác định bởi các tham số của nó, thì đó là một hàm thuần túy". Điều gì xảy ra trong trường hợp hằng số? Nếu tôi có một chức năng areaOfCircle r => Math.Pi * r * r, sẽ areaOfCirclekhông thuần túy vì nó không chỉ sử dụng các tham số?
David Arno

2
@DavidArno Đó là một điểm công bằng. Theo tính minh bạch tham chiếu, việc đề cập đến một giá trị bên ngoài tĩnh sẽ không khác gì việc mã hóa cứng, vì vậy nó vẫn thuần túy.
Daenyth

1
"Điều này có nghĩa là có thể làm cho một hàm thuần túy có hành vi không tinh khiết" - theo định nghĩa, một hàm thuần túy có thể có hành vi không tinh khiết. Bạn đang phạm sai lầm khi nghĩ rằng một chức năng f(f2)gọi f2không quá phụ thuộc vào bất cứ điều gì f2dựa vào. Một hàm có thể gọi các hàm truyền vào tùy ý không thuần túy.
user2357112 hỗ trợ Monica

2
@Daenyth: Tốt hơn, nhưng nó vẫn giả định rằng hàm phải gọi hàm truyền vào. Nó có thể là một cái gì đó giống như function compose(f, g) {return function h(x) {return f(g(x));};}, là thuần túy mặc dù lấy chức năng làm đối số.
user2357112 hỗ trợ Monica

1
"Tôi thấy hiếm khi có các hàm không tinh khiết được sử dụng làm đối số hàm cho các hàm thuần túy." - không phải là ngôn ngữ chức năng, nhưng một số chức năng thư viện C ++ nhất định có các cảnh báo cụ thể rằng các đối số vị ngữ phải là (một số xấp xỉ với) thuần túy. Vì vậy, theo một nghĩa nào đó, điều này có nghĩa là nó không chỉ hiếm, nó không bao giờ xảy ra một cách hợp lệ. Nhưng theo một nghĩa khác, thực tế họ phải cấm nó là bởi vì mọi người đôi khi muốn làm điều đó. Ví dụ, họ muốn vượt qua một findthói quen một vị từ không tinh khiết trả về "true" cho mục phù hợp thứ ba mà nó gặp hoặc một số điều vô nghĩa như vậy.
Steve Jessop

19

Vì độ tinh khiết của một tham số đầu vào là không xác định cho đến khi thời gian chạy, nên một hàm ngay lập tức được coi là không tinh khiết nếu nó lấy một hàm làm tham số đầu vào?

Số mẫu:

function pure(other_function) {
    return 1;
}

Không quan trọng other_functionlà hàm thuần túy, hàm không tinh khiết hay không phải là hàm hoàn toàn. Các purechức năng là tinh khiết.

Ví dụ khác:

function identity(x) {
    return x;
}

Hàm này là thuần túy, ngay cả khi xlà hàm không tinh khiết. identity(impure_function)sẽ luôn trở lại impure_function, bất kể bạn lặp lại cuộc gọi bao nhiêu lần. Không quan trọng là identity(impure_function)()luôn trả lại cùng một thứ; một chức năng giá trị trả về giá trị trả về của không ảnh hưởng đến độ tinh khiết của nó.


Nói chung, nếu một hàm có thể gọi một hàm thì nó được truyền dưới dạng đối số, nó không thuần túy. Ví dụ, một chức năng function call(f) {f();}không thuần túy, bởi vì mặc dù nó không đề cập đến bất kỳ trạng thái toàn cầu hoặc đột biến nào, fcó thể là một cái gì đó giống như alertgây ra tác dụng phụ có thể nhìn thấy.

Nếu một hàm lấy các hàm làm đối số, nhưng nó không gọi chúng hoặc khiến chúng được gọi, thì nó có thể là thuần túy. Nó vẫn có thể không trong sạch nếu nó làm một số điều không tinh khiết khác. Ví dụ, function f(ignored_function) {alert('This isn't pure.');}là không tinh khiết, mặc dù nó không bao giờ gọi ignored_function.


4
Phản ứng này có vẻ quá tầm thường. Chúng ta có thể suy ra câu hỏi rằng mối quan tâm là về tham số hàm được gọi. Sự tồn tại của các hàm có thể / làm lấy các hàm khác làm tham số mà không gọi chúng không ảnh hưởng đến câu hỏi này.
walpen

13
@walpen: Câu hỏi không đề cập đến việc viện dẫn đối số. Không có lý do gì để cho rằng người hỏi thậm chí nhận ra một chức năng có thể lấy một chức năng khác làm đầu vào mà không cần gọi nó. Điều quan trọng là chỉ ra các giả định ẩn như thế này, thay vì chỉ cho rằng bạn có ý định giả định chúng.
user2357112 hỗ trợ Monica

12

Vì độ tinh khiết của một tham số đầu vào là không xác định cho đến khi thời gian chạy, nên một hàm ngay lập tức được coi là không tinh khiết nếu nó lấy một hàm làm tham số đầu vào?

Về mặt kỹ thuật, có, trừ khi có một số cách trong ngôn ngữ của bạn để đảm bảo rằng chức năng đầu vào cũng thuần túy.

nếu một hàm áp dụng một hàm thuần được xác định bên ngoài hàm, nhưng không được truyền vào dưới dạng tham số, thì nó vẫn thuần nếu nó đáp ứng các tiêu chí không có tác dụng phụ và đầu ra chỉ phụ thuộc vào đầu vào?

Đúng. Vì vậy, hãy tập trung vào những gì quan trọng ở đây. Gọi một chức năng thuần túy hoặc không hữu ích. Các hàm thuần túy rất hữu ích vì tạo ra cùng một đầu ra cho bất kỳ đầu vào nào và không phụ thuộc vào trạng thái hoặc có tác dụng phụ là một tập hợp các thuộc tính rất hữu ích. Điều đó có nghĩa là một khi chức năng của bạn đã được chạy, bạn có thể "nhớ" câu trả lời cho đầu vào đó và nó sẽ luôn luôn đúng. Bạn cũng không cần phải chạy lại chức năng để tạo hiệu ứng phụ. bạn có thể chạy chức năng đó song song (hoặc không theo thứ tự) với các chức năng khác và biết rằng chúng sẽ không có bất kỳ tương tác ẩn nào hoạt động xấu.

Các thuộc tính hữu ích đó vẫn giữ nếu hàm sử dụng các hàm chỉ đọc thuần túy khác để thực hiện công việc của nó, bất kể nó tham chiếu chúng như thế nào.


5

Như Telastyn đã nói: Về mặt kỹ thuật, có, trừ khi có một số cách trong ngôn ngữ của bạn để đảm bảo rằng chức năng đầu vào cũng thuần túy.

Đó không phải là giả thuyết, thực sự có nhiều cách tốt để đảm bảo điều này. Ít nhất là trong một ngôn ngữ đánh máy mạnh mẽ.

Một hàm ~ thuần túy như vậy bạn sẽ viết bằng JavaScript như

function foo(f) {
   return f(1) + 2;
}

có thể được dịch trực tiếp sang Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Bây giờ, trong JavaScript, bạn có thể làm những thứ xấu xa như

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

Điều này là không thể trong Haskell . Lý do là, một cái gì đó có tác dụng phụ giống như console.log()luôn phải có một loại kết quả IO something, không chỉ somethingmột mình.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

Để biểu thức này được đánh máy, chúng ta sẽ cần cung cấp foochữ ký loại

foo :: (Int -> IO Int) -> Int

Nhưng hóa ra tôi không thể thực hiện nó nữa: vì hàm đối số có IOtrong kết quả của nó, tôi không thể sử dụng nó trong foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

Cách duy nhất tôi có thể sử dụng một IOhành động foolà nếu kết quả của chính nó foocó loại IO Int:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

Nhưng tại thời điểm này, rõ ràng từ chữ ký của foonó cũng không phải là một chức năng thuần túy.


1
Trước khi bạn nói "không thể", hãy xem unsafeIO:-)
Bergi

2
@Bergi: đó thực sự không phải là một phần của Haskell, mà là giao diện chức năng nước ngoài của nó: cho phép khẳng định rằng một hàm được định nghĩa trong một số ngôn ngữ khác là thuần túy, mà trình biên dịch Haskell rõ ràng không thể suy ra từ chữ ký loại vì các ngôn ngữ khác thường không có những điều như IO. Ngẫu nhiên, nó cũng có thể được sử dụng để gây ra tình trạng hỗn loạn bằng cách ẩn các tác dụng phụ trong chức năng thuần túy của Viking, nhưng đó là thực sự không an toàn trong Haskell vì thực sự không có cách nào đáng tin cậy để xác định thứ tự đánh giá của các hàm thuần túy.
leftaroundabout

Vâng, đó là sự thật. Tuy nhiên, tôi nghĩ rằng tôi đã thấy nó được sử dụng để "che giấu" các tác dụng phụ có lợi trong chức năng "thuần túy", nơi mà sự an toàn của phương pháp này phải được thiết lập trước đó.
Bergi

@Bergi Bạn hầu như không nên sử dụng unsafeIO; đó là một lối thoát cuối cùng vượt qua sự bảo đảm của hệ thống loại, và do đó của bạn không phải là một điểm tốt.
Andres F.

0

Không có nó không phải là.

Nếu hàm được truyền là không tinh khiết VÀ hàm của bạn gọi hàm được truyền thì hàm của bạn sẽ được coi là không trong sạch.

Mối quan hệ thuần túy / không tinh khiết giống như đồng bộ hóa / không đồng bộ trong JS. Bạn có thể tự do sử dụng mã thuần túy từ tạp chất, nhưng không phải là cách khác.


Câu trả lời này không thêm bất cứ điều gì chưa được giải thích trong phần này ... Vui lòng xem lại các câu trả lời trước khi nghỉ ngơi mọi thứ :)
Andres F.

Điều gì về sự tương tự đồng bộ / không đồng bộ?
Bobby Marinoff 3/03/2016
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.