I don’t know yet even anything about monads, same comment about type classes.

Using **VSCode** adding function signatures look often veird compared to Elm.

Anyway, it’s still fun to learn and write some code in Haskell.

Here is the latest example for which I used ca. two months creating five different versions: See the source and an example of running it.

I made a new library SolarCurrent as I didn’t understand several things of the module Solar in Github, e.g. how to give the ZonedTime function arguments as described in the package Data.Time .

nice, learning is always good. the 3 instances of `read $ show $ _`

should probably be a call to `fromIntegral`

Awesome! I would actually have some use for SolarCurrent: I need to take some action when the sun rises above -7 degrees below the horizon and again take some action when the sun sinks below this threshold. (I’ve been using Python ephem for that.)

Being no astronomer, I can not make enough sense of the source code to derive how to use SolarCurrent to the above goal.

A thing you might perhaps enjoy is creating nice documentation with haddock. That would make it easier for other Haskellers to use the library.

Not in this case, as I don’t care about milliseconds of the read posix-seconds, so I round the Double to Integer and it works ok and it’s easy to calculate using the function mod the hours, minutes and seconds, as integer division remainders . However, I converted the integers back to Doubles and summed up to Double minutes to be used in further calculation.

```
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
[1 of 3] Compiling SolarCurrent ( SolarCurrent.hs, interpreted )
[2 of 3] Compiling Main ( Hyper_Helios.hs, interpreted )
Ok, two modules loaded.
ghci> import SolarCurrent
ghci> import Data.Time
ghci> :set +t
ghci> currentTime <- getCurrentTime
currentTime :: UTCTime
ps = round $ utcTimeToPOSIXSeconds currentTime
ps :: Integral b => b
ghci> hours = read $ show $ mod (div ps 3600) 24
hours :: Read a => a
ghci> minutes = read $ show $ mod (div ps 60) 60
minutes :: Read a => a
ghci> seconds = read $ show $ mod ps 60
seconds :: Read a => a
ghci> ps
1725394416
it :: Integral b => b
ghci> (year, month, day) = toGregorian . utctDay $ currentTime
day :: DayOfMonth
month :: MonthOfYear
year :: Year
ghci> show (year, month, day)
"(2024,9,3)"
it :: String
ghci> currentTime
2024-09-03 20:13:35.986237 UTC
it :: UTCTime
```

For some reason hour, minutes and seconds cannot be shown in ghci REPL.

I use then here multifying by 1 as a trick to show the values.

```
ghci> show hours
"*** Exception: Prelude.read: no parse
ghci> 1*hours
20
it :: (Num a, Read a) => a
ghci> 1*minutes
13
it :: (Num a, Read a) => a
ghci> 1*seconds
36
```

As you can see, the calculated values of date and time are the same as shown in the UTC time stamp of the currentTime except the seconds with decimals.

This is interesting too, howcome it shall not cause an error, adding integer hours and double timezone without conversion integer first to double by function **fromIntegral**:

```
ghci> timeZone
2.0
it :: Double
ghci> localTime = timeZone + hours
localTime :: Double
ghci> localTime
22.0
it :: Double
```

Well, the timeZone is Double but hours not!

The sum result localTime is also Double.

This must be some speciality of the library Data.Time.

So the hours, minutes and seconds are not of type Integral but something odd as result of read:

```
hours :: Read a => a
minutes :: Read a => a
seconds :: Read a => a
```

I think the odd things begin already when the UTC time, currentTime is read and converted to POSIX-seconds. Why there is added the character ‘s’ to the end of seconds? And howcome it’s possible to do some trivial mathematics with it without stripping the ending s away? Are that kind of tricks common to Haskell?

```
ghci> currentTime <- getCurrentTime
currentTime :: UTCTime
ghci> utcTimeToPOSIXSeconds currentTime
1725401595.989455s
it :: time-1.12.2:Data.Time.Clock.Internal.POSIXTime.POSIXTime
```

Python’s name ephem must be from ephemeris, meaning:

A table giving the coordinates of a celestial body at a number of specific times during a given period. Well, I’ll look at it.

I agree, a good documentation is important, so I’ll check haddock.

My module SolarCurrent contains exactly the same collection of astronomic functions as the spreadsheets by NOAA (in Excel, Open Office / Libre Office)

I have very little commenting there in the source because all is completely and very well explained on the NOAA pages.

Btw, the limit -7 degrees below horizon is near the common *civil twilight* limit -6 degr below horizon, used often as a clock time result in the solar calculator programs.

This `read`

