[haskell] Haskell에서 부작용이 모나드로 모델링 된 이유는 무엇입니까?

하스켈의 불순한 계산이 모나드로 모델링 된 이유에 대해 몇 가지 조언을 해 줄 수 있습니까?

나는 모나드가 단지 4 개의 연산과의 인터페이스라는 것을 의미한다. 그래서 부작용을 모델링하는 이유는 무엇인가?



답변

함수에 부작용이 있다고 가정하십시오. 입력 및 출력 매개 변수로 생성되는 모든 효과를 취하면 함수는 외부 세계에 순수합니다.

따라서 불순한 기능

f' :: Int -> Int

우리는 고려에 RealWorld를 추가

f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the side effects,
-- then return the new world.

그런 다음 f다시 순수합니다. 매개 변수화 된 데이터 유형을 정의 type IO a = RealWorld -> (a, RealWorld)하므로 RealWorld를 여러 번 입력 할 필요가 없으며 작성 만 가능합니다.

f :: Int -> IO Int

프로그래머에게는 RealWorld를 직접 처리하는 것이 너무 위험합니다. 특히 프로그래머가 RealWorld 유형의 값에 손을 대면 복사를 시도 할 수 있으며 기본적으로 불가능합니다. (예를 들어, 전체 파일 시스템을 복사하려고한다고 생각하십시오. 어디에 두겠습니까?) 따라서 IO에 대한 정의는 전 세계의 상태도 캡슐화합니다.

“불완전한”기능의 구성

이러한 불순한 기능은 서로 연결할 수 없다면 쓸모가 없습니다. 치다

getLine     :: IO String            ~            RealWorld -> (String, RealWorld)
getContents :: String -> IO String  ~  String -> RealWorld -> (String, RealWorld)
putStrLn    :: String -> IO ()      ~  String -> RealWorld -> ((),     RealWorld)

우리는하고 싶다

  • 콘솔에서 파일 이름을 습니다.
  • 그 파일을 읽고
  • 해당 파일의 내용을 콘솔에 인쇄 하십시오.

실제 상태에 접근 할 수 있다면 어떻게해야할까요?

printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
                       (contents, world2) = (getContents filename) world1 
                   in  (putStrLn contents) world2 -- results in ((), world3)

여기서 패턴이 보입니다. 함수는 다음과 같이 호출됩니다.

...
(<result-of-f>, worldY) = f               worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...

따라서 ~~~바인딩 할 연산자 를 정의 할 수 있습니다.

(~~~) :: (IO b) -> (b -> IO c) -> IO c

(~~~) ::      (RealWorld -> (b,   RealWorld))
      ->                    (b -> RealWorld -> (c, RealWorld))
      ->      (RealWorld                    -> (c, RealWorld))
(f ~~~ g) worldX = let (resF, worldY) = f worldX
                   in g resF worldY

우리는 간단히 쓸 수 있습니다

printFile = getLine ~~~ getContents ~~~ putStrLn

현실 세계를 건드리지 않고

“불순 화”

이제 파일 내용을 대문자로 만들고 싶다고 가정 해 봅시다. 대문자는 순수한 기능입니다

upperCase :: String -> String

그러나 그것을 현실 세계로 만들기 위해서는를 돌려 주어야합니다 IO String. 그러한 기능을 들어 올리는 것은 쉽습니다.

impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)

이것은 일반화 될 수 있습니다 :

impurify :: a -> IO a

impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)

그래서 impureUpperCase = impurify . upperCase, 우리는 쓸 수 있습니다

printUpperCaseFile = 
    getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn

(참고 : 일반적으로 작성 getLine ~~~ getContents ~~~ (putStrLn . upperCase))

우리는 모나드와 함께 일하고있었습니다

이제 우리가 한 일을 봅시다 :

  1. 우리는 (~~~) :: IO b -> (b -> IO c) -> IO c두 개의 불순한 기능을 함께 묶는 연산자 를 정의했습니다.
  2. impurify :: a -> IO a순수한 값을 불순으로 변환 하는 함수 를 정의했습니다 .

이제 우리는 확인하게 (>>=) = (~~~)하고 return = impurify, 볼? 모나드가 있습니다.


기술 노트

