Getting the date of the next day in PureScript - date

Is there anyway to write the following function more elegantly?
I can see some patterns but I'm not sure how to abstract them or how to find a simpler way to write the function.
type HasRemainder = Boolean
tomorrow :: Date -> Date
tomorrow date = unsafePartial $ canonicalDate y (fst m) (fst d)
where d :: Tuple Day HasRemainder
d = case toEnum $ 1 + fromEnum (day date) of
Just v -> Tuple v false
Nothing -> Tuple (unsafePartial $ fromJust $ toEnum 1) true
m :: Tuple Month HasRemainder
m = if snd d then
case toEnum $ 1 + fromEnum (month date) of
Just v -> Tuple v false
Nothing -> Tuple (unsafePartial $ fromJust $ toEnum 1) true
else Tuple (month date) false
y :: Year
y = if snd m then
case toEnum $ 1 + fromEnum (year date) of
Just v -> v
-- use 2018 arbitrarly if the conversion from int to Date fails
Nothing -> unsafePartial $ fromJust $ toEnum 2018
else (year date)

I'd do something like this:
import Data.DateTime as DT
import Data.Maybe (maybe)
import Data.Time.Duration (Days(..))
tomorrow :: DT.Date -> DT.Date
tomorrow dt = maybe dt DT.date $ DT.adjust (Days 1.0) (DT.DateTime dt bottom)
Although it will return the input date in the unlikely event that the given date is top (which is 31st December 275759 if I remember correctly).
There is an adjust function for Time and DateTime so it's just an oversight that Date is missing one.

I would try something along those lines
getDatePart datepart defaultval1 defaultval2 =
case toEnum $ defaultval1 + fromEnum datepart of
Just v -> Tuple v false
Nothing -> Tuple (unsafePartial $ fromJust $ toEnum defaultval2) true
getDatePartDefault d datepart defaultval1 defaultval2 =
if snd d then
getDatePart datepart defaultval1 defaultval2
else Tuple datepart false
tomorrow :: Date -> Date
tomorrow date = unsafePartial $ canonicalDate (fst y) (fst m) (fst d)
where d :: Tuple Day HasRemainder
d = getDatePart (day date) 1 1
m :: Tuple Month HasRemainder
m = getDatePartDefault d (month date) 1 1
y :: Tuple Year HasRemainder
y = getDatePartDefault d (year date) 1 2018
Take care: this is not tested

Using succ from Enum:
import Data.Date (Date)
import Data.Enum (succ)
import Data.Maybe (Maybe)
today :: Date
today = ...
tomorrow :: Maybe Date
tomorrow = succ today

Related

Query and Today Function

