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


98

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

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

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

답변:


123

그 어느 때보 다 사람들이 사용하는 용어는 완전히 일치하지 않습니다. 모나드에 의해 영감을 받았지만 엄격하게 말하는 것은 그다지 중요한 개념이 다양합니다. "인덱싱 된 모나드"라는 용어는 이러한 개념을 특성화하는 데 사용되는 용어의 숫자 ( "모나드"및 "매개 변수화 된 모나드"(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유용합니다. 두 모델 모두 상태 변화, 예측 가능 및 예측 불가능과 관련하여 상호 작용 계산의 유효성을 모델링합니다. 예측 가능성은 얻을 수있을 때 가치가 있지만 예측 불가능 성은 때때로 삶의 사실입니다. 바라건대,이 답변은 인덱스 모나드가 무엇인지에 대한 일부 표시를 제공하여 유용하기 시작하는시기와 중지되는시기를 모두 예측합니다.


1
True/ False값을 유형 인수로 DVDDrive어떻게 전달할 수 있습니까? 그것은 확장입니까, 아니면 부울이 실제로 여기에 입력됩니까?
Bergi

8
@Bergi 부울이 유형 수준에 존재하도록 "리프트"되었습니다. 이것은 DataKinds확장을 사용하는 Haskell에서 그리고 의존적으로 유형화 된 언어로 가능합니다 ... 글쎄요, 그게 전부입니다.
J. Abrahamson 2015

MonadIx예를 들어 에서 조금 확장 할 수 있습니까? 이론적 근거에서 더 좋습니까, 아니면 실제 적용에 더 좋습니까?
Christian Conkle 2015

2
@ChristianConkle 나는 그것이별로 도움이되지 않는다는 것을 알고 있습니다. 그러나 당신은 정말로 완전히 다른 질문을 제기합니다. 로컬에서 MonadIx가 "더 좋음"이라고 말하면 예측할 수없는 환경과의 상호 작용을 모델링하는 맥락에서 의미합니다. 당신의 dvd 드라이브가 dvd를 뱉어내는 것이 허용된다면 당신이 그들을 삽입하려고 할 때 좋아하지 않습니다. 일부 실제 상황은 그것만큼 나쁘게 행동합니다. 다른 것들은 더 많은 예측 가능성을 가지고 있습니다 (즉, 작업이 실패하지 않는 것이 아니라 어떤 상태에서 연속이 시작되는지 말할 수 있음을 의미합니다).이 경우 IxMonad를 사용하는 것이 더 쉽습니다.
pigworker

1
대답에서 do 표기법을 "차용"할 때 RebindableSyntax확장 이있는 실제로 유효한 구문이라고 말하는 것이 유용 할 수 있습니다 . 다른 필수 확장에 대한 언급은 앞서 언급 한 것처럼 좋을 것입니다DataKinds
기가 바이트

46

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

나는 이러한 옵션을 색인 된 모나드 à 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


1
우리가 endomorphism monoid를 취하여 전자에서 후자로 갈 수 있고 상태 전환에서 monoidal appends를 인코딩하는 CPS로 뒤로 갈 수 있기 때문에 Orchard의 모나드는 Atkey와 동일하다는 것이 맞습니까?
András Kovács 2015

그것은 나에게 그럴듯하게 들린다.
Edward KMETT

즉, 그가 ICFP 2013에서 저에게 한 말을 바탕으로 Orchard는 그의 유형 가족이 일부 화살표가 연결되지 않는 임의의 범주가 아닌 실제 모노 이드처럼 행동하도록 의도했다고 믿습니다. 그보다 Atkey의 구성을 사용하면 일부 Kleisli 작업이 다른 작업과 연결되는 것을 쉽게 제한 할 수 있습니다. 여러면에서 그것이 바로 그것과 McBride 버전의 핵심입니다.
Edward KMETT

2
"주의 깊게 읽는"내용을 확장하려면 다음과 같이하십시오 . ibindalias 유형을 소개하십시오 Atkey m i j a = m (a := j) i. 이것을 mAtkey의 정의 로 사용하면 우리가 검색하는 두 개의 서명 인 ireturnAtkin :: a -> m (a := i) iibindAtkin :: m (a := j) i -> (a -> m (b := k) j) -> m (b := k) i. 첫 번째는 구성으로 얻습니다 ireturn . V. 두 번째는 (1) forall j. (a := j) j -> m (b := k) j패턴 일치 로 함수 를 빌드 한 다음 복구 된 a것을의 두 번째 인수에 전달합니다 ibindAtkin.
WorldSEnder

23

간단한 시나리오로 상태 모나드가 있다고 가정합니다. 상태 유형은 복잡한 대형 유형이지만 이러한 모든 상태는 빨간색과 파란색 상태의 두 세트로 분할 될 수 있습니다. 이 모나드의 일부 작업은 현재 상태가 파란색 상태 인 경우에만 의미가 있습니다. 이 중 일부는 상태를 파란색 ( 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 유형 모두에서 파일과 관련이없는 모든 함수를 복제해야합니다.


18

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

"표준"모나드 값이 유형을 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있습니다.

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


5

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

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

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

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

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

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