Question

Considérons le programme d'exemple suivant:

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 ()

Il ne peut utiliser au lieu de get parce lift get est défini dans instance MonadState s m => MonadState s (MaybeT m) le module MaybeT.

Beaucoup de tels cas sont définis en nature d'une manière d'explosion combinatoire.

Il aurait été agréable (bien impossible pourquoi?) Si nous avions du type classe suivante:

{-# LANGUAGE MultiParamTypeClasses #-}

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

Essayons de le définir comme tel:

{-# 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

Utilisation au lieu de lifts $ print i œuvres liftIO $ print i, ce qui est agréable.

Mais au lieu de l'aide lifts (get :: StateT Int IO Int) ne fonctionne pas (get :: MaybeT (StateT Int IO) Int).

GHC (6.10.3) donne l'erreur suivante:

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)

Je ne vois pas pourquoi « instance SuperMonad a a » applique. Mais pourquoi ne pense GHC que l'autre est le cas, aussi?

Était-ce utile?

La solution

Pour donner suite excellente réponse de ephemient: classes de type Haskell utilisent un hypothèse monde ouvert : un idiot peut venir plus tard et ajouter une déclaration d'instance qui est pas un doublon et mais chevauche votre instance. Pensez-y comme un jeu adversaire : si un adversaire peut rendre votre programme ambigu, les bêlements du compilateur

.

Si vous utilisez GHC vous pouvez bien sûr dire au compilateur « en enfer avec votre paranoïa, permettez-moi de ma déclaration d'instance ambiguë »:

{-# LANGUAGE OverlappingInstances #-}

Si l'évolution ultérieure de votre programme conduit à une surcharge résolution que vous ne vous attendez pas, le compilateur obtient 1000 I-dit-vous-couça points: -)

Autres conseils

Juste parce que vous n'avez pas défini une instance dans votre module actuel ne veut pas dire que l'on ne pouvait pas définir un autre endroit.

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

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

Supposons que votre module et sont reliés entre eux SomeOtherModule dans un seul programme.

Maintenant, répondez à ceci: utilise votre code

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

ou

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top