将单实例类转换为函数

时间:2014-05-22 14:49:19

标签: haskell ghc

我写了一些玩具代码来证明我的问题:

{-# 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中的循环而足够,并且比使用第二个实例作为基本情况更有效,因为:

  1. 我们需要一个新的基础案例实例,用于您想要使用的每种数字表示形式(模式匹配其具体表示为0,比如说)
  2. 这些实例写得很简单
  3. 他们需要-XOverlappingInstances
  4. 一个包含所有可能案例的单个实例的类感觉糟糕很像函数。这是我尝试转换(这取代了类/实例):

    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)),由于对实例的递归类约束,它在类/实例设置中不是问题。虽然编写一个类的单个实例并不过分繁琐,但有几个原因使得函数看起来像是更好的解决方案:

    1. 为什么在函数足够时编写方法(在这种情况下确实会这样)?
    2. 使用课程为以后使用-XIncoherentInstances并覆盖我的通用实施的人敞开大门。 编辑:如果我不使用{{ 1}}在定义类时,no one else can either
    3. 问题:有没有办法将方法 IncoherentInstances转换为功能

      对我来说最明显的解决方法是以某种方式说服GHC sumTypeVals(我非常乐意做出这样的假设)。但是,我不知道有任何办法这样做。我尝试关闭类型系列HasVal x => HasVal (Div2 x),但这似乎并没有帮助推断,尽管我觉得应该这样做。还有其他想法吗?

0 个答案:

没有答案