부작용이있는 폐쇄는“기능적 스타일”로 간주됩니까?


9

현대의 많은 프로그래밍 언어는 폐쇄 개념 , 즉 코드 또는 블록 (블록 또는 함수) 개념을 지원 합니다.

  1. 값으로 취급 될 수 있으므로 변수에 저장되어 코드의 다른 부분으로 전달되고 프로그램의 한 부분에서 정의되며 동일한 프로그램의 완전히 다른 부분에서 호출됩니다.
  2. 변수가 정의 된 컨텍스트에서 변수를 캡처하고 나중에 호출 될 때 변수에 액세스 할 수 있습니다 (아마도 완전히 다른 컨텍스트에 있음).

스칼라로 작성된 클로저의 예는 다음과 같습니다.

def filterList(xs: List[Int], lowerBound: Int): List[Int] =
  xs.filter(x => x >= lowerBound)

함수 리터럴 x => x >= lowerBound에는 free 변수가 포함되어 있으며 lowerBound,이 변수 는 filterList이름이 같은 함수의 인수에 의해 닫힙니다 . 클로저는 라이브러리 메소드로 전달되어 filter일반 함수로 반복해서 호출 할 수 있습니다.

이 사이트에서 많은 질문과 답변을 읽었으며 이해하는 한 폐쇄 라는 용어 는 종종 자동으로 기능 프로그래밍 및 기능 프로그래밍 스타일과 관련이 있습니다 .

wikipedia 에서 함수 프로그래밍의 정의는 다음과 같습니다.

컴퓨터 과학에서 기능 프로그래밍은 계산을 수학 함수의 평가로 취급하고 상태 및 변경 가능한 데이터를 피하는 프로그래밍 패러다임입니다. 상태의 변화를 강조하는 명령형 프로그래밍 스타일과 달리 함수 적용을 강조합니다.

그리고 더

함수 코드에서 [...]에서 함수의 출력 값은 함수 [...]에 입력되는 인수에만 의존합니다. 부작용을 제거하면 기능적 프로그래밍 개발의 주요 동기 중 하나 인 프로그램의 동작을보다 쉽게 ​​이해하고 예측할 수 있습니다.

반면, 프로그래밍 언어에서 제공하는 많은 클로저 구문은 클로저가 로컬 변수가 아닌 변수를 캡처하고 클로저가 호출 될 때 변수를 변경하여 정의 된 환경에 부작용을 발생시킵니다.

이 경우 클로저는 함수형 프로그래밍의 첫 번째 아이디어 (함수는 다른 값처럼 이동할 수있는 일류 엔티티)를 구현하지만 두 번째 아이디어는 무시합니다 (부작용을 피함).

부작용 이있는 클로저 사용이 기능적 스타일로 간주됩니까 아니면 클로저가 기능적 프로그래밍 스타일과 비 기능적 프로그래밍 스타일 모두에 사용할 수있는보다 일반적인 구조로 간주됩니까? 이 주제에 관한 문헌이 있습니까?

중요 사항

부작용의 유용성이나 부작용으로 인한 폐쇄가 있는지에 대해서는 의문의 여지가 없습니다. 또한 부작용이 있거나없는 폐쇄의 장점 / 단점에 대한 토론에 관심이 없습니다.

나는 그러한 클로저를 사용하는 것이 여전히 기능적 프로그래밍의 지지자에 의해 기능적 스타일로 간주되는지 또는 반대로 기능적 스타일을 사용할 때 사용이 권장되지 않는지 알고 싶습니다.


3
순전히 기능적인 스타일 / 언어에서는 부작용이 불가능합니다. 그래서 문제가 될 것 같습니다. 순도는 어느 수준에서 코드가 기능적이라고 생각합니까?
Steven Evers

@SnOrfus : 100 %?
조르지오

1
순전히 기능적인 언어에서는 부작용이 불가능하므로 질문에 대답해야합니다.
Steven Evers

@ SnOrfus : 많은 언어에서 클로저는 함수형 프로그래밍 구조로 간주되므로 약간 혼란 스러웠습니다. (1) FP를 사용하는 것보다 (1) 사용법이 더 일반적이며 (2) 클로저를 사용한다고해서 기능적 스타일을 사용한다고 자동으로 보장하지는 않습니다.
조르지오

downvoter가이 질문을 개선하는 방법에 대한 메모를 남길 수 있습니까?
조르지오

답변:


7

