2009-12-16 2 views
17

지금 당분간 함수 프로그래밍에 대한 내 머리를 감싸려고 노력하고 있습니까? lambda 미적분, LISP, OCML, F # 및 심지어 조합 논리를 찾았지만 주요 문제점은 어떻게하면 (사용자와 상호 작용하고, 원격 서비스와 통신하며, 심지어는 시뮬레이션을 사용하여) 부작용이 필요한 작업을 수행하는 것입니다. 임의의 샘플링) 순수한 함수 프로그래밍의 기본 전제를 ​​위반하지 않고 주어진 입력에 대해 출력이 결정적이라는 것인가? 나는 내가 제대로 교육시키려는 시도를 환영하지 않는다면 나는 이해가되기를 바란다. 미리 감사드립니다.순수 함수 프로그래밍에서 부작용이 있습니까?

+2

그런데, 당신의 질문 제목과 질문은 매우 다릅니다. 제목을 "순수한 함수 언어로 부작용이 있습니까?"로 변경하고 싶을 수도 있습니다. –

답변

20

대부분의 실제 함수 프로그래밍은 대부분의 감각에서 "순수"하지 않으므로 질문에 대한 답변의 절반은 "순결을 포기함으로써 해"입니다. 즉, 대안입니다.

"순수한"순수함에서 전체 프로그램은 하나 이상의 인수로 이루어진 단일 기능을 나타내며 값을 반환합니다. 눈을 가늘고 손을 흔드는 경우 모든 사용자 입력이 함수의 "인수"의 일부이고 모든 출력이 "반환 값"의 일부이며 그 다음에 내용을 조금씩 퍼지하므로 선언 할 수 있습니다 실제 I/O는 "주문형"입니다.

비슷한 관점은 함수에 대한 입력이 "외부 세계의 전체 상태"이며 함수를 평가하는 것이 새롭고 수정 된 "상태"를 반환한다고 선언하는 것입니다. 이 경우 세계 상태를 사용하는 프로그램의 모든 기능은 분명히 프로그램의 두 가지 평가가 정확히 동일한 외부 세계를 갖지 않으므로 "결정 론적"인 것으로부터 해제됩니다.

당신이 순수한 람다 미적분학 (또는 난해한 언어 Lazy K와 같은 어떤 것)에 대화 형 프로그램을 쓰고 싶다면, 그것은 당신이 그것을 어떻게 할 것인지 개념적으로 말입니다.

실제적으로, 입력이 함수에 대한 인수로 사용될 때 I/O가 올바른 순서로 발생하는지 확인하는 문제가 발생합니다. 이 문제에 대한 "순수한"해법의 일반적인 구조는 함수 구성입니다. 예를 들어 I/O를 수행하는 세 가지 기능이 있으며 특정 순서로 호출하려고한다고 가정 해보십시오. RunThreeFunctions(f1, f2, f3)과 같은 식으로하면 평가할 순서를 결정할 사항이 없습니다. 반면에 각 함수가 인수로 다른 함수를 사용하게하면 다음과 같이 연결할 수 있습니다. f1(f2(f3()))f2의 평가가 해당 값에 따라 다르므로 f3이 먼저 평가됩니다.[편집 : lazy vs. eager 평가에 대한 아래 주석을 참조하십시오. 게으른 평가는 매우 순수한 상황에서 실제로 매우 일반적이기 때문에 이것은 중요합니다. 예를 들어, 순수한 람다 미적분학에서의 재귀의 표준 구현은 열망한 평가 하에서는 종단점이 아니다.]

다시 말하면, 람다 미적분학에 대화식 프로그램을 작성하는 것은 아마도 그렇게 할 수있다. 실제로 프로그래밍 할 수있는 무언가가 필요하다면 함수 합성 부분을 함수의 개념 구조와 결합하여 세계의 상태를 나타내는 값을 가져오고 반환하는 것이 좋을 것입니다. 더 높은 수준의 추상화를 작성하여 " I/O 함수 사이의 "세계 상태"값을 유지하며, 엄격하게 선형성을 유지하기 위해 "세계 상태"를 유지하는 것이 이상적입니다. Haskell의 IO 모나드를 재발 명했지만

잘하면 그게 당신을 심지어 even 혼란스러워하지 않았 으면 좋겠다.

+4

"한편, 각 함수가 인수로 다른 함수를 사용하도록하면 f1 (f2 (f3()))과 같이 연결할 수 있습니다.이 경우 f3이 먼저 평가된다는 것을 알고 있습니다 f2의 값은 그 값에 달려 있습니다. " - f2와 f1이 결과를 계산하기 위해서 그리고 f1의 호출자가 결과를 사용하기 위해서만 실제로 인자를 사용하는 경우. 그렇지 않으면 게으른 평가가 합법적으로 시작될 수 있습니다. –

+5

당신은 물론 정확합니다. 그리고 나는 대답의 이미 거만한 자세한 정보를 악화시키지 않기 위해 많은 다른 세부 사항들을 훑어 보았습니다. –

