12

GHCユーザーズガイドでは、次の例を参照して、非叙述的ポリモーフィズム拡張について説明しています。

f :: Maybe (forall a. [a] -> [a]) -> Maybe ([Int], [Char])
f (Just g) = Just (g [3], g "hello")
f Nothing  = Nothing

ただし、この例をファイルで定義して呼び出そうとすると、タイプエラーが発生します。

ghci> f (Just reverse)

<interactive>:8:9:
    Couldn't match expected type `forall a. [a] -> [a]'
                with actual type `[a0] -> [a0]'
    In the first argument of `Just', namely `reverse'
    In the first argument of `f', namely `(Just reverse)'
    In the expression: f (Just reverse)
ghci> f (Just id)

<interactive>:9:9:
    Couldn't match expected type `forall a. [a] -> [a]'
                with actual type `a0 -> a0'
    In the first argument of `Just', namely `id'
    In the first argument of `f', namely `(Just id)'
    In the expression: f (Just id)

、、、またはタイプチェッカーを満たすだけのようですundefinedNothingJust undefined

したがって、2つの質問があります。

  • Just f上記の関数は、ボトム以外の関数で呼び出すことができますfか?
  • 誰かが、非叙述的なポリモーフィズムでのみ定義可能で、自明ではない方法で使用できる値の例を提供できますか?

後者は特に、非叙述的ポリモーフィズムに関するHaskellWikiページを念頭に置いており、現在、拡張機能の存在について明らかに説得力のない主張をしています。

4

3 に答える 3

7

ImpredicativeTypesghc-7 +の新しいタイプチェッカーで静かに削除されただけではありませんか?ideone.comはまだghc-6.8を使用しており、実際にプログラムは正常に実行するために使用していることに注意してください。

{-# OPTIONS -fglasgow-exts  #-}

f :: Maybe (forall a. [a] -> [a]) -> Maybe ([Int], [Char])
f (Just g) = Just (g [3], g "hello")
f Nothing  = Nothing

main   = print $ f (Just reverse)

Just ([3],"olleh")期待どおりに印刷します。http://ideone.com/KMASZyを参照してください

augustssハンサムなユースケース(ある種の模倣Python dsl)と拡張機能の防御をここに示します:http://augustss.blogspot.com/2011/07/impredicative-polymorphism-use-case-in.html参照こちらのチケットでhttp://hackage.haskell.org/trac/ghc/ticket/4295

于 2012-12-27T02:16:41.943 に答える
7

これは、1つのプロジェクトconst-math-ghc-pluginImpredicativeTypesが一致するルールのリストを指定するために使用する方法の例です。

フォームの式がある場合App (PrimOp nameStr) (Lit litVal)、primop名に基づいて適切なルールを検索するという考え方です。AlitValMachFloat dまたはMachDouble ddRational)のいずれかになります。ルールが見つかったら、そのルールの関数を適用dして正しいタイプに変換します。

この関数mkUnaryCollapseIEEEは、単項関数に対してこれを行います。

mkUnaryCollapseIEEE :: (forall a. RealFloat a => (a -> a))
                    -> Opts
                    -> CoreExpr
                    -> CoreM CoreExpr
mkUnaryCollapseIEEE fnE opts expr@(App f1 (App f2 (Lit lit)))
    | isDHash f2, MachDouble d <- lit = e d mkDoubleLitDouble
    | isFHash f2, MachFloat d  <- lit = e d mkFloatLitFloat
    where
        e d = evalUnaryIEEE opts fnE f1 f2 d expr

最初の引数は、リテラルコンストラクターのいずれかで、またはそれに応じてFloatインスタンス化されるため、ランク2タイプである必要があります。Doubleルールのリストは次のようになります。

unarySubIEEE :: String -> (forall a. RealFloat a => a -> a) -> CMSub
unarySubIEEE nm fn = CMSub nm (mkUnaryCollapseIEEE fn)

subs =
    [ unarySubIEEE "GHC.Float.exp"    exp
    , unarySubIEEE "GHC.Float.log"    log
    , unarySubIEEE "GHC.Float.sqrt"   sqrt
    -- lines omitted
    , unarySubIEEE "GHC.Float.atanh"  atanh
    ]

ボイラープレートが多すぎて私の好みに合わない場合は、これで問題ありません。

ただし、同様の機能がありますmkUnaryCollapsePrimIEEE。この場合、ルールはGHCバージョンごとに異なります。複数のGHCをサポートしたい場合は、少し注意が必要です。同じアプローチを採用した場合、subs定義には多くのCPPが必要になり、保守が不可能になる可能性があります。代わりに、GHCバージョンごとに個別のファイルでルールを定義しました。ただし、mkUnaryCollapsePrimIEEE循環インポートの問題のため、これらのモジュールでは使用できません。モジュールを再構築して機能させることもできますが、代わりにルールセットを次のように定義しました。

unaryPrimRules :: [(String, (forall a. RealFloat a => a -> a))]
unaryPrimRules =
    [ ("GHC.Prim.expDouble#"    , exp)
    , ("GHC.Prim.logDouble#"    , log)
    -- lines omitted
    , ("GHC.Prim.expFloat#"     , exp)
    , ("GHC.Prim.logFloat#"     , log)
    ]

を使用ImpredicativeTypesすることにより、ランク2関数のリストを保持し、の最初の引数に使用できるようにすることができますmkUnaryCollapsePrimIEEE。代替案は、はるかに多くのCPP /ボイラープレート、モジュール構造の変更(または循環インポート)、または多くのコードの重複です。どれも私は望んでいません。

拡張機能のサポートを終了したいと言っているGHCHQを思い出しているようですが、おそらく彼らは再考しました。それは時々非常に便利です。

于 2012-12-28T06:22:20.500 に答える
5

この回避策に注意してください。

justForF :: (forall a. [a] -> [a]) -> Maybe (forall a. [a] -> [a])
justForF = Just

ghci> f (justForF reverse)
Just ([3],"olleh")

またはこれ(これは基本的にインライン化されたものと同じです):

ghci> f $ (Just :: (forall a. [a] -> [a]) -> Maybe (forall a. [a] -> [a])) reverse
Just ([3],"olleh")

型推論にはJust、あなたのケースの型を推測するのに問題があるようです。型を教えなければなりません。

それがバグなのか、それとも正当な理由があるのか​​、私にはわかりません。:)

于 2012-12-27T00:09:05.450 に答える