2009-06-30 18 views
8

Rozważmy następujący przykład program:Haskell: Nakładające się przypadki

next :: Int -> Int 
next i 
    | 0 == m2 = d2 
    | otherwise = 3 * i + 1 
    where 
    (d2, m2) = i `divMod` 2 

loopIteration :: MaybeT (StateT Int IO)() 
loopIteration = do 
    i <- get 
    guard $ i > 1 
    liftIO $ print i 
    modify next 

main :: IO() 
main = do 
    (`runStateT` 31) . runMaybeT . forever $ loopIteration 
    return() 

Można używać tylko get zamiast lift get ponieważ instance MonadState s m => MonadState s (MaybeT m) jest zdefiniowana w module MaybeT.

Wiele takich przypadków definiuje się w sposób kombinatoryczny.

To byłoby miłe (choć możliwe, dlaczego?), Jeżeli mieliśmy następujące typu Klasa:

{-# LANGUAGE MultiParamTypeClasses #-} 

class SuperMonad m s where 
    lifts :: m a -> s a 

Spróbujmy zdefiniować go jako takie:

{-# LANGUAGE FlexibleInstances, ... #-} 

instance SuperMonad a a where 
    lifts = id 

instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b) where 
    lifts = lift . lifts 

Korzystanie lifts $ print i zamiast z liftIO $ print i działa, co jest miłe.

Ale używanie lifts (get :: StateT Int IO Int) zamiast (get :: MaybeT (StateT Int IO) Int) nie działa.

GHC (6.10.3) daje następujący błąd:

Overlapping instances for SuperMonad 
          (StateT Int IO) (StateT Int IO) 
    arising from a use of `lifts' 
Matching instances: 
    instance SuperMonad a a 
    instance (SuperMonad a b, MonadTrans t, Monad b) => 
      SuperMonad a (t b) 
In a stmt of a 'do' expression: 
    i <- lifts (get :: StateT Int IO Int) 

widzę, dlaczego stosuje "instance SuperMonad a a". Ale dlaczego GHC sądzi, że ten drugi też tak robi?

Odpowiedz

35

Aby śledzić doskonałą odpowiedź ephemient za: zajęcia typu Haskell użyć otwarty świat założenie: jakiś idiota może przyjść później i dodać deklarację instancji, który jest nie duplikat i jeszcze pokrywa się z instancji. Pomyśl o tym jako o grze przeciwnika: jeśli przeciwnik może sprawić, że twój program będzie niejednoznaczny, bity kompilatora.

Jeśli używasz GHC Można oczywiście powiedzieć kompilator „do diabła z twojej paranoi; pozwalają mi deklarację niejednoznaczne instancji”:

{-# LANGUAGE OverlappingInstances #-} 

Jeśli później ewolucja programu prowadzi do przeciążenia rozdzielczość nie spodziewałeś się, że kompilator otrzyma 1,000 I-told-you-so-so points :-)

+3

+1 za doskonałe brzmienie. –

+0

dzięki! podążając za twoimi danymi, udało mi się to zrobić! – yairchu

+4

OverlappingInstances znajdują się daleko na mojej liście rozszerzeń (nawet bardziej niż UndecidableInstances, co jedynie utrudnia pracę kompilatora) do użycia - nie tylko nieprzenośne, ale także łamiące gwarancje bezpieczeństwa, które zwykle zapewnia Haskell. Radziłbym OP, żeby to wyssał i poradził sobie z "windowaniem", czy nie "podnoszeniem ręki", zamiast dodawać ten hack w ... ale to jest moja opinia. – ephemient

8

To, że nie zdefiniowałeś instancji w bieżącym module, nie oznacza, że ​​nie można go zdefiniować gdzie indziej.

{-# LANGUAGE ... #-} 
module SomeOtherModule where 

-- no practical implementation, but the instance could still be declared 
instance SuperMonad (StateT s m) m 

Załóżmy modułu i SomeOtherModule są połączone razem w jednym programie.

teraz odpowiedzieć na to pytanie: czy używanie kodu

instance SuperMonad a a 
    -- with a = StateT Int IO 

lub

instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b) 
    -- with a = StateT Int IO 
    --  t = StateT Int 
    --  b = IO 

?

+0

dzięki. ale nadal jestem zdezorientowany: same instancje nie nakładają się na siebie, ale ktoś może zdefiniować instancję, która sprawiłaby, że moje wystąpienia nakładają się na siebie. Czy ktoś nie może zawsze zdefiniować instancji, która pokrywałaby się z moją instancją? – yairchu

+2

Twoja struktura uniemożliwia kompilatorowi jednoznaczne określenie, która instancja ma być użyta w kodzie. Gdyby można było jednoznacznie rozwiązać, nakładanie się w innym miejscu nie miałoby znaczenia. – ephemient

+0

@ephemient: to ze względu na specyficzny sposób działania kompilatora, prawda? Mówię to, ponieważ mogę jednoznacznie określić, która instancja może być użyta. – yairchu

Powiązane problemy