2012-02-15 24 views
5

質問を苦境。明示的な型署名なしでこのコードを動作させる方法はありますか?GHCの型推論は

コード。まず、Data.Newtypeに触発された、実際にはもっと良い方法の代替MonadTransクラスがあります。それは、このようになります

{-# LANGUAGE FlexibleContexts, TypeFamilies #-} 

module Alt.Control.Monad.Trans where 

import Control.Monad 

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

はその後、私はこの方法fooでクラスAを持っている、といくつかの基本モナドMAであれば、任意の変換モナドT MAです。コードでは、私は今、置換、最初の引数でfooのショートカットを作成したい場合は

class A where 
    foo :: String -> () 

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

はしかし、その後、私は明示的な型シグネチャを必要とする、またはコンパイラのコンテキストスタックがオーバーフローします。

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

可能な情報推論を支援します。たとえば、関連するタイプがB :: * -> *であるとします。私はコンパイラにBB t /= tB (B t) /= B tなどを満たすようにしたいと思っています。すなわち、Bは何らかの形で単調です - 関連する型を追跡することはnewtypeラッパーを取り除くことと同等で、newtypeラッパーを永遠に削除することはできませんしたがって、署名に文脈Aを追加する必要があります。

+0

申し訳ありませんが、代替のMonadTransに切り替えたのを覚えていないと気になりませんでした...今は、よりクリーンなコードが作成されていますが、もっと重要な理由があったと思います。 – gatoatigrado

+0

興味深い質問。なぜあなたは明示的な型署名をしたくないのですか? 'minimize_call'はポリモーフィックな定数ではなく、固定値でなければなりません(多分多態性になるかもしれませんが、わかりません)?それはいくつかの単一の固定タイプを持っている場合、私はむしろそれを文書化し、そうでなければ、私はむしろ** **を**文書化したいと思います。読者に、プログラムの全体分析を強制的に行わせて、 'minimize_call'のタイプが少し生産的でないように見えるようにします。 – Ben

+0

@Benこの場合、 'minimize_call'の型シグネチャを持つことは良い方法です。しかし、タイプ推論が壊れていると、何かが間違っている(コンパイラー、デザイン、コンパイラー、またはコンパイラーとの通信に問題がある)ことがわかり、問題を引き起こす可能性があります。 – gatoatigrado

答えて

3

はい、方法があります。 Aため接地インスタンスを提供し、(また、FlexibleInstancesUndecidableInstances必要に加えて)言語プラグマにNoMonomorphismRestrictionを加えます。

しかし、Aクラスが使用できなくなります。BaseMonad m = mMonadTransインスタンスが決して存在しないことをコンパイラが知る方法はありません。したがって、ここからインスタンスを使用するか別のインスタンスを使用するかを知ることができないため、インスタンスを選択することはできません。

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

は、ghc 6.12,7.0,7.2,7.4でコンパイルされます。署名がなければ、minimize_callは、MRがオフにされていない限り、単相性型を取得する必要があります。とにかく、A mという制約はデフォルト設定ではないため、これは動作しません。したがって、MRをオフにする必要があります。しかし、型チェッカーは依然として制約が充足可能であることを証明しようとしている自身の尾を追う。持上げのインスタンスだけでは、それはできません。あなたがアンカーを提供すれば、それはできます。

しかし、型シグネチャを提供するには、はるかに優れています。

+0

ありがとうございますが、私はモジュール内の接地されたインスタンスを望んでいません。実際には、私はコンパイラに何個のバックエンドがあるかに依存し、特定のものを使用することが望ましくないと仮定すると、複数の根拠のあるインスタンスになることがあります。私は既に 'FlexibleInstances'、' FlexibleContexts'、 'NoMonomorphismRestriction'を有効にしています。 – gatoatigrado

+1

エクスポートされていないダミーモナドを使用できます。更新を参照してください。 'minimize_call'の推論された型は' A m => m() 'であり、そうでなければならないので、異なる地面のインスタンスを別の場所で使用することを排除するものではなく、型チェッカを終了させるだけでよい。 –

+0

実際には、 'minimize_call'や' foo'を使うことはできません。型名の有無にかかわらず。 –