하스켈의 불순한 계산이 모나드로 모델링 된 이유에 대해 몇 가지 조언을 해 줄 수 있습니까?
나는 모나드가 단지 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)
)
우리는 모나드와 함께 일하고있었습니다
이제 우리가 한 일을 봅시다 :
- 우리는
(~~~) :: IO b -> (b -> IO c) -> IO c
두 개의 불순한 기능을 함께 묶는 연산자 를 정의했습니다. impurify :: a -> IO a
순수한 값을 불순으로 변환 하는 함수 를 정의했습니다 .
이제 우리는 확인하게 (>>=) = (~~~)
하고 return = impurify
, 볼? 모나드가 있습니다.
기술 노트
그것이 실제로 모나드임을 보장하기 위해 여전히 확인해야 할 몇 가지 공리가 있습니다.
-
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
-
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
-
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를 나타내는 가장 기능적인 방법이라는 것이 답입니다.