[haskell] 인덱스 된 모나드는 무엇입니까?

인덱스 된 모나드 는 무엇 이며이 모나드 의 동기 는 무엇입니까 ?

나는 그것이 부작용을 추적하는 데 도움이된다고 읽었습니다. 그러나 형식 서명과 문서는 나를 어디로도 인도하지 않습니다.

부작용 (또는 다른 유효한 예)을 추적하는 데 도움이 될 수있는 예는 무엇입니까?



답변

그 어느 때보 다 사람들이 사용하는 용어는 완전히 일치하지 않습니다. 모나드에 의해 영감을 받았지만 엄격하게 말하는 것은 그다지 중요한 개념이 다양합니다. “인덱싱 된 모나드”라는 용어는 이러한 개념을 특성화하는 데 사용되는 용어의 숫자 ( “모나드”및 “매개 변수화 된 모나드”(Atkey의 이름) 포함) 중 하나입니다. (관심이 있다면 또 다른 개념은 모노 이드로 색인 된 Katsumata의 “파라 메트릭 효과 모나드”입니다. 여기서 return은 중립적으로 색인되고 bind는 색인에 누적됩니다.)

먼저 종류를 확인합시다.

IxMonad (m :: state -> state -> * -> *)

즉, “계산”(또는 원하는 경우 “액션”이지만 “계산”을 고수하겠습니다)의 유형은 다음과 같습니다.

m before after value

어디 before, after :: statevalue :: *. 이 아이디어는 예측 가능한 상태 개념 을 가진 외부 시스템과 안전하게 상호 작용하는 수단을 포착하는 것입니다 . 하는 계산으로의 유형은 국가가해야 무엇을 알려줍니다 before, 그것은 실행 상태가 될 것입니다 무엇을 after그것을 실행하고 (정기적 인 모나드와 같은 *어떤 종류의) value계산이 생산의.

일반적인 비트와 조각은 *모나드와 state같고 도미노를 연주하는 것과 같습니다.

ireturn  ::  a -> m i i a    -- returning a pure value preserves state
ibind    ::  m i j a ->      -- we can go from i to j and get an a, thence
             (a -> m j k b)  -- we can go from j to k and get a b, therefore
             -> m i k b      -- we can indeed go from i to k and get a b

이렇게 생성 된 “Kleisli 화살표”(계산을 생성하는 함수)의 개념은 다음과 같습니다.

a -> m i j b   -- values a in, b out; state transition i to j

그리고 우리는 구성을 얻습니다

icomp :: IxMonad m => (b -> m j k c) -> (a -> m i j b) -> a -> m i k c
icomp f g = \ a -> ibind (g a) f

와 같은 적, 법률 정확히을 확인 ireturn하고 icomp우리의 카테고리를 제공

      ireturn `icomp` g = g
      f `icomp` ireturn = f
(f `icomp` g) `icomp` h = f `icomp` (g `icomp` h)

또는 코미디 가짜 C / Java / 무엇이든

      g(); skip = g()
      skip; f() = f()
{g(); h()}; f() = h(); {g(); f()}

왜 귀찮게? 상호 작용의 “규칙”을 모델링합니다. 예를 들어, 드라이브에 DVD가 없으면 DVD를 꺼낼 수없고 DVD가 이미있는 경우 드라이브에 DVD를 넣을 수 없습니다. 그래서

data DVDDrive :: Bool -> Bool -> * -> * where  -- Bool is "drive full?"
  DReturn :: a -> DVDDrive i i a
  DInsert :: DVD ->                   -- you have a DVD
             DVDDrive True k a ->     -- you know how to continue full
             DVDDrive False k a       -- so you can insert from empty
  DEject  :: (DVD ->                  -- once you receive a DVD
              DVDDrive False k a) ->  -- you know how to continue empty
             DVDDrive True k a        -- so you can eject when full

instance IxMonad DVDDrive where  -- put these methods where they need to go
  ireturn = DReturn              -- so this goes somewhere else
  ibind (DReturn a)     k  = k a
  ibind (DInsert dvd j) k  = DInsert dvd (ibind j k)
  ibind (DEject j)      k  = DEject j $ \ dvd -> ibind (j dvd) k

이를 통해 “기본”명령을 정의 할 수 있습니다.

dInsert :: DVD -> DVDDrive False True ()
dInsert dvd = DInsert dvd $ DReturn ()

dEject :: DVDrive True False DVD
dEject = DEject $ \ dvd -> DReturn dvd

다른 사람들이 ireturnibind. 이제 쓸 수 있습니다. (차용- do표기법)

discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dvd' <- dEject; dInsert dvd ; ireturn dvd'

그러나 물리적으로 불가능한 것은 아닙니다

discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dInsert dvd; dEject      -- ouch!

또는 기본 명령을 직접 정의 할 수 있습니다.

