-XAllowAmbiguousTypesはいつ適切ですか?


212

私が最近投稿した質問について、構文-2.0の定義についてをshare。私はこれをGHC 7.6で動作させました:

{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}

import Data.Syntactic
import Data.Syntactic.Sugar.BindingT

data Let a where
    Let :: Let (a :-> (a -> b) :-> Full b)

share :: (Let :<: sup,
          sup ~ Domain b, sup ~ Domain a,
          Syntactic a, Syntactic b,
          Syntactic (a -> b),
          SyntacticN (a -> (a -> b) -> b) 
                     fi)
           => a -> (a -> b) -> b
share = sugarSym Let

ただし、GHC 7.8は-XAllowAmbiguousTypesそのシグネチャでコンパイルする必要があります。また、私は置き換えることができfi

(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))

これは、Fundepが暗黙に示すタイプSyntacticNです。これにより、拡張を回避できます。もちろんこれは

  • すでに大きな署名に追加する非常に長い型
  • 手動で導出するのは面倒
  • ファンドペのため不要

私の質問は:

  1. これは許容できる使用法-XAllowAmbiguousTypesですか?
  2. 一般に、この拡張機能はいつ使用する必要がありますか?ここでの答えは、「それはほとんど決して良い考えではない」ことを示唆しています。
  3. 私はドキュメントを読みましたが制約があいまいかどうかを判断するのにまだ問題があります。具体的には、Data.Syntactic.Sugarの次の関数を検討してください。

    sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) 
             => sub sig -> f
    sugarSym = sugarN . appSym

    ここではあいまいであるように見えますがfi(おそらくsup)、拡張子なしでコンパイルされます。なぜsugarSym曖昧でないのshareですか?はのshareアプリケーションなのでsugarSymshare制約はすべてから直接発生しsugarSymます。


4
推測された型をだけで使用できない理由sugarSym Let(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => fありますか?これには、あいまいな型変数が含まれ、含まれていませんか?
kosmikus 14年

3
@kosmikus Sorrt回答に時間がかかりました。このコードは、のために推論された署名とコンパイルされないshareが、ない問題で述べたシグネチャのいずれかを使用した場合コンパイル。以前の投稿の
crockeea

3
未定義の動作は、おそらく最も適切な用語ではありません。1つのプログラムだけに基づいて理解することは困難です。問題は、可決性であり、GHCIがプログラムの型を証明できないことです。このテーマだけに興味を持つかもしれない長い議論があります。haskell.org/pipermail/haskell-cafe/2008-April/041397.html
BlamKiwi

6
(3)に関しては、SyntacticN(つまり、f-»fi)とApplySym(特に、fi-> sig、sup)の定義に機能的な依存関係があるため、その型はあいまいではありません。それから、あなたはそれが得るfだけでは完全に明確にするために十分でありsigfisup
user2141650

3
@ user2141650返信に時間がかかりすぎて申し訳ありません。あなたは上のfundep言っているSyntacticN可能fiで明確なのsugarSymが、なぜに当てはまると同じではありませんfiではshare
crockeea

回答:


12

sugarSym正確な型名を使用するための署名を持つ構文の公開されたバージョンは表示されないため、コミット8cfd02 ^で開発ブランチを使用します。これは、これらの名前を使用した最後のバージョンです。

では、なぜGHCはfi型シグネチャのについて不平を言っているのsugarSymですか。リンク先のドキュメントでは、制約が関数の依存関係を使用して他のあいまいでない型から他のあいまいでない型を推論していない限り、型が制約の右側に表示されない場合はあいまいであることを説明しています。それでは、2つの関数のコンテキストを比較して、関数の依存関係を探しましょう。

class ApplySym sig f sym | sig sym -> f, f -> sig sym
class SyntacticN f internal | f -> internal

sugarSym :: ( sub :<: AST sup
            , ApplySym sig fi sup
            , SyntacticN f fi
            ) 
         => sub sig -> f

share :: ( Let :<: sup
         , sup ~ Domain b
         , sup ~ Domain a
         , Syntactic a
         , Syntactic b
         , Syntactic (a -> b)
         , SyntacticN (a -> (a -> b) -> b) fi
         )
      => a -> (a -> b) -> b

ですからためsugarSym、非あいまいな種類がありsubsigかつf、それらから、私たちは、すなわち、文脈で使用される他のすべてのタイプを明確にするために、機能的な依存関係を追跡することができるはずsupfi。そして実際、のf -> internal機能依存関係はをSyntacticN使用してfを明確化しfi、その後、f -> sig sym機能依存関係はをApplySym使用して新たに明確化したものfiを明確化しますsup(そしてsig、これはすでに明確です)。これsugarSymが、AllowAmbiguousTypes拡張機能が必要ない理由です。

では、を見てみましょうsugar。最初のI通知は、コンパイラがされていることであるではないあいまいなタイプ文句ではなく、オーバーラップインスタンスについて:

Overlapping instances for SyntacticN b fi
  arising from the ambiguity check for share
Matching givens (or their superclasses):
  (SyntacticN (a -> (a -> b) -> b) fi1)
Matching instances:
  instance [overlap ok] (Syntactic f, Domain f ~ sym,
                         fi ~ AST sym (Full (Internal f))) =>
                        SyntacticN f fi
    -- Defined in ‘Data.Syntactic.Sugar’
  instance [overlap ok] (Syntactic a, Domain a ~ sym,
                         ia ~ Internal a, SyntacticN f fi) =>
                        SyntacticN (a -> f) (AST sym (Full ia) -> fi)
    -- Defined in ‘Data.Syntactic.Sugar’
(The choice depends on the instantiation of b, fi’)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes

したがって、私がこの権利を読んでいる場合、GHCが型があいまいであると考えるのではなく、型があいまいかどうかを確認しているときに、GHCが別の別の問題に遭遇したということです。次に、あいまいさチェックを実行しないようにGHCに指示した場合、別の問題は発生しなかったことを示しています。これにより、AllowAmbiguousTypesを有効にすると、コードをコンパイルできるようになります。

ただし、重複するインスタンスの問題は残ります。GHC(SyntacticN f fiSyntacticN (a -> f) ...)でリストされた2つのインスタンスは互いに重複しています。不思議なことに、これらの最初のものは他のインスタンスと重複しているようで、疑わしいものです。そして、どういう[overlap ok]意味ですか?

SyntacticはOverlappingInstancesでコンパイルされていると思います。そして、コードを見ると、確かにそうです。

少し実験してみると、一方が他方よりも厳密に一般的であることが明らかな場合、GHCはインスタンスが重複しても問題ないようです。

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}

