12

考虑一个 DateTime 类型,其中日期必须存在,但以秒为单位的时间部分是可选的。如果有时间部分,则可能还有一个可选的毫秒部分。如果存在毫秒,也可能存在纳秒部分。

有很多方法可以解决这个问题,例如:

--rely on smart constructors 
data DateTime = DateTime { days:: Int, 
                           sec :: Maybe Int, 
                           ms :: Maybe Int, 
                           ns :: Maybe Int 
                         }

-- list all possibilities
data DateTime = DateOnly Int 
              | DateWithSec Int Int
              | DateWithMilliSec Int Int Int
              | DateWithNanoSec Int Int Int Int    

-- cascaded Maybe
data DateTime = DateTime Int (Maybe (Int, Maybe (Int, Maybe Int)))

-- cascaded data
data Nano = NoNano | Nano Int
data MilliSec = NoMilliSec | MilliSec Int Nano
data Sec = NoSec | Sec Int MilliSec
data Date = Date Int Sec

你会使用哪种结构(当然不限于上面的例子),为什么?

[意向]

我正在探索 Frege 中日期类型的可能性(http://code.google.com/p/frege/),使用 date4jDateTime作为指导方针(因为 Haskell 的日期和时间库太复杂了,java.util.Date太破碎了)。在我当前的玩具实现中,所有字段都是必需的,但当然最好将用户从不需要的精度中解放出来(并且原始实现具有可选字段)。

所以主要目标是:

  • 安全:必须不惜一切代价避免非法状态
  • 方便:使用类型应该很容易,例如模式匹配会很酷,日历计算应该很容易......

不那么重要的是:

  • 性能:当然,使用类型不应该太慢,但对于典型的用法,它不必挤出最后一个时钟周期
  • 内存:在这很重要的情况下,很容易推导出更紧凑的存储格式
  • 简洁的实现:这是一个库,我愿意添加使事情顺利进行所需的所有代码

也就是说,所有这些都是非常试探性的,不应该太认真。

4

4 回答 4

10

(这不是一个答案,但是评论太长了,在这里会更清楚。)

还有另一种方法可以处理它:拥有一个DateTime存储所有字段的单一类型,以及一个表示精度的参数,例如

data Precision = Days | Seconds | Milliseconds | Nanoseconds deriving (Ord, Eq {- etc -})
data DateTime = DateTime { prec :: Precision,
                           days :: Int, 
                           sec :: Int,
                           ms :: Int,
                           ns :: Int }

并使用智能构造函数将未使用的参数设置为0. 如果你有dateDifference或其他什么,你可以传播精度(Ord实例会使这个整洁)。

(我对这有多好/Haskell-y 知之甚少,但其他解决方案似乎相当混乱,也许这更优雅。)

于 2012-04-13T07:48:41.630 回答
8

“必须不惜一切代价避免非法状态”和“模式匹配会很酷”是很好的原则,在这种情况下是相互冲突的。

此外,日期和时间是粗糙的人类文化结构,有很多边缘情况和不规则的角落。它们不是我们可以在类型系统中轻松编码的那种规则。

所以在这种情况下,我会使用不透明的数据类型、智能构造函数和智能解构函数。当我们想要使用模式匹配时,总会有视图模式和模式保护。

(而且我什至还没有将依赖的可选数据作为激励因素进行讨论。)

于 2012-04-13T07:56:46.653 回答
3

受@dbaupp 解决方案的启发,我想为候选人添加一个幻像类型版本:

-- using EmptyDataDecls
data DayPrec 
data SecPrec 
data MilliPrec 
data NanoPrec 

data DateTime a = DateTime { days :: Int, sec :: Int, ms :: Int, ns :: Int } 

date :: Int -> DateTime DayPrec
date d = DateTime d 0 0 0

secDate :: Int -> Int -> DateTime SecPrec
secDate d s = DateTime d s 0 0

...    

--will work only for same precision which is a Good Thing (tm)
instance Eq (DateTime a) where
  (DateTime d s ms ns) == (DateTime d' s' ms' ns') = [d,s,ms,ns] == [d',s',ms',ns'] 

如果我没记错的话,这使我可以使用一种类型,但如果需要,可以区分精度。但我想也会有一些缺点......

于 2012-04-13T08:39:34.910 回答
0

假设您只想解决这个问题(而不是更通用的问题),那么十进制库可能就是您想要的。

于 2012-04-13T16:53:37.707 回答