Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
仅支持加法和减法的Haskell类型_Haskell_Time_Timer - Fatal编程技术网

仅支持加法和减法的Haskell类型

仅支持加法和减法的Haskell类型,haskell,time,timer,Haskell,Time,Timer,假设我有一个这样定义的类型 data Seconds | Seconds Integer 我可以像这样定义一个倒计时函数 decrementTimer :: Seconds -> Seconds -> Seconds decrementTimer (Seconds internalSecondsOne) (Seconds internalSecondsTwo) = Seconds $ internalSecondsOne - internalSecondsTwo decremen

假设我有一个这样定义的类型

data Seconds | Seconds Integer
我可以像这样定义一个倒计时函数

decrementTimer :: Seconds -> Seconds -> Seconds
decrementTimer (Seconds internalSecondsOne) (Seconds internalSecondsTwo) = Seconds $ internalSecondsOne - internalSecondsTwo
decrementTimer :: Seconds -> Seconds -> Seconds
decrementTimer a b = a - b
但这似乎是乏味和混乱的,我必须为每一个时间的代表做这件事;小时、分钟、时间段保存秒、分钟和小时的数据

我真正想做的是“实现”(?)Num类型的类,所以我可以做类似的事情

decrementTimer :: Seconds -> Seconds -> Seconds
decrementTimer (Seconds internalSecondsOne) (Seconds internalSecondsTwo) = Seconds $ internalSecondsOne - internalSecondsTwo
decrementTimer :: Seconds -> Seconds -> Seconds
decrementTimer a b = a - b

但是我不需要支持乘法和除法吗?把秒除以秒是没有意义的。我如何制作一个支持加减法的类型?或者,如果这是不可能的,或者我的推理完全错误,那么在Haskell中实现这一点的惯用方法是什么?

如果要限制对数据类型的操作,标准技巧是不导出类型的构造函数。这样函数就不能访问数据的内部,只能使用您提供的操作。所以你想要的是:

module Seconds (Seconds) where

newtype Seconds = Seconds Integer

mkSeconds  :: Integer -> Seconds
addSeconds :: Seconds -> Seconds -> Seconds
subSeconds :: Seconds -> Seconds -> Seconds
请注意,模块导出的是
,而不是
秒(..)
,因此类型
可用,但构造函数不可用。现在不可能编写函数了

dangerousMult :: Seconds -> Seconds -> Seconds
dangerousMult (Seconds i) (Seconds j) = Seconds (i * j)

如果使用标准prelude,您可能会遇到运气不佳的情况,Num typeclass要求您实现对该数据类型没有意义的函数。基本上有三种选择

  • 将函数命名为+和-。这可能是首选方案
  • 实现Num typeclass,但是让没有意义的函数抛出错误。这样做的缺点是,它将编译时错误转换为运行时错误
  • 使用不同的前奏,例如将Num out中的函数拆分为其他类型类。这个选项在数学上是最正确的,但也有点不方便,因为它没有使用标准的前奏曲 您可以使名为
    +
    -
    的函数在秒数上工作,但是如果不将
    秒数作为
    Num
    的实例,它就无法与
    Num
    类型类中的
    +
    -
    相同(因此,这将导致任何获得
    Seconds
    值作为通用
    Num a
    的代码也可以使用其他
    Num
    函数)

    您所要做的就是显式导入前奏曲,要么隐藏
    +
    -
    要么导入它

    问题是,任何使用
    +
    -
    的代码也必须采取措施来解决前奏
    +
    -
    的歧义;要么范围内只有一个版本的
    +
    ,要么至少其中一个必须始终使用限定名称引用(一些
    Prelude.++
    P.++
    S.++
    Seconds.++
    ,等等的变体)。对于一个晦涩的名字,这有时是可以接受的。对于像
    +
    这样常见和基本的东西,这可能不是一个好主意

    您可以通过在一个新类型类(比如
    PlusMinus
    )中生成
    +
    -
    函数,并编写
    instance Num a=>PlusMinus a,其中(+)=(Prelude.+)
    等,使该选项变得更好。然后,您还可以将
    Seconds
    作为
    PlusMinus
    的实例

    这给您带来的好处是,任何想要使用新的
    +
    操作符的代码至少可以安全地隐藏Prelude的
    +
    ,同时仍然能够在其他
    Num
    类型上使用
    +
    。不过,它仍然会给想要使用
    +
    的每个模块带来一些麻烦,而且它有可能被配置使用(有一天,某人可能会看到
    +
    上被使用,而对所有这些都不太熟悉,并假设他们可以在
    上使用其他数值操作)

    最好是生成不被调用的函数
    +
    -
    。如果需要,可以使用包含
    +
    -
    的新多字符运算符(尽管要找到其他库不使用的运算符可能会比较困难)


    这是我曾经采取的一种方法,有点过分,但也有点令人满意

    问题是我有代表绝对位置的向量,也有代表偏移量的向量。我认为加和减偏移量是有意义的,但不是位置。然而,在位置上加一个偏移量得到一个位置,或者减去两个位置得到一个偏移量,甚至用一个标量t乘以一个偏移量也是有意义的o获得补偿

    我最后做的是定义一个类型类,如下所示:

    {-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-}
    
    class Addable a b where
        type Result a
        (|+|) :: a -> b -> Result a b
    
    instance Addable Offset Offset where
        type Result Offset Offset = Offset
        o |+| o = ...
    
    instance Addable Position Offset where
        type Result Position Offset = Position
        p |+| o = ...
    
    instance Addable Offset Position where
        type Result Offset Position = Position
        o |+| p = p |+| o
    

    因此,你最终使用了
    |+|
    而不是
    ++
    ,但它看起来仍然有点像你习惯于思考的代数(一旦你习惯了
    |+|
    +
    的“通用”版本的惯例,等等),它允许您对类型系统中哪些操作有意义的许多规则进行编码,以便编译器可以为您检查。缺点是大量的样板文件定义了所有实例,但对于少量固定数量的类型,您只需执行一次



    1您需要扩展来实现这一点;原则上这有点不安全,因为在某个地方可能存在
    Num
    Seconds
    实例,这将使
    Seconds
    以两种不同的方式匹配
    PlusMinus

    首先–为什么不使用现有的物理量–库?例如,。将自己的时间限制在秒是很奇怪的,因为秒实际上只是许多可能的时间单位中的一个,尽管您使用的是
    Integer
    而不是更明显的
    Double
    这一事实表明您确实对固定时间-r感兴趣