class Foo a where
  whichOne :: a -> String

instance Foo a where
  whichOne _ = "a"

instance Foo [a] where
  whichOne _ = "[a]"

-- |
-- >>> main
-- [a]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])

しかし、GHCは、どちらも他のインスタンスより明らかに適合性が低い場合、インスタンスが重複しても問題ありません。

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}

class Foo a where
  whichOne :: a -> String

instance Foo (f Int) where  -- this is the line which changed
  whichOne _ = "f Int"

instance Foo [a] where
  whichOne _ = "[a]"

-- |
-- >>> main
-- Error: Overlapping instances for Foo [Int]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])

タイプシグネチャはを使用してSyntacticN (a -> (a -> b) -> b) fiおり、どちらSyntacticN f fiもどちらSyntacticN (a -> f) (AST sym (Full ia) -> fi)よりも適していません。タイプシグネチャのその部分をSyntacticN a fiまたはに変更するとSyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)、GHCは重複について不満を言うことはなくなります。

私があなただったら、私はこれら2つの可能なインスタンスの定義を調べてこれら2つの実装のうちの1つが必要な実装であるかどうかを判断します。


2

AllowAmbiguousTypesでの使用に非常に便利であることを発見しましたTypeApplicationsGHC.TypeLitsの関数natVal :: forall n proxy . KnownNat n => proxy n -> Integer考えます。

この関数を使用するために、私は書くことができますnatVal (Proxy::Proxy5)。別のスタイルは、使用することTypeApplicationsです:natVal @5 Proxy。のタイプはProxyタイプアプリケーションによって推論され、を呼び出すたびにそれを記述する必要があるのは面倒ですnatVal。したがって、以下を有効にAmbiguousTypesして書き込むことができます。

{-# Language AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}

ambiguousNatVal :: forall n . (KnownNat n) => Integer
ambiguousNatVal = natVal @n Proxy

five = ambiguousNatVal @5 -- no `Proxy ` needed!

ただし、あいまいになると、元に戻すことはできません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.