As you correctly note, you need to construct a Year, Month, and Day to use exactDate. These types can’t be constructed directly from numbers, because then you would be able to construct invalid values. The docstring you copy-pasted says:
The toEnum function can be used to safely acquire a year value from an integer.
You can search the toEnum function: https://pursuit.purescript.org/search?q=toEnum
toEnum :: forall a. BoundedEnum a => Int -> Maybe a
This means that given an Int, you get a Maybe Year, which is a year or nothing. The same toEnum function can in fact be used to construct also a Day and a Month, because the BoundedEnum instance is also listed in the docs of Day and Month. The final Date construction from integers could look something like this:
makeDate :: Int -> Int -> Int -> Maybe Date
makeDate year month day = case toEnum year of
Nothing -> Nothing
Just y -> case toEnum month of
Nothing -> Nothing
Just m -> case toEnum day of
Nothing -> Nothing
Just d -> exactDate y m d
I didn’t need to construct a Month from a number, I could have used a month constructor instead.
This function can be used in your code, again, using pattern-matching to isolate the construction failure:
case makeDate 2021 1 5 of
Nothing -> log "Provided numbers don't form a valid date."
Just date -> log ("Success. The date is: " <> show date)
The code for makeDate is very repetitive and nested, so there are some ways to make it shorter.
The problem is that each step may fail, and we handle each failure separately. Ideally, you would handle the construction failure only once (when pattern matching on the resulting Maybe Data), and short-circuit all the Nothings which could be created on the way to return Nothing immediately.
The do-notation is a convenient way to do just that:
makeDate :: Int -> Int -> Int -> Maybe Date
makeDate year month day = do
y <- toEnum year
m <- toEnum month
d <- toEnum day
exactDate y m d
Or you can use applicative composition operators. This takes some practice to be comfortable with:
makeDate :: Int -> Int -> Int -> Maybe Date
makeDate year month day =
join $ exactDate <$> toEnum year <*> toEnum month <*> toEnum day
or the same thing, using slightly different combinators:
makeDate :: Int -> Int -> Int -> Maybe Date
makeDate year month day =
join $ lift3 exactDate (toEnum year) (toEnum month) (toEnum day)
To sum up, in Purescript, all failures must be handled. You normally don’t have functions which would throw an exception or create illegal state when the input is bad. Failures are instead indicated by returning a Nothing, or some other “error” value. Working with these comfortably requires some extra plumbing, such as the do-notation.
EDIT: to work with Javascript dates, you can use the JSDate type: https://pursuit.purescript.org/packages/purescript-js-date/7.0.0/docs/Data.JSDate#t:JSDate . JSDate can be converted to Date or DateTime using the provided functions.