그것이 실제로 모나드임을 보장하기 위해 여전히 확인해야 할 몇 가지 공리가 있습니다.

  1. return a >>= f = f a

     impurify a                =  (\world -> (a, world))
    (impurify a ~~~ f) worldX  =  let (resF, worldY) = (\world -> (a, world )) worldX 
                                  in f resF worldY
                               =  let (resF, worldY) =            (a, worldX)       
                                  in f resF worldY
                               =  f a worldX
    
  2. f >>= return = f

    (f ~~~ impurify) worldX  =  let (resF, worldY) = f worldX 
                                in impurify resF worldY
                             =  let (resF, worldY) = f worldX      
                                in (resF, worldY)
                             =  f worldX
    
  3. f >>= (\x -> g x >>= h) = (f >>= g) >>= h

    운동으로 떠났다.


답변

하스켈의 불순한 계산이 모나드로 모델링 된 이유에 대해 누군가가 조언을 해 줄 수 있습니까?

이 질문에는 광범위한 오해가 포함되어 있습니다. 불순물과 Monad는 독립적 인 개념입니다. 불순물은 Monad에 의해 모델링 되지 않았습니다 . 대신, IO명령형 계산을 나타내는와 같은 몇 가지 데이터 유형이 있습니다 . 이러한 유형 중 일부의 경우 인터페이스의 작은 부분이 “Monad”라는 인터페이스 패턴에 해당합니다. 더욱이, 순수한 / 기능적 / 지표 적 설명은 알려져 있지 않으며 IO( “sin bin”의 목적을 고려하여 하나도 없을 것임 IO), World -> (a, World)의 의미에 대한 일반적인 이야기 가 IO a있습니다. 이야기는 정직하게 설명 할 수 IO있기 때문에IO동시성과 비결 정성을 지원합니다. 이야기는 세상과의 중간 계산 상호 작용을 허용하는 결정 론적 계산에 대해서는 효과가 없습니다.

자세한 설명은 이 답변을 참조하십시오 .

편집 : 질문을 다시 읽을 때 내 대답이 제대로 진행되고 있다고 생각하지 않습니다. 명령형 계산 모델은 종종 질문이 말한 것처럼 모나드로 판명됩니다. asker는 모나드가 어떤 식 으로든 명령형 계산의 모델링을 가능하게한다고 가정하지 않을 수도 있습니다.


답변

내가 이해할 때, Eugenio Moggi 라는 사람은 처음에는 “모 노드”라고 불리는 이전에 모호한 수학적 구성이 컴퓨터 언어로 부작용을 모델링하는 데 사용될 수 있다는 것을 알아 차렸다. 따라서 Lambda 미적분을 사용하여 의미를 지정한다. Haskell이 개발 될 때 불완전한 계산이 모델링되는 다양한 방법이 있었지만 (자세한 내용은 Simon Peyton Jones의 “헤어 셔츠”논문 참조) Phil Wadler가 모나드를 도입했을 때 이것이 바로 The Answer라는 것이 명백해졌습니다. 그리고 나머지는 역사입니다.


답변

하스켈의 불순한 계산이 모나드로 모델링 된 이유에 대해 누군가가 조언을 해 줄 수 있습니까?

음, 하스켈 때문에 순수 . 유형 수준 에서 순수하지 않은 계산순수한 계산 을 구분 하고 각각 프로그램 흐름 을 모델링 하려면 수학적 개념이 필요합니다 .

IO a, 부정확 한 계산을 모델링하는 일부 유형 을 사용해야합니다. 그런 다음의 방법을 알 필요가 결합 하는 이러한 계산 순서에 따라 적용을 ( >>=)하고 값을 올려 ( return) 가장 분명하고 기본적인 것들이다.

이 두 가지로 이미 모나드를 정의했습니다 (생각조차하지 않음).)

또한 모나드는 매우 일반적이고 강력한 추상화 를 제공하므로 많은 종류의 제어 흐름을 sequence, liftM특수 구문 과 같은 모나 딕 함수로 편리하게 일반화 할 수 있어 특별한 경우가 아닌 불쾌감을 줄 수 있습니다.

자세한 내용 은 함수형 프로그래밍고유성 입력 (내가 아는 유일한 대안)의 모나드 를 참조하십시오.


