2017-11-22 20 views
3

전 몇 달 전 자유 모나크 컨텍스트에서 펑터 주입에 대해 I asked a question에 대해 설명합니다. Data Types à la Carte에 기반한 해결책은 펑터 (functor) 간의 일종의 포함 관계를 나타내는 클래스를 사용합니다. FreeControl.Monad.Free 모듈에서 오는 경우이 두 인스턴스가 겹치는 방법 (범위를 벗어난 유형 포함)

type WeatherData = String 

data WeatherServiceF a = Fetch (WeatherData -> a) deriving (Functor) 

data StorageF a = Store WeatherData a deriving (Functor) 

그리고 다음과 같은 유형의

fetch :: (WeatherServiceF :-<: g) => Free g WeatherData 

와 기능 :

-- | Class that represents the relationship between a functor 'sup' containing 
-- a functor 'sub'. 
class (Functor sub, Functor sup) => sub :-<: sup where 
    inj :: sub a -> sup a 

-- | A functor contains itself. 
instance Functor f => f :-<: f where 
    inj = id 

-- | A functor is contained in the sum of that functor with another. 
instance (Functor f, Functor g) => f :-<: (Sum f g) where 
    inj = InL 

-- | If a functor 'f' is contained in a functor 'g', then f is contained in the 
-- sum of a third functor, say 'h', with 'g'. 
instance (Functor f, Functor g, Functor h, f :-<: g) => f :-<: (Sum h g) where 
    inj = InR . inj 

는 이제 다음과 같은 데이터 유형을 고려하십시오. 나는이 기능을 사용하려고하면

그런 다음 다음과 같이 내가 처음이 유효한 인스턴스 인 이해,

• Overlapping instances for WeatherServiceF 
          :-<: Sum WeatherServiceF StorageF 
    arising from a use of ‘fetch’ 
    Matching instances: 
    two instances involving out-of-scope types 
     instance (Functor f, Functor g) => f :-<: Sum f g 

     instance (Functor f, Functor g, Functor h, f :-<: g) => 
       f :-<: Sum h g 

을 지금 :

reportWeather :: Free (Sum WeatherServiceF StorageF)() 
reportWeather = do 
    _ <- fetch 
    return() 

내 말은, 중복-경우 오류가 발생합니다 ,하지만 두 번째 이유가 유효한 인스턴스로 간주되는 이유는 무엇입니까? 내가 두 번째 경우에서 변수를 인스턴스화하면 나는 그렇지 사실 WeatherServiceF :-<: StorageF 그 때문에 인스턴스 안

instance (Functor WeatherServiceF 
     , Functor StorageF 
     , Functor WeatherServiceF 
     , WeatherServiceF :-<: StorageF 
     ) => WeatherServiceF :-<: Sum WeatherServiceF g 

을 얻을 것입니다. GHC는 왜 그러한 사례를 추론합니까?

나는 다음과 같은 경우를 사용할 수 있습니다

{-# LANGUAGE DeriveFunctor   #-} 
{-# LANGUAGE FlexibleContexts  #-} 
{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeOperators   #-} 

답변

3

컴파일러는 제약을 보지 않고, 인스턴스 만 "머리"를 고려하여 인스턴스를 선택할 수 있어야한다. 해당 인스턴스가 선택되면 제약 조건 만 고려됩니다. 머리 부분 만보고있는 두 가지 경우를 결정할 수 없다면 중첩됩니다.

최종 완성 프로그램에 사용 된 모든 인스턴스를 모듈로 가져올 수 있다는 보장이 없기 때문입니다. 컴파일러가 다른 인스턴스의 제약 조건을 충족하는 인스턴스를 볼 수 없다는 사실을 기반으로 인스턴스를 선택했다면 다른 모듈은 다른 인스턴스를 기반으로 동일한 유형에 사용할 두 개의 중첩 인스턴스 중 어떤 인스턴스에 대해 다른 선택을 할 수 있습니다 각 인스턴스에서 사용할 수있는 인스턴스 집합.

중복 검사는 그 일을 막기위한 것입니다. 따라서 GHC가 모든 제약 조건을 적어도 으로 처리하면 특정 상황에서 어떤 인스턴스가 적용될 수 있는지를 파악할 때을 충족시킬 수 있습니다. 그것이 정확히 하나의 후보자를 남겨두면, 그 후보자는 다른 인스턴스가 프로그램의 다른 곳에 추가되거나 제거되는 것과 관계없이 유지됩니다. 그런 다음 제약 조건을 충족시키기 위해이 모듈에서 필요한 인스턴스를 사용할 수 있는지 확인할 수 있습니다.

+0

우수. 나는 그것에 대해 몰랐다. 나는이 질문에있는 예제에서이 중첩 인스턴스 오류를 피할 수있는 이유가 무엇입니까? –

+0

물론 'OverlappingInstances'에 의지하지 않고 :) –