2017-01-19 7 views
3

프리 모나드에 상태 모나드를 삽입 할 수 있습니다. 하나가 일치하지 않으면 왜 '겹치는 인스턴스'오류가 발생합니까?

{-# language FlexibleInstances, MultiParamTypeClasses #-} 
module Main where 

import Control.Monad.Free 
import Control.Monad.State 
import Data.Bifunctor 

data Toy state next = 
     Output String next 
    | LiftState (state -> (next, state)) 
    | Done 

instance Functor (Toy s) where 
    fmap f (Output str next) = Output str $ f next 
    fmap f (LiftState stateF) = LiftState (first f . stateF) 
    fmap f Done = Done 

instance MonadState s (Free (Toy s)) where 
    state = overState 

overState :: (s -> (a, s)) -> Free (Toy s) a 
overState = liftF . LiftState 

output :: Show a => a -> Free (Toy s)() 
output x = liftF $ Output (show x)() 

done :: Free (Toy s) r 
done = liftF Done 

program :: Free (Toy Int)() 
program = do 
    start <- get 
    output start 
    modify ((+10) :: (Int -> Int)) 
    end <- get 
    output end 
    done 

interpret :: (Show r) => Free (Toy s) r -> s -> IO() 
interpret (Free (LiftState stateF)) s = let (next, newS) = stateF s 
             in interpret next newS 
interpret (Free (Output str next)) s = print str >> interpret next s 
interpret (Free Done) s = return() 
interpret (Pure x) s = print x 

main :: IO() 
main = interpret program (5 :: Int) 

내가 오류 얻을 : 여기 내 간단한 시도의 지금까지 내가 수집 할 수

• Overlapping instances for MonadState Int (Free (Toy Int)) 
    arising from a use of ‘get’ 
    Matching instances: 
    instance [safe] (Functor m, MonadState s m) => 
        MonadState s (Free m) 
     -- Defined in ‘Control.Monad.Free’ 
    instance MonadState s (Free (Toy s)) 
     -- Defined at app/Main.hs:18:10 
• In a stmt of a 'do' block: start <- get 
    In the expression: 
    do { start <- get; 
     output start; 
     modify ((+ 10) :: Int -> Int); 
     end <- get; 
     .... } 
    In an equation for ‘program’: 
     program 
     = do { start <- get; 
       output start; 
       modify ((+ 10) :: Int -> Int); 
       .... } 

는; 이 경우 적용하려고 다음 free package here에서

(Functor m, MonadState s m) => MonadState s (Free m) 

을; 그러나이 경우에는 Free (Toy s)과 일치해야하고 필요에 따라 MonadState s (Toy s)이 없으므로 적용되는 이유를 이해할 수 없습니다. 내 인스턴스 정의를 제거하면

내가 얻을 :

다른 인스턴스가 실제로 적용되지 않습니다 내 생각을 지원
• No instance for (MonadState Int (Toy Int)) 
    arising from a use of ‘modify’ 

; 어떻게하면 지정된 인스턴스를 사용하여 이것을 컴파일 할 수 있습니까? 왜 이것이 발생하는지 설명 할 수 있습니까? FlexibleInstances이 사용 되었기 때문입니까?

감사합니다.

+0

메타 주석 :이 질문은 모두 무언가의 복제본이어야합니까? –

답변

4

인스턴스를 선택할 때 인스턴스 컨텍스트 ((Functor m, MonadState s m) 비트)는 무시됩니다. 이것은 컴파일러가 잠재적으로 비용이 많이 드는 역 추적 검색을 수행하여 인스턴스를 선택하는 것을 방지하기위한 것입니다. 따라서 두 개의 인스턴스가 적용되고 하나의 인스턴스가 인스턴스 컨텍스트로 인해 제외되는 경우, 귀하의 경우와 같이 중복됩니다.

이것은 mtl 디자인의 불행한 부분이며, 각 하스켈 프로그래머가 어느 시점이나 다른 시점에서 부딪혔다 고 생각합니다. 수정 프로그램에는 많은 선택 사항이 없습니다. 일반적으로 newtype을 추가하고 인스턴스를 부여하면 다음과 같습니다.

newtype FreeToy s a = FreeToy (Free (Toy s) a) 
instance MonadState s (FreeToy s) where -- ... 
+0

알아두면 좋습니다. 하지만 어떻게 해결할 수 있습니까? –

+0

@ChrisPenner 선택 항목이 많지 않습니다. 나는 내 대답에 짧은 것을 추가 할 것이다. –

+0

질문의 핵심은 내 모나드에 상태 모나드를 포함시키고 싶습니다. 그래서 내가 깨끗하게 할 수 있다면 나는 행복하다. 이 기능을 작동시킬 수있는 방법이 없다면 약간의 감시가 될 것입니다. –