아니; 기능적 패러다임 의 정의는 상태 부족과 암묵적으로 부작용이 없다는 것입니다. 그것은 고차 함수, 클로저, 언어 지원 목록 조작 또는 다른 언어 기능에 관한 것이 아닙니다 ...

함수형 프로그래밍의 이름에서 유래 기능의 수학적 개념 - 동일한 입력이 항상 동일한 출력 제공에 호출을 반복 nullipotent 기능을 -. 이것은 데이터가 불변 인 경우에만 달성 할 수 있습니다 . 개발을 용이하게하기 위해 함수는 변경 가능하고 (함수 변경, 데이터는 여전히 변경 불가능) 고차 함수 (예 : 수학 함수, 파생 함수)-다른 함수를 입력하는 함수입니다. 함수가 전달되고 인수로 전달 될 수 있도록 일급 함수 가 채택되었습니다. 이에 따라 생산성을 더욱 높이기 위해 폐쇄가 나타났습니다.

물론 이것은 매우 단순화 된 견해입니다.


3
고차 함수는 가변성과 전혀 관련이 없으며 일류 함수도 아닙니다. 변경 가능한 상태 나 부작용없이 Haskell에서 함수를 전달하고 다른 함수를 쉽게 만들 수 있습니다.
tdammers

@tdammers 나는 아마 매우 명확하게 표현하지 않았다; 내가 함수가 변경 가능하다고 말했을 때 나는 데이터 변경 가능성을 말하는 것이 아니라 함수의 동작이 (고차 함수에 의해) 변경 될 수 있다는 사실을 언급했다.
m3th0dman

고차 함수는 기존 함수를 변경할 필요가 없습니다. 대부분의 교재 예제 기존 함수를 전혀 수정하지 않고 기존 함수를 새로운 함수로 결합map 합니다. 예를 들어 함수를 가져 와서 목록에 적용한 다음 결과 목록을 반환합니다. map인수를 수정하지 않고 인수로 사용하는 함수의 동작을 변경하지 않지만 확실히 고차 함수입니다. 함수 매개 변수로 부분적으로 적용하면 함수 매개 변수 만 사용하여 목록에서 작동하지만 여전히 돌연변이가 발생하지 않은 새로운 기능.
tdammers

@tdammers : 정확함 : 가변성은 명령형 또는 다중 패러다임 언어에서만 사용됩니다. 이것들은 고차 함수 (또는 방법)와 클로저 개념을 가질 수 있지만 불변성을 포기합니다. 이것은 유용 할 수 있습니다 (나는 그렇게해서는 안된다고 말하지는 않습니다).하지만 내 질문은 여전히이 기능을 호출 할 수 있는지 여부입니다. 즉, 일반적으로 클로저는 엄격하게 기능적인 개념이 아닙니다.
Giorgio

@Giorgio : 고전 FP 언어의 대부분이 가변성이있다; Haskell은 전혀 변하지 않는 내 머리 꼭대기에서 생각할 수있는 유일한 사람입니다. 여전히 FP에서 변경 가능한 상태를 피하는 것이 중요합니다.
tdammers

9

"기능 스타일"은 부작용이없는 프로그래밍을 의미합니다.

왜 확장 방법 에 대한 Eric Lippert의 블로그 항목ForEach<T> 과 Microsoft가 Linq에 이와 같은 시퀀스 방법을 포함하지 않은지 살펴보십시오 .

나는 두 가지 이유로 이러한 방법을 제공하는 데 철학적으로 반대합니다.

첫 번째 이유는 그렇게하는 것이 다른 모든 시퀀스 연산자가 기반으로하는 기능 프로그래밍 원칙을 위반하기 때문입니다. 분명히이 방법을 호출하는 유일한 목적은 부작용을 일으키는 것입니다. 표현식의 목적은 부작용을 일으키지 않고 값을 계산하는 것입니다. 진술의 목적은 부작용을 일으키는 것입니다. 이 것의 호출 사이트는 표현식과 같이 끔찍한 것처럼 보일 것입니다 (물론, 메서드가 void 반환이기 때문에 표현식은 "statement expression"컨텍스트에서만 사용할 수 있습니다). 부작용에만 유용한 유일한 유일한 시퀀스 연산자를 만드십시오.

두 번째 이유는 그렇게하면 언어에 새로운 표현력을 추가하지 않기 때문입니다. 이렇게하면 완벽하게 명확한 코드를 다시 작성할 수 있습니다.

foreach(Foo foo in foos){ statement involving foo; }

이 코드로 :

foos.ForEach((Foo foo)=>{ statement involving foo; });

