2012-02-15 17 views
5

Domanda. C'è un modo per far funzionare questo codice senza una firma di tipo esplicita?Guai di inferenza del tipo di GHC

Codice. Innanzitutto, ho una classe alternativa allo MonadTrans alternativa molto più bella, ispirata allo Data.Newtype. Ecco come si presenta,

{-# LANGUAGE FlexibleContexts, TypeFamilies #-} 

module Alt.Control.Monad.Trans where 

import Control.Monad 

class (Monad , Monad (BaseMonad)) => MonadTrans (:: * -> *) where 
    type BaseMonad :: * -> * 
    lift :: (BaseMonad) α -> α 

Poi, ho una classe A con il metodo foo, e se alcuni di base monade M è un A, allora ogni monade trasformato T M è anche un A. Nel codice,

class A where 
    foo :: String -> () 

instance (A (BaseMonad), MonadTrans) => A where 
    foo n = lift $ foo n 

Tuttavia, se ora voglio creare un collegamento per foo con il suo primo argomento sostituito, quindi ho bisogno di una firma di tipo esplicito, o stack overflow contesto del compilatore.

minimize_call :: A => () 
minimize_call = foo "minimize" 

Possibili informazioni per aiutare l'inferenza. Diciamo che abbiamo un tipo associato B :: * -> *. Sto pensando che voglio dire al compilatore B soddisfa B t /= t, B (B t) /= B t, ecc. Cioè B è in qualche modo "monotono" - che inseguire tipi associati equivale a rimuovere i wrapper newtype, e dovrebbe sapere che non può rimuovere per sempre i wrapper newtype , quindi aggiungere il contesto A alla firma è necessario.

+0

scusa, mi sarei preso la briga di ricordare _why_ Sono passato al "MonadTrans" alternativo ... per ora, diciamo che produce codice più pulito, ma penso che ci fosse un motivo più sostanziale. – gatoatigrado

+0

Interessante domanda. Perché non vuoi una firma di tipo esplicita? Il parametro 'minimize_call 'non deve essere un valore fisso, non una costante polimorfica (o forse si può ottenere che sia polimorfico, non ne sono sicuro)? Se ha un singolo tipo fisso, preferirei documentarlo e, in caso contrario, preferirei documentare ** **. Costringere il lettore a fare l'intera analisi del programma nella sua testa per capire che tipo "minimizza_call" sembra un po 'controproducente. – Ben

+0

@Ben, È vero che, in questo caso, avere una firma di tipo per 'minimize_call' è una buona pratica.Tuttavia, l'inferenza del tipo che si interrompe suggerisce che qualcosa non funziona (con il design, il compilatore o la comunicazione con il compilatore) e probabilmente causerà problemi, per non parlare di messaggi di errore incomprensibili. – gatoatigrado

risposta

3

Sì, c'è un modo. Fornire un'istanza di messa a terra per A e aggiungere NoMonomorphismRestriction al pragma della lingua (oltre a quello richiesto anche FlexibleInstances e UndecidableInstances).

Tuttavia, la classe A non sarà utilizzabile. Non c'è modo per il compilatore di sapere che non ci sarà mai un'istanza MonadTrans con BaseMonad m = m. Pertanto non può selezionare un'istanza, mai, perché non può sapere se usare l'istanza da qui o un'altra.

{-# LANGUAGE FlexibleContexts, TypeFamilies, FlexibleInstances, UndecidableInstances, NoMonomorphismRestriction #-} 

module Trans (MonadTrans(..), A(..), minimize_call) where 

import Control.Monad 

class (Monad m, Monad (BaseMonad m)) => MonadTrans (m :: * -> *) where 
    type BaseMonad m :: * -> * 
    lift :: (BaseMonad m) α -> m α 

class A m where 
    foo :: String -> m() 


data Foo a = Bork 

instance Monad Foo where 
    return _ = Bork 
    _ >>= _ = Bork 

instance A Foo where 
    foo _ = Bork 


instance (A (BaseMonad m), MonadTrans m) => A m where 
    foo n = lift $ foo n 

-- minimize_call :: A m => m() 
minimize_call = foo "minimize" 

compila con ghc 6.12, 7.0, 7.2 e 7.4. Senza la firma, minimize_call deve ottenere un tipo monomorfico, a meno che il MR non sia disattivato. Questo non può funzionare comunque perché il vincolo A m non è predefinito. Quindi, il MR deve essere spento. Ma il type checker insegue ancora la coda cercando di dimostrare che il vincolo è soddisfacente. Con solo l'istanza di sollevamento, non può. Se fornisci un'ancora, può.

Ma fornire una firma del tipo è molto meglio.

+0

Grazie, ma non voglio l'istanza messa a terra nel modulo. Lì, infatti, possono esserci istanze multiple di messa a terra, a seconda di quanti backend ho nel mio compilatore, e supponendo che l'uso di un particolare non sia desiderabile. Dispongo già di 'FlexibleInstances',' FlexibleContexts' e 'NoMonomorphismRestriction'. – gatoatigrado

+1

È possibile utilizzare una monade fittizia non esportata, consultare l'aggiornamento. Il tipo inferito di 'minimize_call' è' A m => m() ', come dovrebbe essere, non preclude l'uso di diverse istanze messe a terra da qualche altra parte, è solo necessario lasciare che il correttore di tipi termini. –

+0

In realtà, no non puoi usare 'minimize_call', o' foo' affatto. Con o senza la firma del tipo. –