为什么GHC需要函数依赖来推断该表达式的类型

时间:2016-02-29 13:05:56

标签: haskell

考虑从here

取得的功能依赖性示例
class Extract container elem | container -> elem where
  extract :: container -> elem

instance Extract (a,b) a where
  extract (x,_) = x

我理解为什么需要containerelem之间的功能依赖来避免歧义

instance Extract (a,b) b where
  extract (x,_) = x

但是,如果我们没有后一个实例,为什么编译器不能确定extract ('x', 3)的类型而不是返回以下错误?

<interactive>:408:1:
Could not deduce (Num t0)
from the context (Num t, Extract (Char, t) elem)
  bound by the inferred type for ‘it’:
             (Num t, Extract (Char, t) elem) => elem
  at <interactive>:408:1-20
The type variable ‘t0’ is ambiguous
When checking that ‘it’ has the inferred type
  it :: forall elem t. (Num t, Extract (Char, t) elem) => elem
Probable cause: the inferred type is ambiguous

我尝试extract('x', 3 :: Int),在这种情况下会产生错误:

<interactive>:409:1:
No instance for (Show a0) arising from a use of ‘print’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
  instance Show a => Show (Control.Applicative.Const a b)
    -- Defined in ‘Control.Applicative’
  instance Show a => Show (Control.Applicative.ZipList a)
    -- Defined in ‘Control.Applicative’
  instance Show a => Show (Data.Complex.Complex a)
    -- Defined in ‘Data.Complex’
  ...plus 149 others
In a stmt of an interactive GHCi command: print it

在这种情况下,需要功能依赖的GHC有哪些限制?

由于

1 个答案:

答案 0 :(得分:2)

我发现当typechecker遇到歧义时使用:t会很有帮助。

:set -XMultiParamTypeClasses 
:set -XFunctionalDependencies 
:set -XFlexibleContexts 
:set -XFlexibleInstances 

class Extract big small where extract :: big -> small
instance Extract (a, b) a where extract = fst

所以,顺便说一句:

:t let oneTwo = (1, 2) :: (Int, Int)
:t extract oneTwo
extract oneTwo :: Extract (Int, Int) small => small

请注意!即使编译器知道Extract (Int, Int) small是唯一的实例,它也不会使用该类型解析信息。用词来说:

:t {- in an alternate universe -} extract oneTwo
extract oneTwo :: Int

原因很微妙:你实际上不会想要那种行为。如果使用了这些信息,那么这个编译器或二进制文件可能会在星期五做一些它周一没做的事。因为我可以定义

{- in an alternate universe -}
instance Extract (a, b) a where
  extract = fst
instance Extract (a, b) String where
  extract = const "hey girl"

现在extract oneTwo可以执行以下两项操作之一:

  • :: Int:: String之间随意挑选。这违反了最不惊讶的原则。在A点定义的实例设法对在B点一直定义的代码起作用。

  • 出错了!这个违反了最不惊讶的原则。在A点定义的实例使代码在B点停止类型检查时一直完成。

所以,面对两个罪恶,我们可以做的就是不使用实例来解析类型。

承诺,承诺

现在,如果你承诺第二个实例永远不会出现怎么办?

class Extract' big small | big -> small where
  extract' :: big -> small

instance Extract' (a, b) a where
  extract' = fst

:t extract' oneTwo
extract oneTwo :: Int

现在,Extract' big small的每个选项都只有一个或零个big个实例。现在可以使用实例进行类型解析。为此,以及更多车轮,请参阅24 Days of GHC Extensions