data DVDCommand :: Bool -> Bool -> * -> * where
  InsertC  :: DVD -> DVDCommand False True ()
  EjectC   :: DVDCommand True False DVD

그런 다음 일반 템플릿을 인스턴스화합니다.

data CommandIxMonad :: (state -> state -> * -> *) ->
                        state -> state -> * -> * where
  CReturn  :: a -> CommandIxMonad c i i a
  (:?)     :: c i j a -> (a -> CommandIxMonad c j k b) ->
                CommandIxMonad c i k b

instance IxMonad (CommandIxMonad c) where
  ireturn = CReturn
  ibind (CReturn a) k  = k a
  ibind (c :? j)    k  = c :? \ a -> ibind (j a) k

실제로, 우리는 원시 Kleisli 화살이 무엇인지 (하나의 “도미노”가 무엇인지) 말한 다음 그 위에 “계산 시퀀스”라는 적절한 개념을 구축했습니다.

인덱스 된 모든 모나드 m에 대해 “대각선 변경 없음” m i i은 모나드이지만 일반적으로m i j 으로 그렇지 않습니다. 또한 값은 색인화되지 않지만 계산은 색인화되므로 색인화 된 모나드는 다른 범주에 대해 인스턴스화 된 모나드의 일반적인 아이디어가 아닙니다.

자, Kleisli 화살의 종류를 다시보세요

a -> m i j b

우리는 i시작 하려면 상태 에 있어야한다는 것을 알고 있으며 모든 지속은 상태에서 시작될 것이라고 예측합니다 j. 우리는이 시스템에 대해 많이 알고 있습니다! 이것은 위험한 작업이 아닙니다! DVD를 드라이브에 넣으면 들어갑니다! DVD 드라이브는 각 명령 이후의 상태에 대해 어떠한 말도하지 않습니다.

그러나 그것은 일반적으로 세상과 상호 작용할 때 사실이 아닙니다. 때로는 통제권을 포기하고 세상이 좋아하는 일을하도록해야 할 수도 있습니다. 예를 들어, 서버 인 경우 클라이언트에게 선택권을 제공 할 수 있으며 세션 상태는 선택하는 항목에 따라 달라집니다. 서버의 “제공 선택”작업은 결과 상태를 결정하지 않지만 서버는 어쨌든 계속할 수 있어야합니다. 위의 의미에서 “기본 명령”이 아니므로 인덱스 된 모나드는 예측할 수없는 것을 모델링하는 좋은 도구가 아닙니다. 시나리오 .

더 나은 도구는 무엇입니까?

type f :-> g = forall state. f state -> g state

class MonadIx (m :: (state -> *) -> (state -> *)) where
  returnIx    :: x :-> m x
  flipBindIx  :: (a :-> m b) -> (m a :-> m b)  -- tidier than bindIx

무서운 비스킷? 두 가지 이유가 있습니다. 이 때문에 하나, 그것은, 모나드가 무엇인지와 같은 오히려 더 보이는 것입니다 이상 모나드 만 (state -> *)이 아니라 *. 둘째, Kleisli 화살의 종류를 보면

a :-> m b   =   forall state. a state -> m b state

Good Old Hoare Logic에서 와 같이 전제 조건 a 및 후 조건 으로 계산 유형을 얻습니다 b. 프로그램 논리에 대한 주장은 Curry-Howard 서신을 통과하고 Haskell 유형이되는 데 반세기 미만이 걸렸습니다. returnIx“건너 뛰기”에 대한 Hoare Logic 규칙 인 “아무것도하지 않고 유지되는 모든 사후 조건을 달성 할 수 있습니다.” 라는 유형입니다 . 해당 구성은 “;”에 대한 Hoare Logic 규칙입니다.

유형을 살펴보고 bindIx모든 수량자를 입력하여 마무리하겠습니다 .

bindIx :: forall i. m a i -> (forall j. a j -> m b j) -> m b i

이것들 forall은 반대 극성을 가지고 있습니다. 우리는 초기 상태 와 사후 조건으로 i시작할 수있는 계산을 선택합니다 . 세상은 자신이 좋아 하는 중간 상태를 선택 하지만 사후 조건이 유지 된다는 증거를 우리에게 제공해야하며 그러한 상태에서 우리는 계속 유지할 수 있습니다. 따라서 순서대로 state에서 조건 을 얻을 수 있습니다 . “이후”상태에 대한 그립을 해제함으로써 예측할 수없는 계산을 모델링 할 수 있습니다 .iajbbbi

모두 IxMonadMonadIx유용합니다. 두 모델 모두 상태 변화, 예측 가능 및 예측 불가능과 관련하여 상호 작용 계산의 유효성을 모델링합니다. 예측 가능성은 얻을 수있을 때 가치가 있지만 예측 불가능 성은 때때로 삶의 사실입니다. 바라건대,이 답변은 인덱스 모나드가 무엇인지에 대한 일부 표시를 제공하여 유용하기 시작하는시기와 중지되는시기를 모두 예측합니다.


