Haskell – a correct way to implement a timer

Let’s start from a simple excercise: how to fix the following error?

No instance for (Show IO (UTCTime))

when showing the current system time?

One way is

printTime = do
    time <- getCurrentTime
    print time

or

    print =<< getCurrentTime

timer

the idea

I want to specify a number of seconds secondTo and wait till the system clock is showing that number of seconds.

my implementation

According to a well known approach (see Mark Seemann, John A. De Goes, etc…), I try to maximize the pure logic part

syncClockTime :: UTCTime -> Rational -> Rational
syncClockTime time secondTo =
    let (UTCTime day diff) = time
        secondFrom  = toRational diff `mod'` 60 in
    if secondTo > secondFrom then
        secondTo - secondFrom
    else
        60 + secondTo - secondFrom

and move the IO to the border

waitTill :: Rational -> IO ()
waitTill secondTo = do
    print =<< getCurrentTime
    let waitTime = pure (syncClockTime) <*> getCurrentTime <*> pure (secondTo)
    print =<< waitTime
    delayThread waitTime
    print =<< getCurrentTime

the waitTime line being equivalent to

time <- getCurrentTime
return $ syncClockTime time secondTo

 

Advertisements

3 thoughts on “Haskell – a correct way to implement a timer

  1. I guess that when you mention Seeman regarding the “pure logic part” of Haskell, you are referring to the following comment:

    ” Not that Haskell is the only Functional language out there, but it enforces purity in a way that neither F#, Clojure, nor Scala does. In Haskell, a function must be pure, unless its type indicates otherwise. This forces you to be deliberate in your design, and to separate pure functions from functions with (side) effects”

    One of the many “pluses” of Haskell as a computer language is it is has built-in many features that apply constraints that work as controls in favor of a good architecture.

    Many computer languages, but not all of them, have to deal with a thorny issue that is not technical in nature or origin: they need to be able to capture an audience, in general, a large audience, and that usually means that the language designers choose to make the language “tolerant and/or friendly to foolish approaches” in such a way that it allows newcomers to actually like the language in particular, as they learn both to program, and to program with said language.

    Kind regards, GEN

    Liked by 1 person

    • Yes, I completely agree with you. Notice that the “pure logic part” is the one (syncClockTime) where there is no IO in the signature (UTCTime -> Rational -> Rational) while the other part (waitTill) is effectful as shown by the signature (Rational -> IO ()).
      Notice however that I’m not 100% with Seemann, De Goes et al.. IMHO The language should be as tolerant and friendly as possible and we must be very cautious before saying that another approach is foolish only because it is different… Of course I like Haskell and I’ve learnt interesting things about it, but it doesn’t mean that I’m an extreme, intolerant FP fan! 🙂
      Thank you very much for your attentive reading and your smart comments!

      Like

  2. Giulio, as you just said, in essence, there is nothing wrong per se in using whatever construct that any “tolerant” language offers, as long as you use it in a judicious way … It is the end result of an architecture that can help to discriminate between “judicious” and “non-judicious” uses of the “tolerant” sides of a “tolerant” language.

    Does the architecture behave right or wrong?

    I frequently say that to some systems, the worst that could happen to them is to become successful, as, in time, increasing numbers of users will start to use them and said systems will start to show their own shortcomings and architectural problems.

    Any good architect will use “tolerant” tricks in any tolerant language in very different ways than lousy programmers would do, so, the problem does not lie within the “tolerant forms” in any language, they lie within the wrong uses of tolerant forms.

    As an example that is both close to home and related to Mark Seemann, I remember when EntLib introduced a Dependency Injection tool, and I thought that its architecture was nonsense, not just because it broke with the GoF lemma of “develop to an interface, never to an implementation”, but mainly because it was not usefull … Patterns in the GoF book are good because they work, they help us building sofware based on abstractions that help us delay architectural decisions.

    Whatever is good with abstractions is good for software development, thus, Category Theory is the step beyond OOP and Design Patterns in terms of more subtle and elegant abstractions.

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s