상태 비 저장 프로그래밍의 장점?


132

최근에 함수형 프로그래밍 (특히 Haskell)에 대해 배웠지 만 Lisp 및 Erlang에 대한 자습서도 살펴 보았습니다. 개념이 매우 밝아진 것을 발견했지만 여전히 "부작용 없음"개념의 실제적인 측면을 보지 못했습니다. 그것의 실용적인 장점은 무엇입니까? 나는 기능적인 사고 방식을 생각하려고 노력하고 있지만, 쉬운 방법으로 상태를 저장할 수있는 능력없이 지나치게 복잡해 보이는 상황이 있습니다 (하스켈의 모나드를 '쉬운'이라고는 생각하지 않습니다).

하스켈 (또는 다른 순전히 기능적인 언어)을 계속 깊이 배울 가치가 있습니까? 기능적 또는 상태 비 저장 프로그래밍이 실제로 절차 적보다 더 생산적입니까? 나중에 Haskell 또는 다른 기능적 언어를 계속 사용할 수 있습니까, 아니면 이해를 위해서만 배울 수 있습니까?

생산성보다 성능에 관심이 적습니다. 그래서 나는 주로 절차 적 / 개체 지향적 / 무엇보다 기능적 언어로 더 생산적 일지 묻고 있습니다.

답변:


168

간단히 말해서 기능 프로그래밍을 읽으십시오 .

되어 있지 적어도 어느 비 저장 프로그래밍에 많은 장점이 있습니다 극적으로 멀티 스레드 동시 코드. 솔직히 말하면, 가변 상태는 멀티 스레드 코드의 적입니다. 기본적으로 값을 변경할 수없는 경우 프로그래머는 하나의 스레드가 두 스레드 사이의 공유 상태 값을 변경하는 것에 대해 걱정할 필요가 없으므로 경쟁 조건과 관련된 전체 멀티 스레딩 버그 클래스를 제거합니다. 경쟁 조건이 없기 때문에 잠금을 사용해야 할 이유가 없으므로 불변성으로 인해 교착 상태와 관련된 또 다른 전체 버그가 제거됩니다.

이것이 함수형 프로그래밍이 중요한 이유이며, 아마도 함수형 프로그래밍 기차를 타기에 가장 좋은 이유 일 것입니다. 단순화 된 디버깅 (즉, 함수가 순수하고 응용 프로그램의 다른 부분에서 상태를 변경하지 않음),보다 간결하고 표현적인 코드, 디자인 패턴에 크게 의존하는 언어에 비해 상용구 코드가 적고, 컴파일러는 더욱 적극적으로 코드를 최적화 할 수 있습니다.


5
나는 이것을 두 번째로한다! 함수형 프로그래밍은 병렬 프로그래밍에 적합하기 때문에 앞으로 훨씬 더 널리 사용될 것이라고 생각합니다.
Ray Hidayat

@ 레이 : 분산 프로그래밍도 추가하겠습니다!
Anton Tykhyy

디버깅을 제외하고는 대부분 사실입니다. 실제 호출 스택이없고 패턴 일치 스택 만 있기 때문에 일반적으로 haskell에서 더 어렵습니다. 그리고 코드의 결과를 예측하기가 훨씬 어렵습니다.
hasufell

3
또한 함수형 프로그래밍은 실제로 "상태 비 저장"에 관한 것이 아닙니다. 재귀는 이미 암시 적 (로컬) 상태이며 haskell에서 우리가하는 주요 작업입니다. 관용적 하스켈 (예를 들어, 계산 기하학)에서 몇 가지 사소한 알고리즘을 구현하고 그것들을 재미있게 디버깅하면 분명해집니다.
hasufell

2
무국적자와 FP를 동일시하는 것을 싫어하십시오. 많은 FP 프로그램은 상태로 채워져 있으며 단순히 개체가 아닌 클로저에 존재합니다.
mikemaccana 2016 년

46

더 많은 프로그램 조각이 상태 비 저장 일수록 더 많은 부분을 나누지 않고 조각을 모을 수있는 더 많은 방법이 있습니다 . 무국적 패러다임의 힘은 무국적 (또는 순도) 자체 가 아니라 강력하고 재사용 가능한 기능 을 작성 하고 결합 할 수있는 능력 입니다.

