0
votes

I am making assigment on Haskell, and almost solved it.. Feels like I miss some last step, but cant put my finger on, what do I do wrong.

First my task:

Now, insted of using type synonyms, define data types CountryCode and PhoneNo so that both of them have a value constructor that takes an integer. Derive an instance for Eq, Ord and Show for PhoneType. Derive instances for Eq and Ord for CountryCode and PhoneNo and make Show instances for them so that: CountryCode: print '+' in front of the number. PhoneNo: print only the number. Make a function for both of them (toCountryCode and toPhoneNo) that takes an Integer and throws an error if the integer is negative otherwise it creates the value.

If CountryCode is negative, the error should be "Negative country code" and if PhoneNo is negative, the error should be "Negative phone number" and you should follow these literally to pass the automatic testing.

Again, using the record syntax, define Phone type for phone numbers that has only one value constructor with fields

phoneType :: PhoneType,
countryCode :: CountryCode, (This time the type defined as above)
phoneNo :: PhoneNo. (This time the type defined as above)

Derive an instance for Eq and Ord for the record, but for Show make it "pretty-print" the infromation in this form: e.g. +358 123456789 (WorkLandline)

Now my take on the task:

data PhoneType = WorkLandline|PrivateMobile|WorkMobile|Other deriving (Read,Show,Eq)

newtype PlainString = PlainString String
instance Show PlainString where
  show (PlainString s) = s
--instance Num Int where
--   (*)=int_times
--   (+)=int_plus

data CountryCode = CountryCode
    {
        cCode :: Int
    } deriving (Read,Eq)
instance Show CountryCode where
    show (CountryCode a)=(show (PlainString "+")) ++ (show a)
instance Num CountryCode where 
    (*) a b=a*b 
    (+) a b=a+b 
    (-) a b=a-b
    fromInteger x=fromInteger x 

toCountryCode :: Int->CountryCode
toCountryCode ccode  
    | ccode<0=error "Negative country code"
    |otherwise = CountryCode ccode


data PhoneNo = PhoneNo
    {
        pNo :: Int
    } deriving (Read,Eq)
instance Show PhoneNo where
    show (PhoneNo a)=show a
instance Num PhoneNo where
    (*) a b=a*b
    (+) a b=a+b
    (-) a b=a-b
    fromInteger x= fromInteger x

toPhoneNo :: Int -> PhoneNo
toPhoneNo pno  
    | pno<0=error "Negative phone number"
    |otherwise = PhoneNo pno

data Phone = Phone
    {
        phoneType :: PhoneType,
        countryCode :: CountryCode,
        phoneNo :: PhoneNo
    } deriving (Read,Eq)
instance Show Phone where
    show (Phone a b c)=show a

makePhone :: PhoneType -> CountryCode  -> PhoneNo -> Phone
makePhone phonetype countrycode phoneno
    |otherwise = Phone phonetype countrycode phoneno

This version kinda workds with: makePhone Other 1 1 , which shows Other.

However, if i modify this to: show (Phone a b c)=show b or make sane show like asked in task - program hangs indefinitely. Same goes for show c

What do I do wrong?

2

2 Answers

1
votes

Implementations like:

instance Num CountryCode where 
    (*) a b = a*b 
    (+) a b = a+b 
    (-) a b = a-b
    fromInteger x = fromInteger x

make not much sense, since you here define that you can add two CountryCodes together, by adding these together... This thus results in infite recursion.

You can define addition, multiplication, etc. by unpacking the value wrapped in the CountryCode data constructor, perform the arithmetic, and then wrap it in a CountryCode data constructor:

instance Num CountryCode where 
    CountryCode a * CountryCode b = CountryCode (a * b)
    CountryCode a + CountryCode b = CountryCode (a + b)
    CountryCode a - CountryCode b = CountryCode (a - b)
    fromInteger x = CountryCode (fromInteger x)

The same happens with the instance Num PhoneNo.

1
votes

You can use GeneralizedNewtypeDeriving to derive the following instances, but they must be newtypes for it to work:

  • Show PlainString
  • Num CountryCode
  • Show PhoneNo, Num PhoneNo

I suggest using DerivingStrategies to make it explicit what method of deriving you are using: stock uses the deriving mechanism built in to GHC, newtype uses the instance of the underlying type (Show String, Show Int and Num Int).

{-# Language DerivingStrategies         #-}
{-# Language GeneralizedNewtypeDeriving #-}

newtype CountryCode = CountryCode { cCode :: Int }
  deriving
  stock (Eq, Read)

  deriving
  newtype Num

newtype PlainString = PlainString String
  deriving
  newtype Show

newtype PhoneNo = PhoneNo { pNo :: Int }
  deriving
  stock (Eq, Read)

  deriving
  newtype (Num, Show)