2017-04-11 11 views
1

Idrisを探索する私の旅では、私は小さな日付処理モジュールを "慣用的な"方法で書こうとしています。ここまで私がこれまで持っていたことがあります。日付を計算するときにFin nとIntegerを正しく処理する方法は?

まず私は日、月、年を表現するためにいくつかの基本的な種類があります:

module Date 

import Data.Fin 

Day : Type 
Day = Fin 32 

data Month : Type where 
    January : Month 
    February : Month 
    .... 

toNat : Month -> Nat 
toNat January = 1 
toNat February = 2 
... 

data Year : Type where 
    Y : Integer -> Year 

record Date where 
    constructor MkDate 
    day : Day 
    month : Month 
    year : Year 

私はDateに日のいくつかの番号を追加する機能addDaysを実装したいと思います。このため私は、次の補助関数を定義した:私は、現在では日の追加数はフィット非常に基本的なケースで立ち往生しています

addDays : Date -> Integer -> Date 
addDays (MkDate d m y) days = 
    let maxDays = daysInMonth m y 
     shiftedDays = finToInteger d + days 
    in case integerToFin shiftedDays (finToNat maxDays) of 
      Nothing => ?hole_1 
      Just x => MkDate x m y 

isLeapYear : Year -> Bool 
isLeapYear (Y y) = 
    (((y `mod` 4) == 0) && ((y `mod` 100) /= 0)) || ((y `mod` 400) == 0) 

daysInMonth : Month -> Year -> Day 
daysInMonth January _  = 31 
daysInMonth February year = if isLeapYear year then 29 else 28 
daysInMonth March _  = 31 
... 

そして最後には、としてaddDaysを定義しよう月の期間。

Date.idrでaddDaysでDate.caseブロックの右側のチェック:92:11期待されるタイプ 日でこれが原因のタイプの不可解さ

When checking argument day to constructor Date.MkDate: 
     Type mismatch between 
       Fin (finToNat maxDays) (Type of x) 
     and 
       Day (Expected type) 

     Specifically: 
       Type mismatch between 
         finToNat maxDays 
       and 
         32 

ここでは、コンパイラの出力がありますmaxDaysは、明らかにDayである必要があります。単純にFin 32です。

私は、これは、それ自体がIntegerタイプにmodの非全体から来てisLeapYearの非全体から茎daysInMonthの非全体に関連するかもしれないと思います。

答えて

1

Idrisは、特に依存型を使用している場合は、すべてのステップであなたの証拠が必要です。すべての基本的な考え方は、すでにこの質問に書かれています:

Is there a way to define a consistent date in a dependent type language?

私はあなたの実装をコメントし、解答のコードを翻訳しますにイドリス(それはおそらくAgdaです)。また、コード全体を調整するための調整はほとんど行いませんでした。まず

Monthは少し単純に書き込むことができます:私はすべての12ヶ月の書き込みするつもりはない

data Month = January 
      | February 
      | March 

、それは単なる例です。

セカンドIntegerで動作するほとんどの機能は、合計ではありませんので、YearタイプはNat代わりのIntegerを保存する必要があります。これは良いです:

data Year : Type where 
    Y : Nat -> Year 

それはisLeapYearチェック総作るのに役立ちます:

isLeapYear : Year -> Bool 
isLeapYear (Y y) = check4 && check100 || check400 
    where 
    check4 : Bool 
    check4 = modNatNZ y 4 SIsNotZ == 0 

    check100 : Bool 
    check100 = modNatNZ y 100 SIsNotZ /= 0 

    check400 : Bool 
    check400 = modNatNZ y 400 SIsNotZ == 0 

次へ]を、それはFin 32としてDayを作るために良いではありません。毎月の日の数を具体的に指定する方がよいでしょう。

daysInMonth : Month -> Year -> Nat 
daysInMonth January _ = 31 
daysInMonth February year = if isLeapYear year then 29 else 28 
daysInMonth March _ = 31 

Day : Month -> Year -> Type 
Day m y = Fin (daysInMonth m y) 

そして、あなたは少しあなたのDate記録調整する必要があります。

record Date where 
    constructor MkDate 
    year : Year 
    month : Month 
    day : Day month year 

まあ、今についてaddDaysを。この関数は、従属型を扱うときには実際は非常に複雑です。あなたが正しく気づいたように、いくつかのケースがあります。たとえば、現在の月の合計、次の月の合計、数カ月のスキップ、合計の年の値。そして、そのようなケースのそれぞれには証拠が必要です。今月の合計が確実に収まるようにするには、その事実の証拠を提示する必要があります。

コードに移動する前に、日付ライブラリのタイプのないバージョンであっても、increadibly hardであることを警告したいと思います。さらに、従属型のある言語でフル機能のバージョンを実装しようとしている人はまだいません。だから、私の解決策は最高のものとはほど遠いかもしれません。しかし、少なくともあなたが間違っていることについてあなたにいくつかのアイデアを与えるべきです。

その後、あなたはこのようないくつかの関数を記述して起動することもできます。

daysMax : (d: Date) -> Nat 
daysMax (MkDate y m _) = daysInMonth m y 

addDaysSameMonth : (d : Date) 
       -> (n : Nat) 
       -> Prelude.Nat.LT (finToNat (day d) + n) (daysMax d) 
       -> Date 
addDaysSameMonth d n x = ?addDaysSameMonth_rhs 

まあ、Finと数値の操作は非常にData.Fin moduleの現在の状態に応じて制限されています。したがって、2つのNatを追加し、与えられた証明を使用してFinに変換することも困難な場合があります。また、このようなものを書くことそれはそうより難しいです:)

ここでは、コードの完全なスケッチです:いつものように非常に役立つあなたの洞察力のためのhttp://lpaste.net/3314444314070745088

+0

おかげでたくさん!私は実際に日付を使って働くことを知っています*と*時間は非常に難しく、大きな危険を伴います。私はレッスンを学ぶためにタイムゾーンの問題で十分に噛まれました。私の野心はもっと控えめで、日々の仕事は、私が取り組んでいる別の問題を引き起こしたヤク剃り運動として浮上しました。私の野望は本格的な日付ライブラリー(まだではない)を作成するのではなく、実行可能で興味深い「日付」タイプを持つことです – insitu

+1

他のSOの質問へのリンクもありがとうございます:https:// forge .ocamlcore.org/scm/viewvc.php/trunk/calendarFAQ-2.6.txt?view =マークアップ&ルート=カレンダー – insitu