48 시간 내에 Write Yourself a Scheme (최대 85 시간)을 통해 작업 중이며 Add Variables and Assignments에 대한 부분에 도달했습니다 . 이 장에는 큰 개념적 점프가 있으며, 최종 솔루션으로 바로 점프하는 것보다 중간에 좋은 리팩토링을 통해 두 단계로 수행 되었으면합니다. 어쨌든…
나는 같은 목적을 제공하는 것 다른 클래스의 번호와 분실 들어 왔 : State
, ST
, IORef
,와 MVar
. 처음 세 개는 텍스트에 언급되어 있지만 마지막 세 개는 처음 세 개에 대한 많은 StackOverflow 질문에 대한 선호하는 답변 인 것 같습니다. 그들은 모두 연속 호출 사이에 상태를 가지고있는 것 같습니다.
이들 각각은 무엇이며 서로 어떻게 다릅니 까?
특히 다음 문장은 의미가 없습니다.
대신 우리는 상태 스레드 라는 기능을 사용 하여 Haskell이 집계 상태를 관리 할 수 있도록합니다. 이를 통해 변수를 가져 오거나 설정하는 함수를 사용하여 다른 프로그래밍 언어 에서처럼 가변 변수를 처리 할 수 있습니다.
과
IORef 모듈을 사용하면 IO 모나드 내에서 상태 저장 변수를 사용할 수 있습니다 .
이 모든 것이 라인을 type ENV = IORef [(String, IORef LispVal)]
혼란스럽게 만듭니다 . 두 번째 이유는 IORef
무엇입니까? type ENV = State [(String, LispVal)]
대신 쓰면 무엇이 깨질 까요?
답변
State Monad : 변경 가능한 상태 모델
State 모나드는 상태가있는 프로그램을위한 순전히 기능적인 환경이며 간단한 API를 사용합니다.
- 가져 오기
- 놓다
mtl 패키지의 문서 .
State 모나드는 일반적으로 단일 제어 스레드에서 상태가 필요할 때 사용됩니다. 실제로 구현시 변경 가능한 상태를 사용하지 않습니다. 대신, 프로그램은 상태 값에 의해 매개 변수화됩니다 (즉, 상태는 모든 계산에 대한 추가 매개 변수 임). 상태는 단일 스레드에서만 변경된 것처럼 보입니다 (스레드간에 공유 할 수 없음).
ST 모나드와 STRef
ST 모나드는 IO 모나드의 제한된 사촌입니다.
머신에서 실제 변경 가능한 메모리로 구현되는 임의의 변경 가능한 상태를 허용 합니다. API는 부작용이없는 프로그램에서 안전합니다. rank-2 유형 매개 변수는 변경 가능한 상태에 의존하는 값이 로컬 범위를 이스케이프하는 것을 방지하기 때문입니다.
따라서 순수한 프로그램에서 제어 된 가변성을 허용합니다.
일반적으로 변경된 후 고정되는 가변 배열 및 기타 데이터 구조에 사용됩니다. 또한 변경 가능한 상태가 “하드웨어 가속”이기 때문에 매우 효율적입니다.
기본 API :
- Control.Monad.ST
- runST-새로운 메모리 효과 계산을 시작합니다.
- 및 STRefs : (로컬) 변경 가능한 셀에 대한 포인터.
- ST 기반 배열 (예 : 벡터)도 일반적입니다.
IO 모나드의 덜 위험한 형제라고 생각하십시오. 또는 메모리에 읽고 쓰기 만 할 수있는 IO.
IORef : IO의 STRef
이들은 IO 모나드의 STRef (위 참조)입니다. 지역에 대한 STRef와 동일한 안전 보장이 없습니다.
MVars : 잠금이있는 IORef
STRef 또는 IORef와 비슷하지만 잠금이 연결되어 여러 스레드에서 안전하게 동시 액세스 할 수 있습니다. IORef 및 STRef는 atomicModifyIORef
(비교 및 교체 원자 연산)을 사용할 때 다중 스레드 설정에서만 안전 합니다. MVar는 변경 가능한 상태를 안전하게 공유하기위한보다 일반적인 메커니즘입니다.
일반적으로 Haskell에서는 STRef 또는 IORef를 통해 MVars 또는 TVars (STM 기반 가변 셀)를 사용합니다.
답변
좋습니다 IORef
. 시작하겠습니다 . IORef
IO 모나드에서 변경 가능한 값을 제공합니다. 일부 데이터에 대한 참조 일 뿐이며 다른 참조와 마찬가지로 참조하는 데이터를 변경할 수있는 함수가 있습니다. Haskell에서 이러한 모든 함수는 IO
. 데이터베이스, 파일 또는 기타 외부 데이터 저장소처럼 생각할 수 있습니다. 데이터를 가져오고 설정할 수 있지만 그렇게하려면 IO를 거쳐야합니다. IO가 필요한 이유는 Haskell이 순수 하기 때문입니다 . 컴파일러는 주어진 시간에 참조가 어떤 데이터를 가리키는 지 알 수있는 방법이 필요합니다 ( sigfpe의 “You could have invented monads” 블로그 게시물 참조).
MVar
s는 두 가지 매우 중요한 차이점을 제외하고 기본적으로 IORef와 동일합니다. MVar
동시성 프리미티브이므로 여러 스레드에서 액세스하도록 설계되었습니다. 두 번째 차이점은 an MVar
이 가득 차거나 비어있을 수있는 상자라는 것입니다. 따라서 a가 IORef Int
항상있는 경우 Int
(또는 맨 아래 인 경우) MVar Int
an이 Int
있거나 비어있을 수 있습니다. 스레드가 비어있는 값을 읽으려고하면 (다른 스레드에 의해) 채워질 MVar
때까지 차단됩니다 MVar
. 기본적으로 an MVar a
은 IORef (Maybe a)
동시성에 유용한 추가 의미 체계 가있는 것과 동일 합니다.
State
반드시 IO가 아닌 변경 가능한 상태를 제공하는 모나드입니다. 실제로 순수한 계산에 특히 유용합니다. 당신이 상태를 사용하지만 알고리즘을 경우 IO
하는 State
모나드는 종종 우아한 솔루션입니다.
State의 모나드 변환기 버전도 있습니다 StateT
. 이것은 프로그램 구성 데이터 또는 응용 프로그램에서 “게임 세계 상태”유형의 상태를 유지하는 데 자주 사용됩니다.
ST
약간 다른 것입니다. 의 주요 데이터 구조는 ST
입니다 .는와 STRef
비슷 IORef
하지만 모나드가 다릅니다. ST
모나드가 사용하는 형식 시스템의 속임수 변경 가능한 데이터가 모나드를 벗어날 수 있도록 (이하 “주 스레드”워드 프로세서 언급) 즉, ST 계산을 실행할 때 순수한 결과를 얻습니다. ST가 흥미로운 이유는 IO와 같은 원시 모나드이기 때문에 계산이 바이트 배열 및 포인터에 대해 저수준 조작을 수행 할 수 있기 때문입니다. 즉 ST
, 가변 데이터에 저수준 작업을 사용하면서 순수한 인터페이스를 제공 할 수 있습니다. 즉, 매우 빠릅니다. 프로그램의 관점에서 보면 마치 ST
스레드 로컬 저장소가있는 별도의 스레드에서 계산이 실행되는 것과 같습니다.
답변
다른 사람들은 핵심 작업을 수행했지만 직접적인 질문에 답하기 위해
이 모든 것이 선 유형을
ENV =
IORef [(String, IORef LispVal)]
혼란스럽게 만듭니다 . 왜 두 번째 IORef입니까?type ENV = State
대신 수행하면 무엇이 깨질 까요?
[(String, LispVal)]
Lisp는 변경 가능한 상태와 어휘 범위를 가진 기능적 언어입니다. 가변 변수에 대해 닫았다 고 상상해보십시오. 이제이 변수에 대한 참조가 다른 함수 (예 : (하스켈 스타일 의사 코드에서)) 내부에 매달려 있습니다 (printIt, setIt) = let x = 5 in (\ () -> print x, \y -> set x y)
. 이제 두 개의 함수가 있습니다. 하나는 x를 인쇄하고 다른 하나는 값을 설정합니다. 평가할 때 printIt
, 당신은 조회 할 이름 있는 초기 환경에서 X의 printIt
정의를, 그러나 당신은 조회 할 값 이름이있는 환경에서 결합되어 printIt
있다 라는를 한 후 ( setIt
여러 번 호출 된 수도 ).
이 작업을 수행하기 위해 두 개의 IORef를 사용하는 방법이 있지만 제안한 후자 유형보다 더 많은 것이 필요하므로 어휘 범위 방식으로 이름이 바인딩 된 값을 변경할 수 없습니다. 많은 흥미로운 선사 시대를위한 “funargs 문제”를 구글에 올려 놓으십시오.