我写了一些玩具代码来证明我的问题:
{-# LANGUAGE DataKinds,
PolyKinds,
UndecidableInstances,
ScopedTypeVariables,
FlexibleInstances,
TypeFamilies #-}
import GHC.TypeLits
import Data.Proxy
main :: IO ()
main = print $ show $ sumTypeVals (Proxy :: Proxy 4)
class HasVal x where
val :: Proxy x -> Int
instance (KnownNat nat) => HasVal (nat :: Nat) where
val = fromIntegral . natVal
type family Div2 (x :: k) :: k
type instance Div2 4 = 2
type instance Div2 2 = 1
type instance Div2 1 = 0
type instance Div2 0 = 0
-- compute sum of all powers of two
class Sum x where
sumTypeVals :: Proxy x -> Int
instance (HasVal x, Sum (Div2 x)) => Sum x where
sumTypeVals y | val y == 0 = 0
sumTypeVals (y::Proxy x) = (val y) + sumTypeVals (Proxy :: Proxy (Div2 x))
上面的代码编译。 Sum
的实例适用于任何类型编号表示,而不仅仅是TypeLits
。单个实例由于Div2
中的循环而足够,并且比使用第二个实例作为基本情况更有效,因为:
-XOverlappingInstances
。 一个包含所有可能案例的单个实例的类感觉糟糕很像函数。这是我尝试转换(这取代了类/实例):
sumTypeVals :: (HasVal x) => Proxy x -> Int
sumTypeVals y | val y == 0 = 0
sumTypeVals (y::Proxy x) = (val y) + sumTypeVals (Proxy :: Proxy (Div2 x))
故障当然是具有不同类型的递归调用。 GHC-7.8(正确)cannot deduce (HasVal (Div2 x))
,由于对实例的递归类约束,它在类/实例设置中不是问题。虽然编写一个类的单个实例并不过分繁琐,但有几个原因使得函数看起来像是更好的解决方案:
-XIncoherentInstances
并覆盖我的通用实施的人敞开大门。问题:有没有办法将方法 IncoherentInstances
转换为功能?
对我来说最明显的解决方法是以某种方式说服GHC sumTypeVals
(我非常乐意做出这样的假设)。但是,我不知道有任何办法这样做。我尝试关闭类型系列HasVal x => HasVal (Div2 x)
,但这似乎并没有帮助推断,尽管我觉得应该这样做。还有其他想法吗?