考虑从here
取得的功能依赖性示例class Extract container elem | container -> elem where
extract :: container -> elem
instance Extract (a,b) a where
extract (x,_) = x
我理解为什么需要container
和elem
之间的功能依赖来避免歧义
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有哪些限制?
由于
答案 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。