haskelltypesoutputcustom-data-typehaskell-prelude

Printing a custom representation of my own datatype in Haskell


Context

Important detail, I am using Haskell (NOT CABAL) on repl.it. I would like to print a custom Haskell datatype that I created. I was thinking of an approach similar to Python's __repr__ or __str__ methods, when creating a new class. Something like:

class Length:
    def __init__(self, value, unit_of_measurement):
        self.value = value
        self.unit_of_measurement = unit_of_measurement
    def __str__(self):
        return f'{self.value}{self.unit_of_measurement}'
    def __repr__(self):
        return self.__str__()

Which will produce the following:

>>> # I can print a custom representation of the object
>>> l = Length(10, 'cm')
>>> l
10cm
>>> print(l)
10cm

The problem

I am trying to instantiate the Show class in my custom datatype and use pattern matching to customize the output that will be sent to the console.

What I tried so far

-- This works fine
data Length = Length {value :: Double, unit_of_measurement :: String}
    deriving (Eq, Ord)  -- I don't want to use default Show inheritance

-- These lines are actually wrong, but I don't know how to solve this
-- Also, how to get the fields declared in my datatype???
instance Show Length where  -- Or IO, I am not sure
    print Length = print $ show value ++ unit_of_measurement

Ignoring the wrong lines that I mentioned (so the compiler won't stop the execution) and considering I have used Haskell's built-in inheritance mechanism with Show (deriving(Show)) this will be the result, (which I don't like):

位> :load Main.hs
[1 of 1] Compiling Main             ( Main.hs, interpreted )
Ok, one module loaded.
位> let test = Length 10 "cm"
位> test
Length {value = 10.0, unit_of_measurement = "cm"}  -- HERE 馃槫, THIS IS THE PROBLEM

I understand if it is not possible to do exactly what I want, but, is there any way to do something near this, or similar? Thanks in advance!


Solution

  • The method of the Show typeclass is called show, not print. You'll need to remove the call to print. The show function must return a plain String, not an IO ().

    To pattern match on a record as you have it, you can enable the RecordWildCards language extension by declaring it at the very top of the file, and then in the function definition use Length{..} to pattern match and bring all the fields of the record into scope.

    {-# LANGUAGE RecordWildCards #-}
    
    data Length = Length {value :: Double, unit_of_measurement :: String}
        deriving (Eq, Ord)
    
    instance Show Length where
        show Length{..} = show value ++ unit_of_measurement
    

    Without that language extension, you could access the record fields in a few other ways:

    show Length{value=v, unit_of_measurement=u} = show v ++ u
    
    show (Length v u) = show v ++ u
    
    show l = show (value l) ++ unit_of_measurement l