John Hughes의 논문 Why Functional Programming Matters (PDF) 에서 많은 예제가 포함 된 유용한 자습서를 찾을 수 있습니다 .

당신이 될 것입니다 수병은 당신이 또한 대수 데이터 유형 및 패턴 매칭 (의 Caml, SML, 하스켈)가 있는지 기능적 언어를 선택 특히, 생산성.


믹스 인도 OOP와 비슷한 방식으로 재사용 가능한 코드를 제공하지 않습니까? OOP를 옹호하지 않고 자신을 이해하려고합니다.
mikemaccana 2016 년

20

다른 많은 답변은 함수 프로그래밍의 성능 (병렬) 측면에 중점을 두 었으며, 이것이 매우 중요하다고 생각합니다. 그러나 생산성과 관련하여 구체적으로 요구 한 것은 명령형 패러다임보다 기능적 패러다임에서 동일한 것을 더 빠르게 프로그래밍 할 수 있습니다.

실제로 F #의 프로그래밍이 내가 생각하는 방식과 일치한다는 것이 개인적 경험에서 알기 때문에 더 쉽습니다. 그것이 가장 큰 차이점이라고 생각합니다. F #과 C #으로 모두 프로그래밍했으며 F #에는 "언어와 싸우기"가 훨씬 적습니다. F #의 세부 사항에 대해 생각할 필요가 없습니다. 내가 찾은 것의 몇 가지 예는 다음과 같습니다.

예를 들어 F #이 정적으로 형식이 지정되었지만 (모든 형식이 컴파일 타임에 해결됨) 형식 유추는 어떤 형식이 있는지 파악하므로 말할 필요가 없습니다. 그리고 그것을 알아낼 수 없다면, 자동으로 함수 / 클래스 / 일반적인 것을 자동으로 만듭니다. 따라서 일반적인 내용을 작성할 필요가 없으며 모두 자동입니다. 즉, 문제에 대해 생각하고 구현하는 방법이 적다는 것을 의미합니다. 실제로 C #으로 돌아올 때 마다이 유형 유추를 놓친다는 것을 알았으므로 더 이상 필요하지 않을 때까지 어떻게 산만하게하는지 알지 못합니다.

또한 F #에서는 루프를 작성하는 대신 함수를 호출합니다. 루프 구성에 대해 더 이상 생각할 필요가 없기 때문에 미묘한 변화이지만 중요합니다. 예를 들어, 여기에 가서 무언가를 일치시키는 코드 조각이 있습니다 (프로젝트 오일러 퍼즐의 내용을 기억할 수는 없습니다).

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

C #에서 필터를 수행 한 다음 맵 (각 요소의 변환)을 수행하는 것은 매우 간단하지만 더 낮은 수준으로 생각해야한다는 것을 알고 있습니다. 특히 루프 자체를 작성하고 명시적인 if 문과 그 종류의 것들을 작성해야합니다. F #을 학습 한 이후로, 필터링하려는 경우 "필터"를 작성하고 매핑하려는 경우 구현하는 대신 "맵"을 작성하는 기능적인 방식으로 코딩하는 것이 더 쉽다는 것을 깨달았습니다. 각 세부 사항.

나는 또한 F #과 ocaml 및 다른 기능적 언어를 분리한다고 생각하는 연산자를 좋아합니다. 파이프 연산자이므로 한 표현식의 출력을 다른 표현식의 입력으로 "파이프"할 수 있습니다. 코드가 내가 생각하는 방식을 따르게합니다. 위의 코드 스 니펫에서와 같이 "인수 시퀀스를 가져 와서 필터링 한 다음 매핑하십시오." 루프와 if 문을 작성하는 데 너무 바빠서 명령형 프로그래밍 언어를 사용하지 않는 매우 높은 수준의 사고입니다. 다른 언어로 갈 때마다 가장 그리워하는 것은 바로 하나입니다.

따라서 일반적으로 C #과 F #으로 프로그래밍 할 수 있지만 더 높은 수준으로 생각할 수 있기 때문에 F #을 사용하는 것이 더 쉽다는 것을 알았습니다. 기능적 프로그래밍에서 최소한의 세부 사항이 제거 되었기 때문에 (적어도 F #에서는) 더 생산적이라고 주장합니다.

