Search code examples
haskellconstantsalgebraic-data-types

Defining non-Int constants in Haskell within an ADT


I'm learning Haskell and as part of the process I'm trying to build a toy program, modelling pizza sizes as an Algebraic Data Type.

So far, I've come up with this:

    data Size = Small | Medium | Large | ExtraLarge
        deriving Show

Which is OK in the sense that this models the fact that the valid sizes are finite and defined, but it does not model the notion of how big numerically those sizes are.

One solution could be, as suggested in this SO post, to derive from Enum and write fromEnum and toEnum. Then again, the signature of those functions require that the conversion is from that type to Int which I don't like - first because sizes are not Int naturally, and then because I might decide to have a custom data type anyway (say, Inch).

I could create a bunch of consts like:

    Small = 9.5             
    Medium = 11.5
    ...

But I'd also like to group them into a "proper" data type.

Any suggestions how to move forward?


Solution

  • You could define a function, associating to each size-value a numeric value. E.g.

    sizeToInch :: Size -> Double
    sizeToInch Small  = 9.5
    sizeToInch Medium = 11.5
    ...
    

    If you want, you can also use a custom type to better represent the units:

    newtype Inch = Inch { fromInch :: Double }
    
    sizeToInch :: Size -> Inch
    sizeToInch Small  = Inch 9.5
    sizeToInch Medium = Inch 11.5
    ...
    

    Note that the above Inch type won't let you directly sum Inch values using +, or perform any other arithmetic operation using the standard operators. You might want to add deriving Num to enable standard operators on that type. This will also allow multiplication, though, which is not ideal (an inch squared is not an inch). There are some libraries (like dimensional) that can rigorously handle units, but they can be overkill for such a simple task. Choose the design you are most comfortable with.