/`show`

business really is something to be avoided. You’re turning a number into a string and then back into a number for no apparent reason. This is also the reason you’re unable to print the result in the REPL without doing something extra to it.

What problems do you encounter with

```
ghci> hours = mod (div ps 3600) 24
hours :: Integral a => a
ghci> hours
20
it :: Integral a => a
```

(or, as it is more commonly written, `hours = ps `div` 3600 `mod` 24`

)?

Indeed that’s what it is, possibly corrected for atmospheric aberration? Civil twilight is what our local regulatory authorities deem the activity period of predatory birds, which are to be protected.

Then it is good custom to have the haddock documentation link to the external documentation.

Another suggestion for improvement: Consider `newtype`

aliases for latitude and longitude, possibly indicating the implicit reference system, as in

```
newtype Latitude = LatWGS84 {getLatitude :: Double}
deriving (necessary type classes here)
```

That way, your code can not accidentally mix up latitude and longitude or other `Double`

quantities. As a side-effect, the type signatures of functions like `solarElevationAngle`

become much more readable. For things like elevation angles, with a newtype you could also build in modulo operations, so that adding angles automatically wraps around a full circle.

`SolarCurrent.dateString`

: That is what `Data.Time.Format.formatTime`

is for.

There are a few numeric type classes that allow conversions in Haskell:

```
-- embeds the integer in any 'Num' type.
-- used by the compiler for integer literals in source code
fromInteger :: Num a => Integer -> a
-- careful: overflows without warning!
-- >>> fromIntegral (1000 :: Int) :: Word8
-- 232
fromIntegral :: (Integral a, Num b) => a -> b
toRational :: Real a => a -> Rational
-- embeds fractions in any 'Fractional' type.
-- used by the compiler for literals like 3.141
fromRational :: Fractional a => Rational -> a
properFraction :: (RealFrac a, Integral b) => a -> (b, a)
```

All *time difference* types in the `time`

library are members of `RealFrac`

, `Real`

, `Fractional`

and `Num`

so all the above functions can be used on them.

No problems within ghci.

The following happend as I replaced the lines using

read $ show $ with the new ones without conversions:

```
main = do
currentTime <- getCurrentTime
let (year, month, day) = toGregorian . utctDay $ currentTime
ps = round $ utcTimeToPOSIXSeconds currentTime
-- hours = read $ show $ mod (div ps 3600) 24
-- minutes = read $ show $ mod (div ps 60) 60
-- seconds = read $ show $ mod ps 60
hours = mod (div ps 3600) 24
minutes = mod (div ps 60) 60
seconds = mod ps 60
jC = julianCentury currentTime
sunDecl = sunDeclin jC
% ghci SolarCurrent.hs
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling SolarCurrent ( SolarCurrent.hs, interpreted )
Ok, one module loaded.
ghci> main
<interactive>:2:1: error:
Variable not in scope: main
Suggested fix: Perhaps use ‘min’ (imported from Prelude)
ghci>
• No instance for (Integral Double) arising from a use of ‘round’
• In the first argument of ‘($)’, namely ‘round’
In the expression: round $ utcTimeToPOSIXSeconds currentTime
In an equation for ‘ps’:
ps = round $ utcTimeToPOSIXSeconds currentTimetypecheck(-Wdeferred-type-errors)
```

That was originally the reason why I use conversion Double → Integer with show and read. However, I believe there is a way how to avoid conversions completely.

First the most simple solution: leaving out the local time

In that case I could remove the variables hours, minutes, seconds

and get the minutes simply as Double through currentTime / 60.0 to be used next by several NOAA astronomical functions.

Maybe there is a function in Data.Time getting the local time with the given timezone from the current UTC time.

Also, the error looks like it’s because `ps`

is a Double, but round expects to return an Int or other integral type. You can do `ps = fromInteger $ round $ ...`

to tell the compiler to round to Integer, then convert to Double

I have just uploaded the latest improved version which has no read $ show $ sequences anymore.

The old version **mod** instructions are replaced with a function found from stackoverflow.com

It can be used for remainders of non-integer division.

```
nonIntRem :: RealFrac a => a -> a -> a
nonIntRem x y = x - (y * fromIntegral (truncate (x/y)))
```

Nice! FYI ansi-terminal if you dont want to manage color codes manually

I would rather use CSS so as I do in Elm.

I have not studied yet, how to do that in Haskell.

Well, it’s nostalgic, long time ago I used ANSI-codes previously writing RSTS-11 Basic code for DEC PDP and VAX terminals in job.