I would like to define data types hierarchically, such as:
data Cat = BigCat | SmallCat
data Animal = Cat | Dog
And then write a function that will take Animals as arguments, and write it with pattern matching like this:
bigger::Animal -> Animal -> Bool
bigger SmallCat BigCat = False
bigger BigCat SmallCat = True
bigger Dog Cat = True
bigger Cat Dog = False
The compiler complains. It doesn't want to match the type Animal explicitely written in the function signature against the type Cat in the first and second lines of pattern matching. Why won't haskell admit that a big cat or small cat is an animal?
Computer Data Hierarchy: Bits, Characters, fields, records, files, database bigdata.
Tree is an example of hierarchical data structure.
A hierarchical database is a data model in which data is stored in the form of records and organized into a tree-like structure, or parent-child structure, in which one parent node can have many child nodes connected through links.
You mix up types with their constructors. A type is something of which you can create variables. A type constructor is what you use to create such data. In you code, data Animal = Cat | Dog declares a type Animal with the two constructors Cat and Dog. In the other line, you define a datatype Cat. This is no problem since types and constructors don't share the same namespace.
If you want to have an object of type Cat embedded in your Animal (if the constructor Cat is used), you can add a field to the constructor:
data Animal = Cat Cat | Dog
This means: "Animal is a type that has two constructors, Cat and Dog. Cat has a field of type Cat and Dog has none." If you want to create objects with the constructor Cat, you have to pass an object of type Cat to it:
myCat = Cat BigCat
If you want to match on an Animal, you have to list all fields of the matched constructor. Compare a corrected version of your code:
data Cat = BigCat | SmallCat
data Animal = Cat Cat | Dog
bigger :: Animal -> Animal -> Bool
bigger (Cat SmallCat) (Cat BigCat) = False
bigger (Cat BigCat) (Cat SmallCat) = True
bigger Dog (Cat _) = True
bigger (Cat _) Dog = False
The _ denotes a don't care - regardless of the object passed, this will always match.
The immediate error here is that Animal is defining two data constructors, which have nothing to do with Cat: The expression Cat is of type Animal, while the expression BigCat is of type Cat.
To create nested data types in simple fashion, you'd need to make the Cat type an argument to the relevant constructor:
data Cat = BigCat | SmallCat
data Animal = Cat Cat | Dog
You can then do something like this:
bigger (Cat SmallCat) (Cat BigCat) = False
bigger (Cat BigCat) (Cat SmallCat) = True
bigger Dog (Cat _) = True
bigger (Cat _) Dog = False
This becomes exceeding clumsy if extended beyond a trivial example, however, the redundancy in the Cat type is painful, and the two different uses of the identifier Cat is needlessly confusing. A slight improvement is to avoid conflating size with species, and instead do something like this:
data Size = Big | Small
data Species = Cat | Dog
data Animal = Animal Species Size
An advantage here is that you can more easily extend either types without having to add as much boilerplate nonsense as would otherwise be required.
However, both of these are very silly as anything other than toy examples and in actual use there's very likely to be a much better approach that would be preferable. If the types really are simple enumerations more meaningful than cats and dogs, then deriving Ord, Enum, &c. is preferable to special-purpose stuff. If the intent is a more open-ended way of modelling entities with various properties, it's worth thinking about other designs more tailored to the actual problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With