부작용을 순수하게 학업 적으로 처리 할 때 IO 모나드 패턴의 이점이 있습니까?


17

또 다른 FP + 부작용 질문에 대해 죄송하지만 기존 답변을 찾지 못했습니다.

함수형 프로그래밍에 대한 나의 (제한된) 이해는 상태 / 부작용이 최소화되고 상태 비 저장 로직과 분리되어 있어야한다는 것입니다.

또한 IO 모나드에 대한 Haskell의 접근 방식을 수집하여 나중에 실행하기 위해 컨테이너 자체에 상태 저장 작업을 래핑하여 프로그램 자체의 범위를 벗어난 것으로 간주합니다.

나는이 패턴을 이해하려고 노력하고 있지만 실제로는 파이썬 프로젝트에서 사용할 것인지 결정하기 때문에 하스켈의 특성을 피하고 싶습니다.

조잡한 예제 수신.

내 프로그램이 XML 파일을 JSON 파일로 변환하는 경우 :

def main():
    xml_data = read_file('input.xml')  # impure
    json_data = convert(xml_data)  # pure
    write_file('output.json', json_data) # impure

IO 모나드의 접근 방식이 효과적으로 수행되지 않습니까?

steps = list(
    read_file,
    convert,
    write_file,
)

그런 다음 실제로 해당 단계를 호출 하지 않고 통역관이 그렇게하도록함으로써 책임을 완전히 배제 합니까?

또는 다른 방법으로 작성하면 다음과 같습니다.

def main():  # pure
    def inner():  # impure
        xml_data = read_file('input.xml')
        json_data = convert(xml_data)
        write_file('output.json', json_data)
    return inner

그런 다음 다른 사람이 전화를 걸고 inner()직업이 완료되었다고 말하면 main()순수합니다.

전체 프로그램은 기본적으로 IO 모나드에 포함됩니다.

코드가 실제로 실행될 때 파일을 읽은 후의 모든 것이 해당 파일의 상태에 따라 달라 지므로 명령 구현과 동일한 상태 관련 버그로 여전히 고통받을 것이므로 이것을 유지할 프로그래머로서 실제로 무엇을 얻었습니까?

난 완전히의 혜택에 감사 감소분리 내가 그와 같은 명령형 버전을 구조화하는 이유 사실상 상태 동작을 : 출력에서, 침을 순수 물건을 할, 입력을 수집합니다. 바라건대 convert()완전히 순수하고 캐치 가능성, 스레드 안전성 등의 이점을 얻을 수 있기를 바랍니다 .

또한 모나 딕 유형이 특히 유사한 유형으로 작동하는 파이프 라인에서 유용 할 수 있지만 IO가 모나드를 사용해야하는 이유는 알 수 없습니다.

IO 모나드 패턴이 가져 오는 부작용을 처리하는 데 추가 이점이 있습니까?


1
이 비디오를 봐야 합니다 . 모나드의 불가사의는 Category Theory 또는 Haskell에 의지하지 않고 마침내 드러납니다. 모나드는 JavaScript로 사소하게 표현되며 Ajax의 주요 인 에이 블러 중 하나입니다. 모나드는 놀랍습니다. 그것들은 복잡성을 관리 할 수있는 엄청난 힘으로 거의 사소하게 구현 된 단순한 것입니다. 그러나 그것들을 이해하는 것은 놀라 울 정도로 어려우며, 대부분의 사람들은 일단 그 아하 순간을 가졌 으면 다른 사람들에게 설명 할 수있는 능력을 잃는 것처럼 보입니다.
Robert Harvey

좋은 비디오, 고마워 실제로 JS 소개에서 함수형 프로그래밍에 이르기까지이 내용에 대해 배웠습니다. 그것을 보았지만, 내 질문은 IOrock에 관한 것이 확실하다고 확신합니다 .Crock은 그 비디오에서 다루지 않습니다.
Stu Cox

흠 ... AJAX는 I / O의 형태로 간주되지 않습니까?
Robert Harvey

1
mainHaskell 프로그램 의 유형은 IO ()IO 액션입니다. 이것은 실제로 전혀 기능이 아닙니다. 그것은 A의 . 전체 프로그램은 언어 런타임에 수행 할 작업을 알려주는 지시 사항을 포함하는 순수한 값입니다. 모든 불완전한 것들 (실제로 IO 작업 수행)은 프로그램 범위를 벗어납니다.
Wyzard-하르 밍 모니카 중지-8

