A type Data



The following example shows how a type Date with the relevant operations can be defined. Note the redefinition of the type MyDate to change the printing method.


let rec
type Date <-> 
       [ Day :int;
         Month :int;
         Year :int; 
         Print := meth() :string is
            implode({stringofint(self.Day);"/"; 
                     stringofint(self.Month);"/";
                     stringofint(self.Year)});             
         private Leap := meth(Year :int) :bool is    
           use isdiv := fun(x :int, y:int) :bool 
                          is (x mod y = 0) 
           in  if isdiv(Year, 100) 
                 then isdiv(Year,400) 
                 else isdiv(Year,4);   
         IsLeap := meth() :bool is self.Leap(self.Year); 
         private MonthToDays := 
                  meth(Month :int, Year :int) :int is
                    use MonthTable := {0;31;59;90;120;151;181;
                                     212;243;273;304;334;365}
                    in
                       nth(MonthTable, Month) + 
                       if (Month > 2) And (self.Leap(Year)) 
                          then 1 else 0;
         DateToDays := meth() :int is
           use PastYears := self.Year - 1 
                 %complete years between 1/1/1 and d%
            in  PastYears*365 +( PastYears div 4)  + 
                 (PastYears div 400) +
                  self.MonthToDays(self.Month, self.Year) + 
                   self.Day - (PastYears div 100);
         LessEqDate := meth(d :Date) :bool is
            self.DateToDays <= d.DateToDays;
         private DaysToDate := meth(Days :int)  :Date is
            use Days := var (Days - 1)     
            and E4Years := 365 * 4 + 1
            ext E100Years := E4Years * 25 - 1 
            ext E400Years := E100Years * 4 + 1   
            ext year := var (((at Days) div E400Years) * 400 + 1)
            in  (Days <- (at Days) mod E400Years;
                year <- at year + ((at Days) div E100Years) * 100;
                Days <- (at Days) mod E100Years;
                year <- at year + ((at Days) div E4Years) * 4;
                Days <- (at Days) mod E4Years;
                year <- at year + ((at Days) div 365);
                Days <- ((at Days) mod 365) + 1;
                use month := var 0
                in  (month <- ((at Days) div 30) + 1;
                    while at Days <= self.MonthToDays(at month, at year) do
                      month <- at month - 1;
                    Days <- at Days - self.MonthToDays(at month, at year);
                    mkDate([Year := at year;
                             Month := at month; 
                             Day := at Days ])) );
         AddDays := meth(Days :int) :Date is
           self.DaysToDate(self.DateToDays + Days)
         ]
      before mk(this)
      if Not(
         use y := this.Year 
         and m := this.Month 
         and d := this.Day
         and within := fun(V :int, m :int, M :int) :bool is
               if V >= m And V <= M 
                 then true
                 else false
         in y >= 0 And
            within(m, 1, 12) And
            within(d, 1, if m=2 
                           then if y mod 4 = 0
                                  then 28 
                                  else 29
                           else if m isin {4;6;9;11}
                                  then 30
                                  else 31) )
       do failwith "wrong values for a data";

let mkDate := 
        fun(G:int, M:int, A:int):Date is
            mkDate( [Day := G; Month := M; Year := A] );

% Another Date type to print dates with the format "Month D, Y" %

let rec type
MyDate <-> 
     is Date and
      [Print := meth() :string is
          use MonthName := {"January";"February";"March";"April";"May";
                            "June";"July";"August";"September";"October";
                            "November";"December"}
           in
            implode({nth(MonthName,self.Month);" ";
                     stringofint(self.Day);", ";
                     stringofint(self.Year)});
        AddDays := meth(Days :int) :MyDate is
           inMyDate( (self As Date)!AddDays(Days), [ ] )
        
        ];

let mkMyDate := 
        fun(G:int, M:int, A:int):Date is
            mkMyDate( [Day := G; Month := M; Year := A] );