+0

함수가 평가 될 때 세계 상태가 변경됩니까? 세계 국가 구조를 다시 쓰는 것은 응용 프로그램 외부입니까? 끊임없이 변화하는 세계를 다루기 위해 계속해서 응용 프로그램을 호출해야합니까? –

5

기능적 프로그래밍은 부작용을 차단하는 것에 관한 것으로, 완전히 제거하지 않으려 고합니다.

... 그렇습니다. FP를 유용하게 사용합니다 (어쨌든 어쨌든 얼랭과 함께). "아이디어"에서 "프로그램"(또는 솔루션의 문제)을 얻는 것이 더 쉽다는 것을 알았습니다. 그냥 나.

+0

나는 파이프 라이닝과 필터링이라는 개념을 함수 프로그래밍의 기본 원칙 인 기본 데이터 처리 함수로 항상 좋아했다. –

+0

@Jeremy E : agree : 나는 HW 엔지니어링 배경에서 왔고, Erlang이 나를 허용하는 것처럼 "데이터 흐름"이라는 측면에서 매우 자주 생각한다. – jldupont

2

적어도 또 다른 필수 개념 인 Monads을 알아야합니다. I/O 및 기타 "유용한"작업을 수행하려면이 작업이 필요합니다.

0

작업에서 사용하지 않더라도 하나 이상의 함수형 프로그래밍 언어를 사용하면 다르게 생각하는 법을 배우고 문제에 대한 대체 접근법의 툴킷을 제공 할 수 있습니다 (사용자를 혼란스럽게 만들 수도 있습니다. 다른 언어의 기능적인 접근 방식만큼 깔끔하고 깨끗한 것을 할 수는 없습니다).

그리고 XSL 스타일 시트를 작성하는 데 더 나을 것입니다.

+1

XSLT : Turing-complete XML은 세상에서 필요한 것입니다. –

+0

@camccann 나는 비꼬기를 바랍니다. –

+0

Turing-complete XML은 Turing-complete 유형 일반화 메커니즘 (c.f. C++ 템플릿 등)보다 훨씬 더 좋습니다. 즉, 꺾쇠 괄호 안에는 XQuery가 있습니다. –

9

하스켈은 순수 함수형 프로그래밍 언어입니다. Haskell에서 모든 함수는 순수하다 (즉, 항상 같은 입력에 대해서 같은 출력을 준다). 그러나 어떻게 하스켈에서 부작용을 처리합니까? 음,이 문제는 아름답게 monads을 사용하여 해결됩니다.

예를 들어 I/O 취하기. Haskell에서 I/O를하는 모든 함수는 IO 계산, 즉 IO 모나드의 계산을 반환합니다. 그래서, 예를 들어, 대신 int를 반환하는 키보드에서 int를 읽는 기능은, 그것이 실행되는 int를 결정타 IO 계산을 반환

askForInt :: String -> IO Int 

이 대신 I/O 계산을 반환하므로 Int 인 경우이 결과를 직접 합계로 사용할 수 없습니다. Int 값에 액세스하려면 계산을 "해제"해야합니다. 이 작업을 수행 할 수있는 유일한 방법은 바인딩 기능 (>>=)를 사용하는 것입니다 :이 또한 IO 계산을 반환하기 때문에

(>>=) :: IO a -> (a -> IO b) -> IO b 

을, 당신은 항상 I/O 계산으로 끝낼. 이것이 하스켈이 부작용을 격리하는 방법입니다. IO 모나드는 실세계의 상태를 추상화하는 역할을합니다 (실제로 커버 아래에 상태 부분에 대해 RealWorld 유형으로 구현됩니다).

+0

그러나 모나드는 기능이 없으므로 하스켈은 "순수 함수형 프로그래밍 언어"가 아닙니다. – RBarryYoung

+2

@RBarryYoung : "비 기능적"이라는 것은 무엇을 의미합니까? 왜 하스켈은 순전히 기능적이지 않다고 말하는가? Haskell *은 순전히 기능적입니다. – Stephan202

+2

순수 함수형 언어가 모든 것이 기능이라는 것을 의미하지는 않습니다. ** 기능 ** 언어이며 모든 기능은 ** 순수 ** (nevermind'unsafePerformIO') –

2

하스켈이 수행하는 방법은 page에 대한 설명은 wikipedia과 하스켈의 설명을 참조하십시오.

기본적으로 아이디어는 IO 모나드를 제거하지 않는다는 것입니다. 내 이해는 당신이 IO 모나드를 unwrap하고 그 함수를 실행하는 함수를 연결할 수 있다는 것이다. 그러나 IO 모나드를 모두 제거 할 수는 없습니다.

IO에 직접 연결되지 않은 모나드를 사용하는 또 다른 예는 아마 모나드입니다. 이 모나드는 IO 모나드와는 반대로 '풀릴 수 없습니다'.그러나 Maybe 모나드를 사용하여 모나드 사용법을 설명하는 것이 더 쉽습니다. 다음 함수가 있다고 가정 해 봅시다. Just 9을 반환하는