예를 들어, 모나 딕 부분은 한 계산의 결과를 가져 와서 ( read_file) 다음 계산 의 인수로 사용할 때 write_file입니다. 일련의 독립적 인 작업 만 수행했다면 Monad가 필요하지 않습니다.
lortabac

답변:


14

전체 프로그램은 기본적으로 IO 모나드에 포함됩니다.

그것은 당신이 Haskellers의 관점에서 그것을 보지 못한다고 생각하는 부분입니다. 그래서 우리는 다음과 같은 프로그램을 가지고 있습니다 :

module Main

main :: IO ()
main = do
  xmlData <- readFile "input.xml"
  let jsonData = convert xmlData
  writeFile "output.json" jsonData

convert :: String -> String
convert xml = ...

나는 전형적인 Haskeller가 이것을 취하는 것이 convert순수한 부분 이라고 생각합니다 .

  1. 아마도이 프로그램의 대부분 일 수도 있고 IO부분 보다 훨씬 더 복잡 할 수도 있습니다.
  2. IO전혀 처리하지 않고도 추론하고 테스트 할 수 있습니다 .

그들은으로이 표시되지 않습니다 그래서 convert것 "포함"에서 IO가 된 것으로, 오히려하지만, 고립 에서 IO. 유형에 convert따라 IO행동 에서 발생하는 모든 것에 절대 의존 할 수 없습니다 .

코드가 실제로 실행될 때 파일을 읽은 후의 모든 것은 해당 파일의 상태에 따라 달라 지므로 명령 구현과 동일한 상태 관련 버그로 여전히 고통받을 것이므로 이것을 유지할 프로그래머로서 실제로 무엇을 얻었습니까?

나는 이것이 두 가지로 나뉩니다.

  1. 프로그램이 실행되면 값 인수 에이 convert파일의 상태에 따라 달라집니다.
  2. 그러나 convert함수 의 기능 파일의 상태에 의존하지 않습니다. 다른 지점에서 다른 인수로 호출하더라도 convert항상 동일한 함수 입니다.

이것은 다소 추상적 인 점이지만, Haskellers가 이것에 대해 이야기 할 때 의미하는 것이 실제로 핵심입니다. 유효한 인수 convert가 주어지면 해당 인수에 대한 올바른 결과를 생성하는 방식 으로 작성하려고합니다 . 이런 식으로 볼 때 파일을 읽는 것이 상태 저장 작업이라는 사실은 방정식에 들어 가지 않습니다. 중요한 것은 인수가 무엇이든 어디에서 왔든간에 convert올바르게 처리해야한다는 것입니다. 그리고 순도 convert가 입력으로 할 수있는 일을 제한한다는 사실은 그 추론을 단순화합니다.

따라서 convert일부 인수에서 잘못된 결과를 생성 readFile하고 그러한 인수를 제공하면 state에 의해 발생한 버그로 볼 수 없습니다 . 순수한 기능의 버그입니다!


나는 이것이 최고의 설명이라고 생각합니다 (다른 사람들도 나를 위해 명확하게하는 데 도움이되었지만), 감사합니다.
Stu Cox

파이썬에서 모나드를 사용하면 파이썬이 하나의 (정적) 유형을 가지므로 아무런 이점을 얻지 못하므로 이점이 적을 수 있습니다.
jk.

7

"순전히 학문적"이라는 말의 의미를 정확히 이해하기는 어렵지만 대답은 대부분 "아니오"라고 생각합니다.

Simon Peyton Jones ( 강한 독서!) 의 어색한 분대 태클에서 설명했듯이 , 모나 딕 I / O는 Haskell이 I / O를 처리하는 데 사용한 실제 문제를 해결하기위한 것입니다. 여기서는 복사하지 않는 요청 및 응답이있는 서버의 예를 읽으십시오. 매우 유익합니다.

Haskell은 Python과 달리 유형 시스템에 의해 시행 될 수있는 "순수한"계산 스타일을 권장합니다. 물론,이 스타일을 따르기 위해 파이썬으로 프로그래밍 할 때 자기 훈련을 사용할 수 있지만, 작성하지 않은 모듈은 어떻습니까? 타입 시스템 (및 공통 라이브러리)의 도움이 없다면 모나 딕 I / O는 아마도 파이썬에서 덜 유용 할 것입니다. 언어의 철학은 엄격하고 순수한 분리를 강요하기위한 것이 아닙니다.

