仿真“静态依赖类型”在Haskell中(如果需要,使用GHC扩展) [英] Emulate "static dependent types" in Haskell (if needed, with GHC extensions)

查看:150
本文介绍了仿真“静态依赖类型”在Haskell中(如果需要,使用GHC扩展)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个类型类 Vec ,它实现了比方说有理数的向量空间的理论。

  class Vec a where 
(+):: a - > a - > a
zero :: a
- 其余省略

数字n,我可以很容易地构造一个 Vec 的实例,其基础类型是有理列表的类型,并且实现了维度为n的向量空间。我们在以下方面取n = 3:

pre $ newtype RatList3 = RatList3 {list3 :: [Rational]}
instance VEC RatList3其中
V + W = RatList3(zipWith(前奏。+)(项目list3 v)的(项目list3瓦特))
零= RatList3(坐3(重复0))

对于另一个自然数,例如计算的数字,我可以写出

  f :: Int  - > Int 
fx = x * x - 一些复杂的函数
n :: Int
n = f 2
newtype RatListN = RatListN {listN :: [Rational]}
instance Vec RatListN其中
V + W = RatListN(zipWith(前奏。+)(listN v)(listN瓦特))
零= RatListN(取n(重复0))

现在我有两种类型,一种是维度为3的向量空间,另一种是维度为n的向量空间。但是,如果我想将模板中的实例声明放在实例Vec RatList?中,我不知道主程序最终会使用哪个模块,问题作为类型 RatList?不知道它属于哪个n。



为了解决这个问题,我试图做以下几行:

 类HasDim a其中
dim :: Int

实例(HasDim a,小数a)=> Vec [a]其中
v + w =​​ ...
zero = take dim(重复(fromRational 0))

- 在主模块中
实例HasDim合理的地方
dim = n - 一些整数

这当然不起作用因为 HasDim 中的 dim 独立于类型变量 a 实例(HasDim a)=> Vec [a] 目前还不清楚哪种类型的 dim 需要。我试图通过引入另一种类型来绕过第一个问题:

  newtype Dim a = Dim {idim :: Int} 

然后我可以写出

  class HasDim a其中
dim :: Dim a

然而,它是我不清楚如何在实例(HasDim a)=>中使用它。 Vec [a]其中。另外,我的整个解决方案对我来说看起来相当麻烦,而提出的问题看起来很简单。 (我认为使用C ++模板编写代码很容易。)



编辑

我已经除了ephemient的答案,因为没有类型算术,它以我想要的方式解决了我的问题。仅供参考,我的最终解决方案如下:

 类Vec a其中
zero :: a
- ...

n :: Int
n = 10

newtype RatListN = RatListN [Rational]

实例Vec RatListN其中
zero = RatListN。取n $ repeat 0
- ...


解决方案

这似乎是类型算术会让你得到一些你想要的东西的情况。 / b>

 数据零
数据Succ a
类型One = Succ零
类型Two = Succ One
type Three = Succ Two
- ...

class NumericType a where
toNum ::(Num b)=> a - > b
instance NumericType Zero其中
toNum = const 0
实例(NumericType a)=> NumericType(Succ a)其中
toNum(Succ a)= toNum a + 1

data RatList ab = RatList {list :: [b]}
instance(NumericType a,数字b)=> Vec(RatList a b)其中
zero = RatList。 take(toNum(undefined :: a))$ repeat 0

现在 RatList两个Int RatList三个Int 是不同的类型。另一方面,这确实会阻止您在运行时实例化新维度。


Assume that I have a type class Vec that implements the theory of, say, vector spaces over the rationals.

class Vec a where
    (+)  :: a -> a -> a
    zero :: a
    -- rest omitted

Now given a natural number n, I can easily construct an instance of Vec whose underlying type is the type of lists of rationals and which implements a vector space of dimension n. We take n = 3 in the following:

newtype RatList3 = RatList3 { list3 :: [Rational] }
instance Vec RatList3 where
    v + w = RatList3 (zipWith (Prelude.+) (list3 v) (list3 w))
    zero  = RatList3 (take 3 (repeat 0))

For another natural number, for example a calculated one, I can write

f :: Int -> Int
f x = x * x -- some complicated function
n :: Int
n = f 2
newtype RatListN = RatListN { listN :: [Rational] }
instance Vec RatListN where
    v + w = RatListN (zipWith (Prelude.+) (listN v) (listN w))
    zero  = RatListN (take n (repeat 0))

Now I have two types, one for vector spaces of dimension 3 and one for vector spaces of dimension n. However, if I want to put my instance declaration of the form instance Vec RatList? in a module where I don't know which n my main program eventually uses, I have a problem as the type RatList? doesn't know which n it belongs to.

To solve the problem, I tried to do something along the following lines:

class HasDim a where
    dim      :: Int

instance (HasDim a, Fractional a) => Vec [a] where
    v + w = ...
    zero  = take dim (repeat (fromRational 0))

-- in the main module
instance HasDim Rational where
    dim = n -- some integer

This doesn't work, of course, because dim in HasDim is independent of the type variable a and in instance (HasDim a) => Vec [a] it is not clear which type's dim to take. I tried to circumvent the first problem by introducing another type:

newtype Dim a = Dim { idim :: Int }

Then I can write

class HasDim a where
    dim      :: Dim a

However, it is not clear to me how to use this in instance (HasDim a) => Vec [a] where. Also my whole "solution" looks rather cumbersome to me, while the posed problem looks simple. (I think it is easy to code this with C++ templates.)

EDIT

I have excepted ephemient's answer because without the type arithmetic it solved my problem the way I wanted to. Just for information, my final solution is along the following lines:

class Vec a where
    zero :: a
    -- ...

n :: Int
n = 10

newtype RatListN = RatListN [Rational]

instance Vec RatListN where
    zero = RatListN . take n $ repeat 0
    -- ...

解决方案

This seems like a case where type arithmetic would get you some of what you want.

data Zero
data Succ a
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
-- ...

class NumericType a where
    toNum :: (Num b) => a -> b
instance NumericType Zero where
    toNum = const 0
instance (NumericType a) => NumericType (Succ a) where
    toNum (Succ a) = toNum a + 1

data RatList a b = RatList { list :: [b] }
instance (NumericType a, Num b) => Vec (RatList a b) where
    zero = RatList . take (toNum (undefined :: a)) $ repeat 0

Now RatList Two Int and RatList Three Int are different types. On the other hand, this does prevent you from instantiating new dimensionalities at runtime…

这篇关于仿真“静态依赖类型”在Haskell中(如果需要,使用GHC扩展)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