Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

haskell

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?

like image 205
nh2 Avatar asked Mar 01 '14 02:03

nh2


1 Answers

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.

like image 106
nh2 Avatar answered Oct 24 '22 09:10

nh2