편집 : 나는 함수형 프로그래밍 언어에서 "상태"의 예를 요청한 의견 중 하나를 보았습니다. F #은 필수적으로 작성 될 수 있으므로 F #에서 변경 가능한 상태를 갖는 방법에 대한 직접적인 예는 다음과 같습니다.

let mutable x = 5
for i in 1..10 do
    x <- x + i

1
나는 귀하의 게시물에 일반적으로 동의하지만, |> 기능 프로그래밍과는 아무런 관련이 없습니다. 실제로, a |> b (p1, p2)단지 구문 설탕입니다 b (a, p1, p2). 이것을 오른쪽 연관성과 결합하면 알 수 있습니다.
Anton Tykhyy

2
사실, F #에 대한 많은 긍정적 인 경험이 함수형 프로그래밍보다 F #과 더 관련이 있다는 것을 인정해야합니다. 그러나 여전히이 둘 사이에는 강한 상관 관계가 있으며 형식 유추 및 |>와 같은 기능 자체가 함수형 프로그래밍이 아니더라도 분명히 "영역과 함께 가십시오"라고 주장합니다. 적어도 일반적으로.
Ray Hidayat

|>는 또 다른 고차 삽입 함수입니다 (이 경우 함수 응용 연산자). 자신의 고차원 적 정의, 중위 사업자는 확실히 함수형 프로그래밍의 일부 (당신은 음모를 아니라면). Haskell은 파이프 라인의 정보가 오른쪽에서 왼쪽으로 흐르는 것을 제외하고 $가 동일합니다.
Norman Ramsey

15

디버깅에 오랜 시간을 소비 한 모든 어려운 버그를 고려하십시오.

이제 프로그램의 두 가지 개별 구성 요소 사이에 "의도하지 않은 상호 작용"으로 인해 버그가 몇 개나 있었습니까? (거의 모든 스레딩 버그는 다음과 같은 형식을 갖습니다. 공유 데이터 작성, 교착 상태 등. ... 또한 전역 상태에 예기치 않은 영향을 미치는 라이브러리를 찾거나 레지스트리 / 환경을 읽거나 쓰는 등이 일반적입니다.) I '하드 버그'3 개 중 1 개 이상이이 범주에 속한다고합니다.

상태 비 저장 / 불변 / 순수 프로그래밍으로 전환하면 모든 버그가 사라집니다. 당신은 대신 새로운 도전되게됩니다 (예를 들어, 당신은 할 때 어떻게 환경과 상호 작용하는 다른 모듈을 원하는)하지만 하스켈과 같은 언어로, 그 상호 작용이 명시 적으로 단지의 유형을 볼 수 있다는 것을 의미 타입 시스템으로 구체화 얻을 나머지 프로그램과의 상호 작용 유형에 대한 기능과 이유.

그것은 '불변성'IMO의 큰 승리입니다. 이상적인 세계에서 우리는 모두 훌륭한 API를 설계하고, 상황이 변경 가능하더라도 효과는 지역적이고 잘 문서화되고 '예기치 않은'상호 작용은 최소화됩니다. 실제 세계에는 수많은 방식으로 전역 상태와 상호 작용하는 많은 API가 있으며, 가장 위험한 버그의 원인입니다. 무국적자에 대한 열망은 구성 요소들 사이의 의도하지 않은 / 암시 적 / 비하인드 상호 작용을 제거하기를 갈망합니다.


6
누군가가 한 번 변경 가능한 값을 덮어 쓰면 이전 값을 명시 적으로 가비지 수집 / 해제한다는 의미입니다. 경우에 따라 프로그램의 다른 부분은 해당 값을 사용하여 수행되지 않았습니다. 값을 변경할 수 없으면이 버그 클래스도 사라집니다.
shapr

8

상태 비 저장 함수의 한 가지 장점은 함수의 반환 값을 미리 계산하거나 캐싱 할 수 있다는 것입니다. 일부 C 컴파일러조차도 함수를 상태 비 저장으로 표시하여 최적화 가능성을 향상시킬 수 있습니다. 다른 많은 사람들이 지적했듯이 상태 비 저장 기능은 병렬화가 훨씬 쉽습니다.

