Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Several data type with Type constructor accepting the same Type, how to get same name accessor ?

Tags:

haskell

Short : I have a solution to my problem, but it seems overkill so I wonder if I miss something.

Long : I have a 2 data Type, an Animal type and an Owner type. Both have same "attributes" age and name. For sake of simplicity, I want to be able to call age and name indifferenly on Animal and on Owner

type Age = Int
type Name = String

data AnimalType = Dog | Cat | Snake
 deriving (Read, Show,Eq)

--with  datatype and pattern matching
data Animal = Animal AnimalType Name Age
 deriving(Show, Eq)
name (Animal _ name _) = name 
age (Animal _ _ age) = age 
animalType (Animal animalType _ _) = animalType 

data Owner = Owner Name Age [Animal]
 deriving(Show,Eq)
name    (Owner name _      _) = name 
age     (Owner _    age    _) = age
animals (Owner _    _      animals) = animals

garfield = Animal Cat "Garfield" 8
rantanplan = Animal Dog "Rantanplan " 4
kaa = Animal Snake "Kaa" 15

dupond = Owner "Dupont" 28 [garfield, rantanplan]
bob = Owner "Bob" 35 [kaa]

This does not compile,

  Multiple declarations of `age'

The same thing does not work neither with Record syntax.

Doing so, one is forced to name differently age for Owner and age for Animal.

Then I dug a bit and found out that I could use typeclass to achieve that.

type Age = Int
type Name = String

class Nameable a where
 name:: a -> Name

class Ageable a where
 age:: a -> Age


data AnimalType = Dog | Cat | Snake
 deriving (Read, Show,Eq)

--with  datatype and pattern matching
data Animal = Animal AnimalType Name Age
 deriving(Show, Eq)

instance Nameable Animal where
 name (Animal _ name _) = name 

instance Ageable Animal where
 age (Animal _ _ age) = age

animalType (Animal animalType _ _) = animalType 


data Owner = Owner Name Age [Animal]
 deriving(Show,Eq)

instance Nameable Owner where 
 name (Owner name _ _) = name

instance Ageable Owner where
 age (Owner _ age _) = age

animals (Owner _    _      animals) = animals



garfield = Animal Cat "Garfield" 8
rantanplan = Animal Dog "Rantanplan " 4
kaa = Animal Snake "Kaa" 15

dupond = Owner "Dupont" 28 [garfield, rantanplan]
bob = Owner "Bob" 35 [kaa]

This approach is almost identical to usage of Interface in Java. The first one that does not work is closer to old C struct approach.

is there a quicker way to achieve the same result ?

like image 872
sandwood Avatar asked Nov 07 '17 14:11

sandwood


1 Answers

Records (and their accessors) in Haskell are... suboptimal. That said, this particular issue regarding duplicate record fields has a workaround (since GHC 8.0) in the form of the DuplicateRecordFields extension. Note that the record accessors have to be used in an unambiguous fashion (there is no fancy polymorphism happening here).

{-# LANGUAGE DuplicateRecordFields #-}

type Age = Int
type Name = String

data AnimalType = Dog | Cat | Snake
  deriving (Read, Show, Eq)

data Animal = Animal
  { animalType :: AnimalType
  , name :: Name
  , age :: Age
  } deriving(Show, Eq)

data Owner = Owner
  { name :: Name
  , age :: Age
  , animals :: [Animal]
  } deriving(Show, Eq)

garfield = Animal Cat "Garfield" 8
rantanplan = Animal Dog "Rantanplan " 4
kaa = Animal Snake "Kaa" 15

dupond = Owner "Dupont" 28 [garfield, rantanplan]
bob = Owner "Bob" 35 [kaa] 
like image 95
Alec Avatar answered Nov 15 '22 08:11

Alec