2012-04-13 8 views
12

日付が存在する必要があるDateTime型を考えてみましょう。ただし、秒単位の時間はオプションです。時間部分がある場合は、オプションのミリ秒部分もあります。ミリ秒がある場合は、ナノ秒の部分もあります。これに対処するための多くの方法がありますHaskellの "Dependent optional"データ

、例えば:

--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 

構築物はあなたが(もちろん上記の例に限定されない)を使用するでしょう、そして、なぜ?

[意図]

私は、フレーゲ(http://code.google.com/p/frege/)の日付タイプの可能性を模索しdate4jのDateTimeガイドラインとしてを使用して(Haskellの日付と時刻のlibがあまりにも複雑であるとして、そしてjava.util.Dateよあまりにも壊れた)。私の現在のおもちゃの実装では、すべてのフィールドは必須ですが、もちろん、望ましくない精度からユーザーを解放するのが良いでしょう(元の実装にはオプションフィールドがあります)。

だから、主な目標は以下のとおりです。

  • 安全性:不正な状態はすべてのコスト
  • 都合の良い時に避けなければならない。例えば、タイプで動作するように簡単にする必要がありますパターンマッチングは、クールになる、カレンダーの計算が簡単なはず...

それほど重要ではないが、次のとおりです。

  • パフォーマンス:もちろんタイプでの作業が遅すぎることが、典型的なのためにはなりませんそれは最後のクロックサイクルをsqeezeする必要はありません。
  • メモリ:これが本当に重要な場合は、よりコンパクトなストレージフォーマットを導出するのが簡単です
  • 簡潔な実装:ライブラリです。物事をスムーズにするために必要なすべてのコードを追加する意思がある

しかし、これはすべて非常に暫定的なものであり、あまり深刻ではありません。

+1

あなたの要件について詳しく知りませんが、これはわかりません。それは、型、性能、空間の考慮事項(迂回数、フィールド展開の機会など)を定義する予定の関数を含む多くのものに依存する可能性があります。 – hammar

+2

もう一つのオプションは、単純なコンストラクタ 'DateTime = DateTime [Int ] 'を呼び出すと、' mkDateTimeWithSec :: [Int] - > DateTime'、 'isDateTimeWithSec :: DateTime - > Bool'、' getSeconds :: DateTime 'のような関数を介して、 - > Maybe Int'などパターンマッチングを使用することはできません(ガードに置き換えてアクセサー関数を使用するだけで恐ろしいことではないかもしれませんが)すべてをチェックできるので、ユーザーが違法な状態を作成することを明示的に禁止します(例えば、負の値をチェックすることができます)。 –

+1

不特定のデータがデフォルトではなくオプションであるという元の主張は、非常に工夫された型になります - それを操作する関数を書くのは面倒です。 Just nanosecondsとNothing秒の値のようなジャンクを持つこともできます。私はタックを変え、不特定の時間値のためのデフォルト値0が理論的にも実践的にも有害であるかどうかを考え出します。 –

答えて

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インスタンスはこれをきれいにします)。

(私はこれがどのように良い/ハスケル-Yについて少し考えを持っているが、他の解決策は、多分これは、よりエレガントで、かなり厄介なようです。)

+1

私はこの種の解決方法を自分自身で提案しようとしていました。 – augustss

+1

これは、私が示唆しているようなものですが、 'sec'、' ms'、 'ns'フィールドを1つの' withinDay'フィールドに畳ませるよう提案していたでしょう。その解釈は秒、または 'prec'フィールドの値に応じてナノ秒単位で指定します。 –

+2

@DanielWagner、データ型を変更しなくても動作するとは思えません。(32ビットプラットフォームの場合)Intの最大値は数十億ですが、1日で86兆ナノ秒です。 – huon

8

「違法状態はすべてのコストで避けなければなりません」 「パターンマッチングはクールだ」というのは、この場合、互いに直接衝突するという罰則です。

さらに、日付と時刻は、多くのエッジケースと不規則なコーナーを持つぎこちない人間の文化的構成です。型システムで簡単にエンコードできるルールのようなものではありません。

この場合、私は不透明なデータ型、スマートコンストラクタ、スマートデコンストラクタを使用します。パターンマッチングを使用したいときは、常にパターンとパターンガードを表示します。

(そして私も動機要因として依存オプションのデータを説明していない。)

+0

ビューパターンはここでは非常に便利ですが、現在私のターゲット言語Fregeではサポートされていません(他の点ではHaskellとよく似ています) – Landei

+0

これは残念です。私はこの方法にかかわらず行くと思うし、Fregeが将来のビューパターンをサポートすることを願っています。 – dave4420

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'] 

私は間違っていないとすれば、これによって1つのタイプで作業することができますが、必要に応じて精度を区別することができます。しかし、私はそこにもいくつかの欠点があると思います...

+0

私はこれがいくつかのことを少し厄介なものにしてくれると思わない限り、これが好きです。 'DateTime DayPrec'と' DateTime SecPrec'の間の時間を計算し、正確な精度(おそらく 'DayPrec')を得る:関数の依存関係や型のファミリのようなものや、多くのインスタンス宣言のタイプが必要になると思います。 (そのような操作が望ましく、よく定義されていると仮定します) – huon

+1

私は、異なる精度を混ぜ合わせるべきではなく、計算する前に明示的に精度を切り捨てたり拡張したりするべきだと思います。 – Landei

+0

'parseDateTime'の型は何でしょうか? (プログラマが元の文字列の精度を維持したいが、コンパイル時に期待する精度がわからない場合) – dave4420

0

あなたはこの問題(より一般的なものとは対照的に)を解決したいとすれば、decimalライブラリはあなたが望むものかもしれません。