그러나 효율성 만이 유일한 관심사는 아닙니다. 순수 함수는 영향을받는 모든 것이 명시 적으로 명시되어 있으므로 테스트 및 디버그가 더 쉽습니다. 그리고 기능적 언어로 프로그래밍 할 때는 가능한 한 "더러운"기능 (I / O 등)을 거의 사용하지 않는 습관이 있습니다. 이러한 방식으로 상태 저장 항목을 분리하는 것은 작동하지 않는 언어로도 프로그램을 디자인하는 좋은 방법입니다.

기능적 언어는 "습득"하는 데 시간이 걸릴 수 있으며 해당 프로세스를 거치지 않은 사람에게는 설명하기가 어렵습니다. 그러나 오래 지속되는 대부분의 사람들은 결국 기능적 언어를 많이 사용하지 않아도 소란이 가치가 있음을 깨닫습니다.


그 첫 번째 부분은 정말 흥미로운 점입니다. 전에는 그런 생각을하지 않았습니다. 감사!
Sasha Chedygov 2

sin(PI/3)PI가 상수 인 코드에 컴파일러가 있다고 가정하면 컴파일러는 컴파일 타임 에이 함수 평가 하고 결과를 생성 된 코드에 포함시킬 수 있습니다.
Artelius 2009

6

상태가 없으면 코드를 자동으로 병렬화하는 것이 매우 쉽습니다 (CPU가 점점 더 많은 코어로 만들어 지므로 매우 중요합니다).


예, 나는 분명히 그것을 조사했습니다. Erlang의 동시성 모델은 특히 매우 흥미 롭습니다. 그러나 지금은 생산성만큼 동시성에 관심이 없습니다. 상태가없는 프로그래밍으로 인한 생산성 보너스가 있습니까?
Sasha Chedygov

2
@musicfreak, 생산성 보너스는 없습니다. 그러나 현대 FP 언어를 사용하면 실제로 필요한 경우 상태를 계속 사용할 수 있습니다.
알 수 없음

정말? 기능적 언어로 상태의 예를 들어 줄 수 있습니까?
사샤 체디 고프

- 하스켈에서 국가 모나드를 확인 book.realworldhaskell.org/read/monads.html#x_NZ
rampion

4
@ 알 수 없음 : 동의하지 않습니다. 상태가없는 프로그래밍은 예상치 못한 / 다른 의도하지 않은 다른 구성 요소의 상호 작용으로 인한 버그 발생을 줄입니다. 또한 더 나은 디자인 (더 많은 재사용 성, 메커니즘과 정책의 분리, 그런 종류의 것들)을 장려합니다. 항상 작업에 적합하지는 않지만 어떤 경우에는 실제로 빛납니다.
Artelius 2009

6

트래픽이 많을 때 상태 비 저장 웹 응용 프로그램이 필수적입니다.

예를 들어 보안상의 이유로 클라이언트 측에 저장하지 않으려는 많은 사용자 데이터가있을 수 있습니다. 이 경우 서버 측에 저장해야합니다. 웹 응용 프로그램 기본 세션을 사용할 수 있지만 응용 프로그램 인스턴스가 둘 이상인 경우 각 사용자가 항상 동일한 인스턴스로 연결되어 있는지 확인해야합니다.

로드 밸런서는 종종로드 밸런서가 사용자 요청을 보낼 서버를 어떻게 알 수있는 '고정 세션'을 가질 수 있습니다. 예를 들어 웹 응용 프로그램을 다시 시작할 때마다 연결된 모든 사용자가 세션을 잃게된다는 의미는 아닙니다.

더 나은 접근법은 웹 서버 뒤에 세션을 일종의 데이터 저장소에 저장하는 것입니다. 요즘에는 사용할 수있는 훌륭한 nosql 제품 (redis, mongo, elasticsearch, memcached)이 많이 있습니다. 이렇게하면 웹 서버는 상태 비 저장 상태가되지만 여전히 서버 쪽 상태가 유지되며 올바른 데이터 저장소 설정을 선택하여이 상태의 가용성을 관리 할 수 ​​있습니다. 이러한 데이터 저장소는 일반적으로 중복성이 뛰어나 사용자에게 영향을주지 않고 웹 응용 프로그램과 데이터 저장소를 거의 항상 변경할 수 있어야합니다.


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