이것은 학문적 인 I / O가 어떻게 다른지에 대한 것보다 Haskell과 Python의 다른 철학에 대해 더 많은 것을 말해줍니다. 파이썬에는 사용하지 않을 것입니다.

다른 것. 당신은 말한다 :

전체 프로그램은 기본적으로 IO 모나드에 포함됩니다.

Haskell main함수는에 "존재"하는 IO것이 사실이지만 실제 Haskell 프로그램은 IO필요하지 않을 때마다 사용하지 않는 것이 좋습니다 . I / O를 수행 할 필요가없는 거의 모든 함수에는 type이 없어야합니다 IO.

그래서 마지막 예제에서 당신은 그것을 거꾸로했습니다 : main불순합니다 (파일을 읽고 쓰기 때문에) 불완전하지만 핵심 기능 convert은 순수합니다.


3

IO가 불완전합니까? 다른 시간에 다른 값을 반환 할 수 있기 때문입니다. 시간에 대한 종속성이 있어야 어떤 식 으로든를 차지 할 수는. 게으른 평가에서는 더욱 중요합니다. 다음 프로그램을 고려하십시오.

main = do  
    putStrLn "Please enter your name"  
    name <- getLine
    putStrLn $ "Hello, " ++ name

IO 모나드가 없으면 왜 첫 번째 프롬프트가 출력됩니까? 그것에 의존하는 것은 없기 때문에 게으른 평가는 결코 요구되지 않을 것임을 의미합니다. 입력을 읽기 전에 프롬프트를 출력하도록 강요하는 것도 없습니다. 컴퓨터와 관련하여 IO 모나드가없는 한 처음 두 표현식은 서로 완전히 독립적입니다. 다행히 name두 번째 명령을 내립니다.

순서 의존성 문제를 해결하는 다른 방법이 있지만 IO 모나드를 사용하는 것이 명령형 코드 섹션이 거의 없어도 모든 것이 게으른 기능 영역에 머 무르도록하는 가장 간단한 방법 일 것입니다 (최소한 언어 관점에서). 또한 가장 유연합니다. 예를 들어, 사용자 입력에 따라 런타임에 동적으로 IO 파이프 라인을 비교적 쉽게 구축 할 수 있습니다.


2

함수형 프로그래밍에 대한 나의 (제한된) 이해는 상태 / 부작용이 최소화되고 상태 비 저장 로직과 분리되어 있어야한다는 것입니다.

그것은 단지 기능적인 프로그래밍이 아닙니다. 그것은 보통 어떤 언어로든 좋은 생각입니다. 당신이 단위 테스트를 할 경우, 방법은 당신이 갈라 read_file(), convert()그리고 write_file()에도 불구하고 있기 때문에 자연스럽게 완벽하게 제공 convert()그것을 위해 테스트를 작성, 코드의 지금까지 가장 복잡하고 큰 부분으로되고 상대적으로 쉽다 : 사용자가 설정하는 데 필요한 모든 입력 매개 변수입니다 . 에 대한 테스트를 작성 read_file()하고 write_file()꽤 어렵다 생성 및 / 또는 전 기능을 호출 한 후 파일 시스템에 물건을 읽을 필요가 있기 때문에 (심지어 자체가 거의 사소한 수있는 기능하지만). 이상적으로는 이러한 기능을 너무 간단하게 만들어서 테스트하지 않는 것이 편하다고 느끼므로 많은 번거 로움을 덜 수 있습니다.

Python과 Haskell의 차이점은 Haskell에는 함수에 부작용이 없음을 증명할 수있는 형식 검사기가 있다는 것입니다. 파이썬에서는 실수로 파일 읽기 또는 쓰기 기능을 convert()(예 :)에 빠뜨리지 않기를 바랍니다 read_config_file(). 모나드 convert :: String -> String없이 선언 하거나 유사한 Haskell IO에서 형식 검사기는 입력 매개 변수에만 의존하는 순수 함수임을 보증합니다. 누군가가 convert구성 파일을 읽 도록 수정하려고 하면 함수의 순도를 깨뜨리고 있음을 나타내는 컴파일러 오류가 빠르게 나타납니다. (그리고 그들이 순도를 유지하면서 결과를 read_config_file밖으로 나아갈 수있을 정도로 합리적이기를 바랍니다 .)convertconvert

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