2013-08-28 4 views
6

은 왜 대신 현재리프트의 반환 값이 모나드로 제한되지 않는 이유는 무엇입니까?

class MonadTrans t where 
    lift :: Monad m => m a -> t m a 

이 (Why aren't monad transformers constrained to yield monads?의 제안과는 달리) 하스켈 98의

class MonadTrans t where 
    lift :: (Monad m, Monad (t m)) => m a -> t m a 
--     ^^^^^^^^^^^ 

로 정의하고 그 결과는 항상있을 것이라는 점을 보장하지 MonadTrans한다 모나드. 모나드 변압기가 모나드가 아닌 무언가를 생산하도록 허용되는 이유가 있습니까?

+1

왜 결과가 모나드인지 확인 하시겠습니까? 그것은 엄격히 일반적이지 않으며, 나는 그것으로부터 어떤 이익도 보지 못합니다. –

+3

@ JohnL 주로''MonadTrans' laws (http://hackage.haskell.org/packages/archive/transformers/0.3.0.0/doc/html/Control-Monad-Trans-Class.html#t:MonadTrans) 모나드로 표현되므로 결과는 모나드 여야합니다. 그것이 아니라면, 법을 표현할조차 수 없습니다. 그러나 bheklilr은 그의 답변에서 좋은 지적을했고, 그것을 바탕으로 나는 "Monad"-> "Applicative"변압기를 생산하는 [예제] (http://stackoverflow.com/a/18495255/1333025)를 만들었습니다. 그러나 이것은 우리가 다른 법칙들을 공식화 할 필요가 있음을 의미합니다. –

답변

4

내 생각은 MonadTrans 대신 MonadMonad를 변환, 뭔가로 Monad 변환이다. Monad을 변환하는 것을 쓰고 lift을 정의 할 수 있기 때문에 더 일반화되었지만 >>=return을 정의 할 수는 없습니다. 대부분의 (전부는 아닐지라도) MonadTrans 인스턴스가 Monad으로 끝나기 때문에 컴파일러가 여전히 잘 처리하므로 실제로는 문제가되지 않습니다.

+0

개인적인 지식은 없지만, 이것이 이유라고 생각합니다. 'Monad'가 아닌'Functor' 나'Applicative'에 대해'MonadTrans' 인스턴스를 원할 수도 있습니다. –

+0

@ JohnL 이것은 정확하게 나의 추론이었다. 어떤 출처도 모르기 때문에 (누군가가 그 문서를 자유롭게 게시 할 수 있다면) MonadTrans가 두 개가 아닌 하나의 모나드에서만 작동한다는 것이 합리적입니다. – bheklilr

6

bheklilr의 대답은 나에게 모나드 변압기는 모나드없는 무언가를 생성하는 예를의 생각을했다. 모나드가 아닌 것으로 잘 알려진 예는 ZipList입니다. 각 레벨에서 모나드 액션을 실행하는 변형을 만들 수 있습니다.

import Control.Applicative 
import Control.Arrow ((***)) 
import Control.Monad 
import Control.Monad.Trans 

-- | A list where each step is produced by a monadic action. 
data ListT m a = Nil | Cons (m (a, ListT m a)) 

실제로 이것은 모나드 스트림입니다. 그리고 그것은 쉽게 Functor로 할 수 있으며, 분명히 Applicative

instance Monad m => Functor (ListT m) where 
    fmap f Nil  = Nil 
    fmap f (Cons k) = Cons $ (f *** fmap f) `liftM` k 
instance Monad m => Applicative (ListT m) where 
    pure x = Cons $ return (x, pure x) 
    Cons mf <*> Cons mx = Cons $ do 
     (f, fs) <- mf 
     (x, xs) <- mx 
     return (f x, fs <*> xs) 
    _  <*> _  = Nil 

하지만 모나드. 따라서 모나드를 Applicative으로 변환하는 MonadTrans 인스턴스가 있습니다.

instance MonadTrans ListT where 
    lift mx = Cons $ (\x -> (x, lift mx)) `liftM` mx 

(이 모든 것은 나를 실험 ZipSink 도관-추가도 좋은 예입니다 것을 깨닫게했다.)


그러나, 이것은 또 다른 질문이 제기 : 우리는 원하는 경우 변압기, 그들은 어떤 법칙을 고수해야합니까? MonadTrans에 대한 법률은 그래서 우리의 경우 우리가

lift (f `liftM` x) = fmap f (lift x) 

lift . return  = pure 
lift (m `ap` f)  = lift m <*> lift f 
+0

'ListT'를 하나의 마지막 모나드 레이어로 감싸는 경우 모나드, 특히 [ "ListT done right"] (http://www.haskell.org/haskellwiki/ListT_done_right)에주의하십시오. –

+0

@GabrielGonzalez 예, 'ListT'를 기억하는 것이 제 영감이었습니다. 하지만 제 ListT는''ZipList' (http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Applicative.html)와 같이''pure''와''<*>''을 다르게 정의합니다. #t : ZipList)는'[]'과 다릅니다. 따라서 유효한 모나드 인스턴스가 없습니다. –

3

내가 그 말을 다른 두 답변에 동의하지 않을거야처럼 뭔가를 할 수

lift . return = return 
lift (m >>= f) = lift m >>= (lift . f) 

으로 정의된다 결과는 모나드 여야합니다. 그 이유는 그렇지 않다면 lift이 지켜야 할 합리적인 법칙이 없다는 것입니다.

: 당신은 그들이 두 Kleisli 범주 사이 펑터 법을 깨닫게 될 때 이러한 법률이 더 의미가

lift (return x) = return x 

lift (m >>= f) = lift m >>= \r -> lift (f r) 

:

lift 은 (는) 다음과 같은 두 가지 법을 준수해야 함을 의미하는 모나드 morphisms 있어야하는데

-- i.e. "fmap id = id" 
(lift .) return = return 

-- i.e. "fmap (f . g) = fmap f . fmap g" 
(lift .) (f >=> g) = (lift .) f >=> (lift .) g 

그러나 출력을 모나드로 제한하지 않으면 해당 법칙이 더 이상 유효하지 않으며 을 올바르게 구현했는지 확인할 수있는 합리적인 방법이 없습니다.

진짜 이유는 클래스를 만드는 것이었다는 것입니다.

+0

'lift :: (Monad m, Monad (t m)) => m a -> t m a'에 대한 제 생각은 H98 인 것 같습니다. 그래서 우리는'ZipListT'와 같은 것들에 대해'lift :: (Applicative f) => f a -> t f a'와 같은 또 다른 응용 용 변압기를 사용해야한다고 제안 할 것입니다. –

+0

@ PetrPudlák 제약 조건이 없다는 진정한 이유는 모르겠다. 그래서 나는 추측하고있다. 적용 변압기는 어떤 법칙을 준수합니까? –

+0

응용 변압기가 적용 법률을 준수해야하고, 펑터 변압기가 펑터 법만 준수해야한다고 생각합니다. 대부분 뾰족한 펑터가 될 가능성이 높지만 '리프트. 순수 = 순수한'여전히 유효해야합니다. 나는 그것이 작업하기에 충분할 것이라고 생각한다. –