小编典典

-XAllowAmbiguousTypes 什么时候合适?

all

我最近发布了一个关于syntactic-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是一个应用程序sugarSym,所有的share约束都直接来自sugarSym


阅读 60

收藏
2022-06-22

共1个答案

小编典典

我没有看到任何已发布的语法版本,其签名sugarSym使用这些确切的类型名称,所以我将在提交 8cfd02^ 处使用开发分支,最后一个版本仍然使用这些名称。

那么,为什么 GHC 抱怨fiin 你的类型签名而不是那个 forsugarSym呢?您链接到的文档解释说,如果一个类型没有出现在约束的右侧,则它是不明确的,除非约束使用函数依赖关系从其他非歧义类型推断出其他不明确的类型。因此,让我们比较两个函数的上下文并寻找函数依赖关系。

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,非歧义类型是subsigf,并且我们应该能够遵循函数依赖关系,以便消除上下文中使用的所有其他类型,即supfi。实际上,f -> internal函数依赖SyntacticN使用我们的f来消除歧义fi,然后f -> sig sym函数依赖ApplySym使用我们新消除的歧义fi来消除歧义sup(并且sig,这已经是非歧义的)。这就解释了为什么sugarSym不需要AllowAmbiguousTypes扩展。

现在让我们来看看sugar。我注意到的第一件事是编译器不是在抱怨一个模棱两可的类型,而是在抱怨重叠的实例:

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) ...) 列出的两个实例确实相互重叠。奇怪的是,似乎第一个应该与任何其他实例重叠,这是可疑的。是什么[overlap ok]意思?

我怀疑语法是用 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 fior SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi),GHC 将不再抱怨重叠。

如果我是你,我会查看这两个可能实例的定义,并确定这两个实现之一是否是你想要的。

2022-06-22