답변

아시다시피 Monad, 매우 간단한 구조입니다. 답의 절반은 다음과 같습니다. Monad부작용을 일으켜 함수를 사용할 수있는 가장 간단한 구조입니다. 함께 Monad우리는이 일을 할 수있다 : 우리는 사이드 초래 값 (같은 순수 값을 처리 할 수있다 return), 우리는 새로운 측면 초래 값을 얻을 수있는 측면 초래 값 사이드 초래 기능을 적용 할 수 있습니다 ( >>=). 이 두 가지 중 하나를 수행 할 수있는 능력을 잃어 버리면 피해를 입게 될 것입니다. 따라서 부작용 유형은 “적어도”이어야 하며 지금까지 필요한 모든 것을 구현 Monad하기 Monad에 충분합니다.

나머지 절반은 : “가능한 부작용”에 우리가 줄 수있는 가장 상세한 구조는 무엇입니까? 우리는 모든 가능한 부작용의 공간을 집합으로 생각할 수 있습니다 (필요한 유일한 작업은 회원 자격입니다). 우리는 두 가지 부작용을 하나씩 차례로 수행하여 다른 부작용을 일으킬 수 있으며, 이것은 다른 부작용을 초래할 것입니다 (또는 첫 번째가 “종료 컴퓨터”이고 두 번째가 “파일 쓰기”인 경우 결과는 동일 할 것입니다) 이 구성은 “종료 컴퓨터”입니다.

자,이 작업에 대해 무엇을 말할 수 있습니까? 연관성이 있습니다. 즉, 우리가 세 가지 부작용을 결합하면 결합 순서는 중요하지 않습니다. 파일을 작성한 다음 소켓을 읽은 다음 컴퓨터를 종료하면 파일을 쓴 다음 소켓을 읽은 다음 종료하는 것과 같습니다 컴퓨터). 그러나 ( “파일 쓰기”와 “파일 삭제”)는 ( “파일 삭제”와 “파일 쓰기”)와는 다른 부작용입니다. 그리고 우리는 정체성을 가지고 있습니다 : “부작용 없음”효과가있는 특수 부작용 ( “부작용 없음”, “파일 삭제”는 “파일 삭제”와 같은 부작용입니다))이 시점에서 모든 수학자는 “그룹!” 그러나 그룹에는 역수가 있으며 일반적으로 부작용을 반전시킬 수있는 방법은 없습니다. “파일 삭제” 돌이킬 수 없습니다. 우리가 남긴 구조는 monoid의 구조입니다. 이것은 우리의 부작용이 모나드 여야한다는 것을 의미합니다.

더 복잡한 구조가 있습니까? 확실한! 가능한 부작용을 파일 시스템 기반 효과, 네트워크 기반 효과 등으로 나눌 수 있으며 이러한 세부 사항을 보존하는보다 정교한 구성 규칙을 만들 수 있습니다. 그러나 다시 말하지만, 그것은 Monad매우 간단하지만 우리가 관심있는 대부분의 속성을 표현할만큼 강력합니다. (특히 연관성 및 기타 공리로 인해 응용 프로그램의 부작용이 조각의 부작용 조합과 동일하다는 확신을 가지고 작은 조각으로 응용 프로그램을 테스트 할 수 있습니다).


답변

실제로 기능적인 방식으로 I / O를 생각하는 것은 매우 깨끗한 방법입니다.

대부분의 프로그래밍 언어에서는 입력 / 출력 작업을 수행합니다. Haskell 에서 작업 을 수행 하는 것이 아니라 원하는 작업 목록을 생성하는 코드 작성을 상상해보십시오 .

Monads는 정확히 그에 대한 구문입니다.

왜 모나드가 다른 것과 반대되는지 알고 싶다면, 사람들이 하스켈을 만들 때 생각할 수있는 I / O를 나타내는 가장 기능적인 방법이라는 것이 답입니다.


답변

AFAIK, 이유는 유형 시스템에 부작용 검사를 포함 할 수 있기 때문입니다. 자세한 내용을 보려면 SE-Radio 에피소드를 시청하십시오 : 에피소드 108 : 기능 프로그래밍에 관한 Simon Peyton Jones 및 에피소드 72 : LINQ의 Erik Meijer