将函数调用移动到 where 子句会破坏具有重叠实例的类型检查器
Moving function call into where clause breaks type checker with OverlappingInstances
我正在使用 OverlappingInstances
制作漂亮的印刷品 class,只要我没有为某种类型提供自定义实例,它就会默认为 Show
。
出于某种原因,每当您使用 where
子句或 let
表达式时,这似乎都会中断。
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class View a where
view :: a -> String
instance {-# OVERLAPS #-} Show a => View a where
view = show
-- Works just fine
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ view a ++ ", " ++ view b ++ ")"
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ a' ++ ", " ++ b' ++ ")"
where
a' = view a
b' = view b
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = let
a' = view a
b' = view b
in "(" ++ a' ++ ", " ++ b' ++ ")"
现在,如果我删除默认的重叠实例,所有其他实例都可以正常工作。
我希望有人能向我解释为什么会发生这种情况,或者这只是一个错误?
我得到的每个错误的具体错误是:
Could not deduce (Show a) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
Could not deduce (Show b) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
所以出于某种原因 where
/ let
正在欺骗类型检查器认为 View
需要 Show
,而实际上它不需要。
类型族方法如下所示:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses,
FlexibleInstances, DataKinds, KindSignatures,
ScopedTypeVariables #-}
import Data.Proxy
data Name = Default | Booly | Inty | Pairy Name Name
type family ViewF (a :: *) :: Name where
ViewF Bool = 'Booly
ViewF Int = 'Inty
ViewF Integer = 'Inty --you can use one instance many times
ViewF (a, b) = 'Pairy (ViewF a) (ViewF b)
ViewF a = 'Default
class View (name :: Name) a where
view' :: proxy name -> a -> String
instance (Show a, Num a) => View 'Inty a where
view' _ x = "Looks Inty: " ++ show (x + 3)
instance a ~ Bool => View 'Booly a where
view' _ x = "Looks Booly: " ++ show (not x)
instance Show a => View 'Default a where
view' _ x = "Looks fishy: " ++ show x
instance (View n1 x1, View n2 x2) => View ('Pairy n1 n2) (x1, x2) where
view' _ (x, y) = view' (Proxy :: Proxy n1) x ++ "," ++ view' (Proxy :: Proxy n2) y
view :: forall a name .
(ViewF a ~ name, View name a)
=> a -> String
view x = view' (Proxy :: Proxy name) x
-- Example:
hello :: String
hello = "(" ++ view True ++ view (3 :: Int)
++ view "hi" ++ ")"
感谢@dfeuer 提供了另一种方法,但我认为我应该为问题本身编写相当快速的修复程序:
{-# LANGUAGE MonoLocalBinds #-}
将其放在文件顶部后,一切正常。从我所做的研究来看,某些扩展(我猜包括 OverlappingInstances
)似乎在本地绑定类型检查中戳破了漏洞,并且 MonoLocalBinds
牺牲了使用本地绑定的多态性来修复这些漏洞。
我正在使用 OverlappingInstances
制作漂亮的印刷品 class,只要我没有为某种类型提供自定义实例,它就会默认为 Show
。
出于某种原因,每当您使用 where
子句或 let
表达式时,这似乎都会中断。
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class View a where
view :: a -> String
instance {-# OVERLAPS #-} Show a => View a where
view = show
-- Works just fine
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ view a ++ ", " ++ view b ++ ")"
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ a' ++ ", " ++ b' ++ ")"
where
a' = view a
b' = view b
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = let
a' = view a
b' = view b
in "(" ++ a' ++ ", " ++ b' ++ ")"
现在,如果我删除默认的重叠实例,所有其他实例都可以正常工作。
我希望有人能向我解释为什么会发生这种情况,或者这只是一个错误?
我得到的每个错误的具体错误是:
Could not deduce (Show a) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
Could not deduce (Show b) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
所以出于某种原因 where
/ let
正在欺骗类型检查器认为 View
需要 Show
,而实际上它不需要。
类型族方法如下所示:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses,
FlexibleInstances, DataKinds, KindSignatures,
ScopedTypeVariables #-}
import Data.Proxy
data Name = Default | Booly | Inty | Pairy Name Name
type family ViewF (a :: *) :: Name where
ViewF Bool = 'Booly
ViewF Int = 'Inty
ViewF Integer = 'Inty --you can use one instance many times
ViewF (a, b) = 'Pairy (ViewF a) (ViewF b)
ViewF a = 'Default
class View (name :: Name) a where
view' :: proxy name -> a -> String
instance (Show a, Num a) => View 'Inty a where
view' _ x = "Looks Inty: " ++ show (x + 3)
instance a ~ Bool => View 'Booly a where
view' _ x = "Looks Booly: " ++ show (not x)
instance Show a => View 'Default a where
view' _ x = "Looks fishy: " ++ show x
instance (View n1 x1, View n2 x2) => View ('Pairy n1 n2) (x1, x2) where
view' _ (x, y) = view' (Proxy :: Proxy n1) x ++ "," ++ view' (Proxy :: Proxy n2) y
view :: forall a name .
(ViewF a ~ name, View name a)
=> a -> String
view x = view' (Proxy :: Proxy name) x
-- Example:
hello :: String
hello = "(" ++ view True ++ view (3 :: Int)
++ view "hi" ++ ")"
感谢@dfeuer 提供了另一种方法,但我认为我应该为问题本身编写相当快速的修复程序:
{-# LANGUAGE MonoLocalBinds #-}
将其放在文件顶部后,一切正常。从我所做的研究来看,某些扩展(我猜包括 OverlappingInstances
)似乎在本地绑定类型检查中戳破了漏洞,并且 MonoLocalBinds
牺牲了使用本地绑定的多态性来修复这些漏洞。