避免使用封闭类型系列重叠实例(GHC / AdvancedOverlap)

时间:2016-09-20 16:28:06

标签: haskell typeclass

关于如何从Haskell Wiki编译中创建闭合类型族示例的任何指针?

GHC/AdvancedOverlap(解决方案3)

这是我试过的版本:

{-# LANGUAGE 
    DataKinds,
    FlexibleInstances,
    MultiParamTypeClasses,
    TypeFamilies,
    UndecidableInstances #-}

data HTrue
data HFalse

type family ShowPred a where
  ShowPred Int   = HTrue
  ShowPred Bool  = HTrue
  ShowPred [a]   = ShowPred a
  ShowPred a     = HFalse

class Print a where
  print :: a -> IO ()

instance (ShowPred a ~ flag, Print' flag a) => Print a where
  print = print' (undefined :: flag)

class Print' flag a where
  print' :: flag -> a -> IO ()

instance Show a => Print' HTrue a where
  print' _ x = putStrLn (show x)

instance Print' flag a where
  print' _ _ = putStrLn "No show method"

然而,这仍然让我:

  

Print'重叠的实例flag0 a。

(GHC 8.0.1是我正在使用的版本。)

修改:将第二个实例定义为Print' HFalse a会导致:

  

无法推断(Print' flag0 a)因使用print'从上下文:( ShowPred a~flag,Print' flag a)。

编辑:以下是所有更正的示例。所有功劳都归功于@dfeuer。

{-# LANGUAGE 
    DataKinds,
    FlexibleInstances,
    MultiParamTypeClasses,
    ScopedTypeVariables,
    TypeFamilies,
    UndecidableInstances #-}

import Prelude hiding (print)

import Data.Proxy

data HTrue
data HFalse

type family ShowPred a where
  ShowPred Int   = HTrue
  ShowPred Bool  = HTrue
  ShowPred [a]   = ShowPred a
  ShowPred a     = HFalse

class Print a where
  print :: a -> IO ()

instance (ShowPred a ~ flag, Print' flag a) => Print a where
  print = print' (Proxy :: Proxy flag)

class Print' flag a where
  print' :: Proxy flag -> a -> IO ()

instance Show a => Print' HTrue a where
  print' _ x = putStrLn (show x)

instance Print' HFalse a where
  print' _ _ = putStrLn "No show method"

1 个答案:

答案 0 :(得分:1)

初始错误是由

引起的
instance Show a => Print' HTrue a where
  print' _ x = putStrLn (show x)

instance Print' flag a where
  print' _ _ = putStrLn "No show method"

flag ~ HTrue时这些实例重叠。解决方法是用

替换第二个实例
instance Print' HFalse a where ...

你的下一个问题是

instance (ShowPred a ~ flag, Print' flag a) => Print a where
  print = print' (undefined :: flag)

默认情况下,实例上下文中的flag变量不在实例主体的范围内,因此undefined :: flag具有不明确的类型。解决方案是启用ScopedTypeVariables

我的最后一点是,不要让print'获取flag类型的值然后传递undefined,而应该使用

print' :: proxy flag -> a -> IO ()

然后导入Data.Proxy以定义

print = print' (Proxy :: Proxy flag)

现代代理传递方法似乎很新,虽然不太令人满意的版本已经存在了一段时间。有关历史背景,请参阅this question的答案。