답변

내가 아는 인덱스 모나드를 정의하는 방법은 최소한 세 가지가 있습니다.

나는 이러한 옵션을 색인 된 모나드 à la X 라고 부르겠습니다. X는 컴퓨터 과학자 인 Bob Atkey, Conor McBride 및 Dominic Orchard에 걸쳐 있습니다. 이 구성의 일부는 범주 이론을 통해 훨씬 더 긴 저명한 역사와 더 좋은 해석을 가지고 있지만 처음에는 이러한 이름과 관련된 것을 배웠고이 대답이 너무 난해 하지 않도록 노력하고 있습니다.

Atkey

Bob Atkey의 인덱스 된 모나드 스타일은 모나드의 인덱스를 처리하기 위해 2 개의 추가 매개 변수로 작업하는 것입니다.

이를 통해 사람들이 다른 답변에서 던진 정의를 얻을 수 있습니다.

class IMonad m where
  ireturn  ::  a -> m i i a
  ibind    ::  m i j a -> (a -> m j k b) -> m i k b

또한 Atkey의 색인화 된 comonads를 정의 할 수 있습니다. 나는 실제로 코드베이스 에서lens 많은 마일리지를 얻습니다 .

McBride

색인화 된 모나드의 다음 형태는 Conor McBride가 그의 논문 “Outrageous Fortune의 Kleisli Arrows” 에서 정의한 것 입니다. 대신 색인에 단일 매개 변수를 사용합니다. 이것은 인덱스 된 모나드 정의가 다소 영리한 형태를 갖도록합니다.

다음과 같이 매개 변수를 사용하여 자연 변환을 정의하면

type a ~> b = forall i. a i -> b i 

McBride의 정의를 다음과 같이 작성할 수 있습니다.

class IMonad m where
  ireturn :: a ~> m a
  ibind :: (a ~> m b) -> (m a ~> m b)

이것은 Atkey와는 상당히 다른 느낌이 들지만 모나드를 구축하는 대신 일반 모나드와 비슷하게 느껴 (m :: * -> *)집니다 (m :: (k -> *) -> (k -> *).

흥미롭게도 McBride는 그의 모방 할 수없는 스타일의 McBride가 “at key”로 읽어야한다고 선택하는 영리한 데이터 유형을 사용하여 McBride에서 색인화 된 모나드의 Atkey 스타일을 실제로 복구 할 수 있습니다.

data (:=) :: a i j where
   V :: a -> (a := i) i

이제 당신은 그것을 해결할 수 있습니다

ireturn :: IMonad m => (a := j) ~> m (a := j)

확장되는

ireturn :: IMonad m => (a := j) i -> m (a := j) i

j = i 일 때만 호출 할 수 있으며,을주의 깊게 읽으면 ibindAtkey의 ibind. 이러한 (: =) 데이터 구조를 전달해야하지만 Atkey 프레젠테이션의 힘을 회복합니다.

반면에 Atkey 프레젠테이션은 McBride 버전의 모든 사용을 복구 할만큼 강력하지 않습니다. 권력은 엄격하게 획득되었습니다.

또 다른 좋은 점은 McBride의 인덱스 된 모나드는 분명히 모나드라는 것입니다. 다른 펑터 범주의 모나드 일뿐입니다. 그것은 펑터의 범주 (k -> *)(k -> *)아닌 펑터의 범주에있는 endofunctor에 대해 작동 *합니다 *.

재미있는 연습은 색인 된 코 모나드에 대해 McBride에서 Atkey로 변환하는 방법을 알아내는 것 입니다. 개인적으로 McBride의 논문에서 “at key”구성에 데이터 유형 ‘At’을 사용합니다. 저는 실제로 ICFP 2013에서 Bob Atkey에게 다가 가서 그를 뒤집어 “코트”로 만들었다 고 말했습니다. 그는 눈에 띄게 혼란스러워 보였다. 내 머릿속에서 라인이 더 잘 연주되었습니다. =)

과수원

마지막으로, “인덱싱 된 모나드”라는 이름에 대해 거의 드물게 참조되는 세 번째 주장자는 Dominic Orchard에 기인합니다. 대신 그는 유형 수준의 모노 이드를 사용하여 인덱스를 합칩니다. 구성에 대한 세부 사항을 살펴보기보다는 간단히이 강연에 연결하겠습니다.

https://github.com/dorchard/effect-monad/blob/master/docs/ixmonad-fita14.pdf


답변

