# How can I write human-language units as postfixes in Haskell, like 3 seconds?

Ruby has a nice feature that allows to convert numbers to other things, e.g. 3.times for iteration or 3.to_s for converting it to a string.

People say Haskell is good for writing natural DSLs.

Is it possible to write units as postfixes, e.g. timeout = 3 seconds?

• Maybe you should have a look at hackage.haskell.org/package/dimensional . They use something like timeout = 3 *~ seconds ... but you get all other si units and prefixes from that, too.
– fho
Apr 3 '14 at 7:24

Yes.

You can do this with the following simple trick:

{-# LANGUAGE FlexibleInstances #-}

instance Num (Integer -> Integer) where
fromInteger n = \scale -> n * scale  -- return a function that takes
-- a number and returns a number


Then you can write:

seconds, minutes, hours, days :: Integer

seconds = 1000000 -- base unit, e.g. microseconds
minutes = 60 seconds
hours   = 60 minutes
days    = 24 hours

soon :: Integer
soon = 2 hours + 4 seconds


How does this work?

Above we have given a Num instance for Integer -> Integer, that is for a function that takes an integer and returns an integer.

Every type that implements Num and has its function fromInteger defined is allowed to be represented by a numeric literal, e.g. 3.

This means that we can write 3 :: Integer -> Integer - here 3 is a function that takes an integer and returns an integer!

Therefore, we can apply an integer to it, for example seconds; we can write 3 seconds and the expression will be of type Integer.

A more type-safe version

In fact, we could even write 3 (3 :: Integer) now - this probably doesn't make much sense though. We can restrict this by making it more type-safe:

newtype TimeUnit = TimeUnit Integer
deriving (Eq, Show, Num)

instance Num (TimeUnit -> TimeUnit) where
fromInteger n = \(TimeUnit scale) -> TimeUnit (n * scale)

seconds, minutes, hours, days :: TimeUnit

seconds = TimeUnit 1000000
minutes = 60 seconds
hours   = 60 minutes
days    = 24 hours


Now we can only apply things of type TimeUnit to number literals.

You could do that for all kinds of other units, such as weights or distances or people.

• ...it's simultaneously beautiful and hideous. Congratulations. Upvoted. Mar 1 '14 at 3:02
• It's nice to see the answer I was going to give. :) Mar 1 '14 at 3:34
• To take this to its logical conclusion, see the unittyped package. It's really awesome about automatically managing units for you, but it has to use a bunch of extensions and the error messages are absurdly bad. Mar 1 '14 at 7:02
• This is very nice because it allows us to drop the (*), yet one must watch out for expressions like 3 x^2 ....
– Ingo
Mar 1 '14 at 15:33
• ...because function application between 3 and x binds more tightly than any infix operator, so it's not possible for this to square the x before multiplying by the 3 without brackets. This is the opposite of the way you expect. Mar 1 '14 at 18:46

If your units are in lower case you can use the TimeUnit type given above. But if your units starts in Upper case, you would need to define a newtype or data for each unit and define the Num instance for them. An example of that is in the Basic interpreter
{"ft_lang_label":"__label__en","ft_lang_prob":0.78968257,"math_prob":0.95899355,"size":1674,"snap":"2021-43-2021-49","text_gpt3_token_len":415,"char_repetition_ratio":0.15808383,"word_repetition_ratio":0.09150327,"special_character_ratio":0.27001196,"punctuation_ratio":0.14641744,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9724429,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2021-10-23T22:05:54Z\",\"WARC-Record-ID\":\"<urn:uuid:d53a6500-9165-4565-a903-05d2b95516c2>\",\"Content-Length\":\"182979\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:8949923a-00c5-4070-9b70-c2011bd058ec>\",\"WARC-Concurrent-To\":\"<urn:uuid:d57a3ede-5dc4-4542-bed7-bb5abff631df>\",\"WARC-IP-Address\":\"151.101.193.69\",\"WARC-Target-URI\":\"https://stackoverflow.com/questions/22109333/how-can-i-write-human-language-units-as-postfixes-in-haskell-like-3-seconds/22109334\",\"WARC-Payload-Digest\":\"sha1:JC5BFK2Z6TOYOI45JAPLUYDRF4RRLWZL\",\"WARC-Block-Digest\":\"sha1:3BDRWFQ6KAD4ZKFLWDDGJQKGFXU5CGQ6\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2021/CC-MAIN-2021-43/CC-MAIN-2021-43_segments_1634323585768.3_warc_CC-MAIN-20211023193319-20211023223319-00537.warc.gz\"}"}