This formula should filter all results between today and 7 days from now from another sheet but it gives a Formula parse error:
=QUERY(Sheet2!A3:H13;"SELECT A, B, C, D, E, F, G, H WHERE (A >= date '"&text(TODAY(),"yyyy-MM-dd")&"' AND A <= date '"&text(TODAY()+7,"yyyy-MM-dd")& "')"; false)
If I use this formula instead:
=QUERY(Sheet2!A3:H13;"SELECT A, B, C, D, E, F, G, H WHERE (A >= date '2020-11-08' AND A <= date '2020-11-15')"; false)
It works but it would need to change dates manually everytime.
Where I am wrong?
try:
=QUERY(Sheet2!A3:H13;
"where A >= date '"&TEXT(TODAY(); "yyyy-MM-dd")&"'
and A <= date '"&TEXT(TODAY()+7; "yyyy-MM-dd")&"'"; 0)

number_in_month exercise (Tycon Mismatch Error In SML function which tries to build a list)

(* Write a function dates_in_month that takes a list of dates and a month (i.e., an int) and
returns a list holding the dates from the argument list of dates that are in the month.
The returned list should contain dates in the order they were originally given. *)
fun dates_in_months( datelist : (int*int*int) list, month : int) =
if null(tl (datelist))
then if #2(hd (datelist)) = month then #2(hd (datelist)) :: [] else []
else if #2(hd (datelist)) = month
then #2(hd (datelist)) :: number_in_month(tl datelist, month)
else number_in_month(tl datelist, month)
This is the error I get:
hw1.sml:55.22-55.78 Error: operator and operand do not agree [tycon mismatch]
operator domain: int * int list
operand: int * int
in expression:
(fn {2=<pat>,...} => 2) (hd datelist) ::
number_in_month (tl datelist,month)
val it = () : unit
Any help appreciated.
Found the correct way:
fun dates_in_month(datelist : (int*int*int) list, month : int) =
if null(tl (datelist))
then if #2(hd (datelist)) = month
then (hd (datelist)) :: []
else []
else if #2(hd (datelist)) = month
then (hd (datelist)) :: dates_in_month(tl datelist, month)
else dates_in_month(tl datelist, month)
I had several flaws, I was calling the function I copied code over from instead of the function itself + some other syntax issues. In addition, I added just the month element of the date-tuple to the list.

Haskell How to compare IO tuple with normal tuple

I try to compare the tuple members (date) of an IO tuple with a normal tuple.
d1 ->(Integer, Int, Int) and d2 -> IO (Integer, Int, Int),
Is it possible to compare these two tuples?
I've tried something like that:
import Data.Time.Clock
import Data.Time.Calendar
import Data.Time.LocalTime
-- helper functions for tuples
x_fst (x,_,_) = x
x_snd (_,x,_) = x
x_trd (_,_,x) = x
getDate :: IO (Integer, Int, Int)
getDate = do
now <- getCurrentTime
tiz <- getCurrentTimeZone
let zoneNow = utcToLocalTime tiz now
let date#(year, month, day) = toGeorgian $ localDay zoneNow
return $ date -- here I will return an IO tuple -> IO (Integer, Int, Int)
compareDates :: a -> IO (Integer, Int, Int) -> IO Bool
compareDates d1 d2 = do
let year1 = x_fst d1
let year2 = x_fst d2
let month1 = x_snd d1
let month2 = x_snd d2
let day1 = x_trd d1
let day2 = x_trd d2
return $ (year1 == year2 && month1 == month2 && day1 == day2)
But I get the message that I can't compare an IO tuple with a normal tuple:
Couldn't match expected type `(Integer, Integer, Integer)`
with actual type `IO (Integer, Int, Int)`
In the second argument of `compareDates`, namely `date`
Is there a way around it? I would appreciate any help.
Thanks.
With the help of the comment / chat section I got it to work with the following code:
getDate :: IO Day
getDate = do
now <- getCurrentTime
tz <- getCurrentTimeZone
return . localDay $ utcToLocalTime tz now
main = do
d2 <- getDate
return $ fromGregorian 2019 6 15 == d2

Ways to make operator `<` work on custom types

I have type 'a edge = {from: 'a; destination: 'a; weight: int}
and I want to have Printf.printf "%b\n"
( {from= 0; destination= 8; weight= 7}
< {from= 100; destination= 33; weight= -1} ) print true
so I tried this let ( < ) {weight= wa} {weight= wb} = wa < wb
but after this, the < operator only works on 'a edge, and it means that 1 < 2 will raise an error.
the reason why I want to do this is below
I write a leftist tree
type 'a leftist = Leaf | Node of 'a leftist * 'a * 'a leftist * int
let rank t = match t with Leaf -> 0 | Node (_, _, _, r) -> r
let is_empty t = rank t = 0
let rec merge t1 t2 =
match (t1, t2) with
| Leaf, _ -> t2
| _, Leaf -> t1
| Node (t1l, v1, t1r, r1), Node (t2l, v2, t2r, r2) ->
if v1 > v2 then merge t2 t1
else
let next = merge t1r t2 in
let rt1l = rank t1l and rn = rank next in
if rt1l < rn then Node (next, v1, t1l, rn + 1)
else Node (t1l, v1, next, rt1l + 1)
let insert v t = merge t (Node (Leaf, v, Leaf, 1))
let peek t = match t with Leaf -> None | Node (_, v, _, _) -> Some v
let pop t = match t with Leaf -> Leaf | Node (l, _, r, _) -> merge l r
If I cannot make < work as I expect, I must pass in a compare lambda wherever the < is used and substitute it. And I find it unattractive.
OCaml does not support adhoc polymorphism, but you can put the custom operator in a module that you can open locally only where you need it:
module Infix =
struct
let ( > ) = ...
end
...
if Infix.(rt1l < rn) then ...
That way, < will work on trees only inside Infix.( ... ) and still refer to Pervasives.(<) outside it.

number_in_month exercise (Compare two lists with each other)

I have previous asked, but no answer so I will try to rewrite the problem.
I have two lists. first list is dates (int * int * int) list and second list is only months. eg(1,2,3,4,5,6,7,8,9,10,11,12) int list
NOTE: assume any number in month list is not repeated and only 1-12.
I want to check how many dates has the same month as in the list of month.
example: [(87,09,08),(67,08,17),(90,08,23)], [1,5,8] = 2
I know how to recursively compare a list with a number, but i cant figure out how to recursivly compare a list with a list...
fun number_in_months (dates :( int * int * int) list, months : int list)=
if null dates
then 0
else if null months
then 0
else
let
fun dm_notempty (dates : (int * int * int) list, months : int list)=
if (null (tl dates) andalso null (tl months)) andalso (#2 (hd dates) <> hd months)
then 0
else if (null (tl dates) andalso null (tl months)) andalso (#2 (hd dates) = hd months)
then 1
else
let val dates_tl = dm_notempty(tl dates, tl months)
in
if #2(hd dates) = hd months
then dates_tl + 1
else dates_tl + 0
end
in
dm_notempty(dates, months)
end
Break the problem down into smaller problems.
For instance, imagine that you had a function number_in_month that counts the occurrences of a single month.
Then you could write this as
fun number_in_months (dates, []) = 0
| number_in_months (dates, x::xs) = number_in_month(dates, x) + number_in_months(dates, xs)
Now all that remains is that function, which is simpler to write because it only has one list argument, not two.
(Implementing it left as an exercise.)