为Haskell中的每个Integer创建一个类型?

乔恩·迪顿

我想在Haskell中创建一个数据类型,该数据类型表示整数mod n,并且是一个数据实例,Num可以帮助执行简单的模块化算术运算。我的第一次尝试看起来像这样

data Z n e = El n e
instance (Show n, Show e) => Show (Z n e) where
    show (El n e) = show e ++ " (mod " ++ show n ++ ")"

instance (Integral k, Integral e) => Num (Z k e) where
    (+) (El n a) (El m b) = El n (mod (a + b) n)
    (-) (El n a) (El m b) = El n (mod (a - b) n)
    (*) (El n a) (El m b) = El n (mod (a * b) n)
    negate (El n a) = El n (mod (0 - a) n)
    abs (El n a) = El n a
    signum (El n a) = El n a
    fromInteger i = -- problematic...

这会编译但会带来问题,这不仅是因为它不清楚如何实现(fromInteger因为k超出了范围),而且还因为允许在不带类型错误的情况下添加带有整数mod 4的整数mod 5在第二次尝试中,我试图解决这些问题

data Z n = El Integer
instance (Show n) => Show (Z n) where
    show (El n e) = show e ++ " (mod " ++ show n ++ ")"

instance (Integral k) => Num (Z k) where
    (+) (El k a) (El k b) = El k (mod (a + b) k)
    (-) (El k a) (El k b) = El k (mod (a - b) k)
    (*) (El k a) (El k b) = El k (mod (a * b) k)
    negate (El k a) = El k (mod (0 - a) k)
    abs (El k a) = El k a
    signum (El k a) = El k a
    fromInteger i = El (fromIntegral i) k

Num由于定义冲突k仍然超出范围,我在实现接口时遇到了麻烦如何在Haskell中创建这样的数据类型?

卡尔·布尔

如评论中所述,其想法是利用自然数的类型级别表示,因此对于2,3,4,您具有单独的可识别类型,等等。这需要扩展:

{-# LANGUAGE DataKinds #-}

将自然表示为类型有两种主要方法。首先是定义一个递归数据类型:

data Nat' = Z | S Nat'

DataKinds扩展名会自动提升到类型级别。然后,您可以将其用作类型级别的标记,以定义一系列相关但不同的类型:

{-# LANGUAGE KindSignatures #-}
data Foo (n :: Nat') = Foo Int

twoFoo :: Foo (S (S Z))
twoFoo = Foo 10

threeFoo :: Foo (S (S (S Z)))
threeFoo = Foo 20

addFoos :: Foo n -> Foo n -> Foo n
addFoos (Foo x) (Foo y) = Foo (x + y)

okay = addFoos twoFoo twoFoo
bad =  addFoos twoFoo threefoo -- error: different types

第二种是使用内置的GHC工具,该工具会自动将整数文字提升为,2并将其3提升为类型级别。它的工作方式大致相同:

import GHC.TypeLits (Nat)

data Foo (n :: Nat) = Foo Int

twoFoo :: Foo 2
twoFoo = Foo 10

threeFoo :: Foo 3
threeFoo = Foo 20

addFoos :: Foo n -> Foo n -> Foo n
addFoos (Foo x) (Foo y) = Foo (x + y)

okay = addFoos twoFoo twoFoo
bad =  addFoos twoFoo threefoo -- type error

当您仅使用自然语言来“标记”类型时,通常使用的GHC.TypeLits版本会更方便Nat如果必须对类型进行类型级别的计算,则使用递归版本更容易完成某些计算。

由于我们只需要自然变量作为标签,因此我们可以坚持使用该GHC.TypeLits解决方案。因此,我们将像这样定义您的数据类型:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
import GHC.TypeLits
data Z (n :: Nat) = El Integer

在这种Show情况下,我们需要利用其他一些功能GHC.TypeLits将类型级别转换为可以包含在打印表示形式中Nat的值级别Integer

instance (KnownNat n) => Show (Z n) where
  show el@(El e) = show e ++ " (mod " ++ show (natVal el) ++ ")"

这里有魔术!natVal函数具有签名:

natVal :: forall n proxy. KnownNat n => proxy n -> Integer

意味着对于a而言"KnownNat",无论意味着什么,它都可以采用类型为形式的代理值proxy n,并返回与type-level参数相对应的实际整数n幸运的是,我们原来的元素具有类型Z n符合该proxy n类型模式就好了,这样通过运行natVal el,我们获得的价值层面Integer与该类型相应级别nZ n

我们将在Integral实例中使用相同的魔术

instance (KnownNat k) => Num (Z k) where
    (+) el@(El a) (El b) = El (mod (a + b) k) where k = natVal el
    (-) el@(El a) (El b) = El (mod (a - b) k) where k = natVal el
    (*) el@(El a) (El b) = El (mod (a * b) k) where k = natVal el
    negate el@(El a) = El (mod (0 - a) k) where k = natVal el
    abs el@(El a) = El a where k = natVal el
    signum el@(El a) = El 1
    fromInteger i = El (fromIntegral i)

注意,构造函数中的k消失消失了El,因为它不是数据级别的数量。在需要的地方,可以natVal el使用KnownNat实例进行检索

完整的程序是:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
import GHC.TypeLits
data Z (n :: Nat) = El Integer

instance (KnownNat n) => Show (Z n) where
  show el@(El e) = show e ++ " (mod " ++ show (natVal el) ++ ")"

instance (KnownNat k) => Num (Z k) where
    (+) el@(El a) (El b) = El (mod (a + b) k) where k = natVal el
    (-) el@(El a) (El b) = El (mod (a - b) k) where k = natVal el
    (*) el@(El a) (El b) = El (mod (a * b) k) where k = natVal el
    negate el@(El a) = El (mod (0 - a) k) where k = natVal el
    abs el@(El a) = El a where k = natVal el
    signum el@(El a) = El 1
    fromInteger i = El (fromIntegral i)

它按预期工作:

> :set -XDataKinds
> (El 2 :: Z 5) + (El 3 :: Z 5)
0 (mod 5)
> (El 2 :: Z 5) + (El 3 :: Z 7)

<interactive>:15:18: error:
    • Couldn't match type ‘7’ with ‘5’
      Expected type: Z 5
        Actual type: Z 7
    • In the second argument of ‘(+)’, namely ‘(El 3 :: Z 7)’
      In the expression: (El 2 :: Z 5) + (El 3 :: Z 7)
      In an equation for ‘it’: it = (El 2 :: Z 5) + (El 3 :: Z 7)

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

为网站中的每个产品创建一个页面

来自分类Dev

创建一个“ repin”类型的动作,但为每个用户提供唯一的描述

来自分类Dev

创建一个“ repin”类型的动作,但为每个用户提供唯一的描述

来自分类Dev

使用输入属性 (id, name) 为文本类型的每个输入创建一个标签

来自分类Dev

Haskell:创建列表中每个列表的最后一个元素的列表

来自分类Dev

在熊猫中为每个唯一值创建一个数据框

来自分类Dev

为每个唯一 ID 创建一个图

来自分类Dev

为另一个表中的每个元素创建一个包含一列的新表

来自分类Dev

为与Django中另一个实例相关的每个模型实例创建一个表单字段

来自分类Dev

为列表中的每个数据框创建一个小表

来自分类Dev

C#WPF为列表中的每个对象动态创建一个GUI元素

来自分类Dev

如何在Redux中为每个实例创建一个商店?

来自分类Dev

在另一个表中为每个用户创建新记录

来自分类Dev

为xml文件中的每个节点创建一个ID

来自分类Dev

为R中的每个数据集创建一个图表

来自分类Dev

是否将在C#中为类的每个实例创建一个属性实例?

来自分类Dev

为列表中的每个数据框创建一个小表

来自分类Dev

为列表列表中的每个列表创建一个df

来自分类Dev

使用Redux为ReactJS中的每个数据块创建一个新的div

来自分类Dev

为datagridview中的每个单元格创建一个列表

来自分类Dev

在Ruby on Rails中为每个记录创建一个输入日志表单

来自分类Dev

为python中的每个子图创建一个灰度色条

来自分类Dev

为包含NA(在R中)的每个列创建一个附加(虚拟)列的函数

来自分类Dev

为html目录中的每个文件创建一个按钮

来自分类Dev

为xml文件中的每个节点创建一个ID

来自分类Dev

为列表中的每个项目创建一个新对象

来自分类Dev

如何在 JavaScript 中为来自 JSON API 的每个响应创建一个新的 div?

来自分类Dev

为Android上另一个表中的每个项目创建新表

来自分类Dev

Python 为 DataFrame 中的每个组创建一个字典列表

Related 相关文章

  1. 1

    为网站中的每个产品创建一个页面

  2. 2

    创建一个“ repin”类型的动作,但为每个用户提供唯一的描述

  3. 3

    创建一个“ repin”类型的动作,但为每个用户提供唯一的描述

  4. 4

    使用输入属性 (id, name) 为文本类型的每个输入创建一个标签

  5. 5

    Haskell:创建列表中每个列表的最后一个元素的列表

  6. 6

    在熊猫中为每个唯一值创建一个数据框

  7. 7

    为每个唯一 ID 创建一个图

  8. 8

    为另一个表中的每个元素创建一个包含一列的新表

  9. 9

    为与Django中另一个实例相关的每个模型实例创建一个表单字段

  10. 10

    为列表中的每个数据框创建一个小表

  11. 11

    C#WPF为列表中的每个对象动态创建一个GUI元素

  12. 12

    如何在Redux中为每个实例创建一个商店?

  13. 13

    在另一个表中为每个用户创建新记录

  14. 14

    为xml文件中的每个节点创建一个ID

  15. 15

    为R中的每个数据集创建一个图表

  16. 16

    是否将在C#中为类的每个实例创建一个属性实例?

  17. 17

    为列表中的每个数据框创建一个小表

  18. 18

    为列表列表中的每个列表创建一个df

  19. 19

    使用Redux为ReactJS中的每个数据块创建一个新的div

  20. 20

    为datagridview中的每个单元格创建一个列表

  21. 21

    在Ruby on Rails中为每个记录创建一个输入日志表单

  22. 22

    为python中的每个子图创建一个灰度色条

  23. 23

    为包含NA(在R中)的每个列创建一个附加(虚拟)列的函数

  24. 24

    为html目录中的每个文件创建一个按钮

  25. 25

    为xml文件中的每个节点创建一个ID

  26. 26

    为列表中的每个项目创建一个新对象

  27. 27

    如何在 JavaScript 中为来自 JSON API 的每个响应创建一个新的 div?

  28. 28

    为Android上另一个表中的每个项目创建新表

  29. 29

    Python 为 DataFrame 中的每个组创建一个字典列表

热门标签

归档