간단한 시나리오로 상태 모나드가 있다고 가정합니다. 상태 유형은 복잡한 대형 유형이지만 이러한 모든 상태는 빨간색과 파란색 상태의 두 세트로 분할 될 수 있습니다. 이 모나드의 일부 작업은 현재 상태가 파란색 상태 인 경우에만 의미가 있습니다. 이 중 일부는 상태를 파란색 ( blueToBlue)으로 유지하고 다른 일부는 상태를 빨간색 ( blueToRed)으로 유지합니다. 일반 모나드에서는 다음과 같이 작성할 수 있습니다.

blueToRed  :: State S ()
blueToBlue :: State S ()

foo :: State S ()
foo = do blueToRed
         blueToBlue

두 번째 작업이 파란색 상태를 예상하기 때문에 런타임 오류를 트리거합니다. 이것을 정적으로 방지하고 싶습니다. 인덱싱 된 모나드는이 목표를 달성합니다.

data Red
data Blue

-- assume a new indexed State monad
blueToRed  :: State S Blue Red  ()
blueToBlue :: State S Blue Blue ()

foo :: State S ?? ?? ()
foo = blueToRed `ibind` \_ ->
      blueToBlue          -- type error

두 번째 인덱스 인해 입력 오류 트리거 blueToRed( Red)의 첫번째 인덱스 다르다 blueToBlue( Blue).

또 다른 예로, 인덱스 된 모나드를 사용하면 상태 모나드가 상태에 대한 유형을 변경할 수 있습니다.

data State old new a = State (old -> (new, a))

위를 사용하여 정적으로 유형이 지정된 이기종 스택 인 상태를 빌드 할 수 있습니다. 작업에는 유형이 있습니다.

push :: a -> State old (a,old) ()
pop  :: State (a,new) new a

또 다른 예로, 파일 액세스를 허용하지 않는 제한된 IO 모나드가 필요하다고 가정합니다. 예를 들어 사용할 수 있습니다.

openFile :: IO any FilesAccessed ()
newIORef :: a -> IO any any (IORef a)
-- no operation of type :: IO any NoAccess _

이런 식 IO ... NoAccess ()으로 유형 이 있는 작업 은 정적으로 파일 액세스가 없음을 보장합니다. 대신 작업 유형 IO ... FilesAccessed ()이 파일에 액세스 할 수 있습니다. 인덱싱 된 모나드가 있다는 것은 제한된 IO에 대해 별도의 유형을 빌드 할 필요가 없음을 의미하며, 두 IO 유형 모두에서 파일과 관련이없는 모든 함수를 복제해야합니다.


답변

인덱싱 된 모나드는 예를 들어 상태 모나드와 같은 특정 모나드가 아니라 추가 유형 매개 변수가있는 모나드 개념의 일종의 일반화입니다.

“표준”모나드 값이 유형을 Monad m => m a갖는 반면, 인덱스 모나드의 값은 IndexedMonad m => m i j a어디에 i있고 j인덱스 유형이므로 i모나드 계산 시작과 계산 j끝의 인덱스 유형이 됩니다. 어떤 의미에서는 i일종의 입력 유형과 j출력 유형으로 생각할 수 있습니다 .

사용 State예로서, 상태 연산은 State s a입력의 상태를 유지 s연산 통하여 입력의 결과를 반환한다 a. 인덱싱 된 버전 IndexedState i j a은 계산 중에 상태가 다른 유형으로 변경 될 수있는 상태 저장 계산입니다. 초기 상태에는 유형 i과 상태가 있고 계산의 끝에는 유형이 j있습니다.

인덱싱 된 모나드를 일반 모나드에 사용하는 것은 거의 필요하지 않지만 경우에 따라 더 엄격한 정적 보장을 인코딩하는 데 사용할 수 있습니다.


답변

종속 유형 (예 : agda)에서 인덱싱이 어떻게 사용되는지 살펴 보는 것이 중요 할 수 있습니다. 이것은 인덱싱이 일반적으로 어떻게 도움이되는지 설명 할 수 있으며이 경험을 모나드로 변환 할 수 있습니다.

인덱싱을 사용하면 특정 유형 인스턴스 간의 관계를 설정할 수 있습니다. 그런 다음 그 관계가 유지되는지 확인하기 위해 몇 가지 값에 대해 추론 할 수 있습니다.

예를 들어 (agda에서) 일부 자연수가와 관련이 있음을 지정할 수 _<_있으며 유형은 어떤 숫자인지 알려줍니다. 그런 다음 일부 함수에 m < n.

또 다른 예로, 선택한 언어에 대한 충분한 인내와 컴파일러 지원이 주어지면 함수가 특정 목록이 정렬 된 것으로 가정하도록 인코딩 할 수 있습니다.

인덱싱 된 모나드는 부작용을보다 정확하게 관리하기 위해 종속 형 시스템이 수행하는 작업 중 일부를 인코딩 할 수 있습니다.


답변