我几天前asked a question关于在free-monads的上下文中注入仿函数。在那里提出的解决方案,基于Data Types à la Carte使用了一个代表仿函数之间的一种包含关系的类。
-- | Class that represents the relationship between a functor 'sup' containing
-- a functor 'sub'.
class (Functor sub, Functor sup) => sub :-<: sup where
inj :: sub a -> sup a
-- | A functor contains itself.
instance Functor f => f :-<: f where
inj = id
-- | A functor is contained in the sum of that functor with another.
instance (Functor f, Functor g) => f :-<: (Sum f g) where
inj = InL
-- | If a functor 'f' is contained in a functor 'g', then f is contained in the
-- sum of a third functor, say 'h', with 'g'.
instance (Functor f, Functor g, Functor h, f :-<: g) => f :-<: (Sum h g) where
inj = InR . inj
现在考虑以下数据类型:
type WeatherData = String
data WeatherServiceF a = Fetch (WeatherData -> a) deriving (Functor)
data StorageF a = Store WeatherData a deriving (Functor)
具有以下类型的函数
fetch :: (WeatherServiceF :-<: g) => Free g WeatherData
Free
来自Control.Monad.Free
模块。
然后,如果我尝试按如下方式使用此功能:
reportWeather :: Free (Sum WeatherServiceF StorageF) ()
reportWeather = do
_ <- fetch
return ()
我收到重叠实例错误,说:
• Overlapping instances for WeatherServiceF
:-<: Sum WeatherServiceF StorageF
arising from a use of ‘fetch’
Matching instances:
two instances involving out-of-scope types
instance (Functor f, Functor g) => f :-<: Sum f g
instance (Functor f, Functor g, Functor h, f :-<: g) =>
f :-<: Sum h g
现在,我知道第一个是有效的实例,但为什么第二个被认为是有效的实例呢?如果我在第二种情况下实例化变量,我会得到
instance ( Functor WeatherServiceF
, Functor StorageF
, Functor WeatherServiceF
, WeatherServiceF :-<: StorageF
) => WeatherServiceF :-<: Sum WeatherServiceF g
哪个不应该是实例,因为WeatherServiceF :-<: StorageF
不是真的。为什么GHC推断出这样的例子?
我启用了以下实例:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
答案 0 :(得分:3)
编译器必须能够通过仅考虑&#34; head&#34;来选择实例。实例,没有看约束。只有在选择适用的实例后才考虑约束。如果它无法决定只看头部的两个实例,那么它们会重叠。
原因是无法保证最终完整程序中使用的所有实例都将导入此模块。如果编译器曾承诺基于无法看到实现另一个实例的约束的实例来选择实例,那么不同的模块可以根据不同的类型对两个重叠实例中的哪一个用于相同类型做出不同的选择每个实例中可用的实例集。
重叠检查旨在阻止这种情况发生。因此,唯一可以做到这一点的方法是,当GHC在给定情况下看到哪些实例可能适用时,GHC会将所有约束视为至少可能可满足。如果只留下一个候选人,那么无论在程序的其他地方添加或删除其他实例,该候选人都将保留。然后,它可以检查此模块中是否有必要的实例以满足约束条件。