거의 정확히 같은 문자를 약간 다른 순서로 사용합니다. 그러나 두 번째 버전은 이해하기 어렵고 디버깅하기가 어렵고 클로저 의미를 도입하여 잠재적으로 개체 수명을 미묘한 방식으로 변경합니다.


1
나는 그에게 동의하지만 고려할 때 ... 그의 주장은 조금 약화 ParallelQuery<T>.ForAll(...). 이러한 구현은 IEnumerable<T>.ForEach(...)디버깅에 매우 유용 ForAll문을합니다 (교체 ForAllForEach하고, 제거 AsParallel()하고 훨씬 더 쉽게 / 디버깅을 통해 단계 수)
스티븐 에버스

분명히 Joe Duffy 는 다른 아이디어를 가지고 있습니다. : D
Robert Harvey

스칼라는 순도를 강요하지 않습니다. 순결하지 않은 클로저를 고차 함수에 전달할 수 있습니다. 클로저에 대한 아이디어는 함수형 프로그래밍에만 국한된 것이 아니라보다 일반적인 아이디어입니다.
Giorgio

5
@Giorgio : 클로저는 여전히 클로저로 간주되기 위해 순수 할 필요는 없습니다. 그러나 "기능적 스타일"로 간주 되려면 순수해야합니다.
Robert Harvey

3
@Giorgio : 변경 가능한 상태와 부작용에 대해 클로저를 만들어 다른 함수에 전달하는 것이 정말 유용합니다. 함수형 프로그래밍의 목표와 반대입니다. 기능적인 언어에서 일반적으로 사용되는 람다에 대한 우아한 언어 지원에서 많은 혼란이 발생한다고 생각합니다.
GlenPeterson

0

함수형 프로그래밍은 일급 함수를 다음 개념 수준으로 확실히 가져 오지만 익명 함수를 선언하거나 다른 함수에 함수를 전달하는 것이 반드시 함수형 프로그래밍 일 필요는 없습니다. C에서는 모든 것이 정수였습니다. 숫자, 데이터에 대한 포인터, 함수에 대한 포인터 ... 모두 int입니다. 함수 포인터를 다른 함수로 전달하고 함수 포인터 목록을 만들 수 있습니다 ... 어셈블리 언어로 작업하는 경우 함수는 실제로 기계 명령어 블록이 저장된 메모리의 주소 일뿐입니다. 함수에 이름을주는 것은 컴파일러가 코드를 작성해야하는 사람들에게 추가 오버 헤드입니다. 따라서 기능은 완전히 기능이없는 언어로 "일류"였습니다.

REPL에서 수학 공식을 계산하는 것만으로도 언어 적으로 기능적으로 순수 할 수 있습니다. 그러나 대부분의 비즈니스 프로그래밍에는 부작용이 있습니다. 장기 실행 프로그램이 완료되기를 기다리는 동안 돈을 잃는 것은 부작용입니다. 외부 조치 수행 : 파일에 쓰기, 데이터베이스 업데이트, 순서대로 이벤트 로깅 등의 상태 변경이 필요합니다. 코드를 걱정할 필요가 없도록 부작용을 일으키는 불변 래퍼에 이러한 작업을 캡슐화하면 상태가 실제로 변경되는지 여부에 대해 토론 할 수 있습니다. 그러나 나무가 아무도 들리지 않고 숲에 떨어지면 나무가 소리를 내는지 여부를 논의하는 것과 같습니다. 사실 나무는 똑바로 시작되어 땅에 닿았습니다. 작업이 완료되었다고보고하더라도 작업이 완료되면 상태가 변경됩니다.

따라서 우리는 흑백이 아니라 회색 음영의 기능적 순도를 유지합니다. 그리고 그 규모에서 부작용이 적고 변이가 적을수록 (기능적) 더 좋습니다.

기능 코드에서 부작용이나 변경 가능한 상태가 절대적으로 필요한 경우 가능한 한 최선을 다해 프로그램의 나머지 부분에서 캡슐화하거나 그 상태를 유지하려고합니다. 클로저 (또는 다른 것)를 사용하여 부작용이나 변경 가능한 상태를 순수한 함수에 주입하는 것은 함수형 프로그래밍의 대립입니다. 유일한 예외는 클로저가 전달 된 코드의 부작용을 캡슐화하는 가장 효과적인 방법 인 경우 일 수 있습니다. 여전히 "기능적 프로그래밍"은 아니지만 특정 상황에서 얻을 수있는 옷장 일 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.