wrap :: Maybe x -> (x -> y) -> Maybe y 
wrap Nothing f = Nothing 
wrap (Just x) f = Just (f x) 

지금은 wrap (Just 4) (5+)를 호출 할 수 있습니다.

IO 모나드의 아이디어는 내부 유형에 (+5)와 같은 기능을 사용할 수 있다는 것입니다. 모나드는 함수가 순차적으로 호출된다는 것을 보장 할 것입니다. 왜냐하면 모든 함수가 포장 IO 모나드로 연결되기 때문입니다.

+1

그건 생각입니다. IO 계산을 "언랩 (unwrap)"하고 다른 * IO 계산을 반환하는 함수에 전달하는 바인드 함수/연산자 ('>> =')가 있습니다. IO 계산을 "해제"하는 다른 방법이 없으므로 제거 할 수 없습니다. –

+0

@Ruben : 'unwrappable'=> 나중에 참조 할 수 있도록이 모나드는 IO가 닫힌 모나드가 아니라 열린 모나드라고합니다. 덕분에 –

+0

. 나는 그 용어를 몰랐다. – Ruben

7

사용자와 상호 작용하고 원격 서비스와 통신하려면 소프트웨어에 일종의 비 기능적 부분이 필요합니다.

많은 "기능 언어"(대부분의 리스프와 마찬가지로)는 순전히입니다. 부작용은 대부분의 상황에서 "낙담"하지만 여전히 부작용으로 행동 할 수 있습니다.

하스켈은 "순전히 기능적"이지만 IO 모나드를 통해 기능 외적인 작업을 수행 할 수 있습니다. 기본 개념은 순수 기능적 프로그램이 비 기능적 프로그램 (사용자가 작성하지 않고 환경의 일부 임)에 의해 평가되는 느린 데이터 구조를 방출한다는 것입니다. 이 데이터 구조 자체가 필수 프로그램이라고 주장 할 수 있습니다. 따라서 당신은 함수형 언어로 명령형 메타 프로그래밍을하는 것과 같습니다.

어떤 접근 방식을 "더 잘"무시하면 두 경우 모두 목표는 프로그램의 기능적 부분과 비 기능적 부분을 분리하고 가능한 한 많이 비 기능적 부분의 크기를 제한하는 것입니다. 기능 부품은 재사용 가능하고 테스트 가능하며 추론하기 쉽습니다.

+0

+1 : 흥미로운 관점. – gorsky

2

내가 아는 완전히 순수한 함수 언어는 C++의 템플릿 시스템입니다. Haskell은 프로그램의 필수 부분을 명시 적으로 만들어서 두 번째 자리를 차지합니다.

하스켈에서 프로그램은 변경 가능한 상태이지만 기능은 (거의 항상) 그렇지 않습니다. 당신은 순수한 프로그램의 99 %를 순수하게 유지하고 바깥 세계와 상호 작용하는 부분 만이 불순합니다. 따라서 함수를 테스트 할 때 부작용이 없다는 것을 알고 있습니다. 불순한 껍질을 가진 순수한 핵.

+0

나는 이것을 정리하는 데 도움이되는 내 자신의 동일한 결론에 도달하기 시작했다. –

+0

Haskell은 변경 가능한 상태를 가지고 있지 않습니다. "newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}"의 정의를 사용하여 "fake"하고 IO는 거의 동일한 정의를가집니다. –

1

대부분의 프로그램이 외부 세계에 영향을 미치므로 (데이터베이스에 파일 작성, 데이터 수정 ...) 프로그램 전체가 거의 부작용이 거의 없습니다. 학업 이외의 운동에도 노력할 필요가 없습니다.

그러나 프로그램은 빌딩 블록 (서브 루틴, 함수, 메소드, 원하는대로 호출)에서 어셈블되며 순수 함수는 매우 잘 작동하는 빌딩 블록을 만듭니다.

대부분의 함수형 프로그래밍 언어는 함수를 순수하게 만들 필요가 없지만, 훌륭한 함수 프로그래머는 참조 투명성의 이점을 얻기 위해 실현 가능성이 높고 실용적인만큼 많은 함수를 순수하게 만들려고합니다.

하스켈은 더 나아갑니다. 하스켈 프로그램의 모든 부분은 순수합니다 (적어도 "unsafePerformIO"와 같은 죄가없는 경우). 하스켈에서 작성한 모든 함수는 순수합니다.

부작용은 모나드를 통해 소개됩니다. 그들은 일종의 "쇼핑 목록 - 구매자"- 구분을 소개하는 데 사용할 수 있습니다. 기본적으로 프로그램은 쇼핑 목록 (단순한 데이터이며 순수한 방식으로 조작 할 수 있음)을 작성하는 반면 언어 런타임은 쇼핑 목록을 해석하고 효과적인 쇼핑을 수행합니다. 불완전한 코드는 컴파일러 - 작성자가 제공하는 반면, 모든 코드는 순수하고 등식 추론에 호의적입니다.

관련 문제