Haskell 将单实例类转换为函数
我写了一些玩具代码来演示我的问题:Haskell 将单实例类转换为函数,haskell,ghc,Haskell,Ghc,我写了一些玩具代码来演示我的问题: {-# LANGUAGE DataKinds, PolyKinds, UndecidableInstances, ScopedTypeVariables, FlexibleInstances, TypeFamilies #-} import GHC.TypeLits import Data.Proxy main :: IO ()
{-# 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
李>
一个类只有一个实例,涵盖了所有可能的情况,这让人感觉非常像一个函数。下面是我尝试进行的转换(这将替换类/实例):
当然,问题是使用不同类型的递归调用。GHC-7.8(正确)无法推断(HasVal(Div2 x))
,这在类/实例设置中不是问题,因为实例上存在递归类约束。虽然编写一个类的单个实例并不过分繁重,但函数似乎是更好的解决方案有几个原因:
-XIncoherentInstances
并覆盖我的通用实现的人打开了大门编辑:如果在定义类时未使用不连贯性
sumTypeVals
转换为函数
对我来说,最明显的修复方法是以某种方式说服GHC,HasVal x=>HasVal(Div2 x)(我非常乐意做出这样的假设)。然而,我不知道有什么方法可以做到这一点。我试着关闭类型族
Div2
,但这似乎对推断没有帮助,尽管我觉得应该这样做。还有其他想法吗?您是否在type类中实际使用了sumTypeVals
?当我在ghci中对其使用:t
时,我得到了“上下文缩减堆栈溢出”。啊,当您实际使用一个具体参数调用它时,我看到它确实起作用,例如sumTypeVals(Proxy::Proxy 4)
。这是一个奇怪的函数@AndrewC我只是想找到一种最简洁的方法来做类型级的算术(比如2的幂和运算),它在这些数字的类型/类型表示上是多态的。例如,我一直在使用Data.Reflection(kind*
),但我也在考虑GHC.TypeLits(kindNat
)和Data.Natural?有朝一日,单例可以从普通函数生成sumtypeval。
sumTypeVals :: (HasVal x) => Proxy x -> Int
sumTypeVals y | val y == 0 = 0
sumTypeVals (y::Proxy x) = (val y) + sumTypeVals (Proxy :: Proxy (Div2 x))