Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare a custom data type in haskell

Tags:

haskell

I have a custom data type which I would like to compare.

data Tile = Wall | Ground | Storage | Box | Blank

I would like to do instance-of-tile == Box

I've tried using == like so

tileToInteger :: Tile -> Integer
tileToInteger tile 
  | tile == Blank || tile == Wall = 1
  | otherwise = 2

I've also tried

tileToInteger :: Eq => Tile -> Integer

The error message from stack build is

No instance for (Eq Tile) arising from a use of ‘==’
    • In the first argument of ‘(||)’, namely ‘tile == Blank’
      In the expression: tile == Blank || tile == Wall
      In a stmt of a pattern guard for an equation for ‘tileToInteger’: tile == Blank || tile == Wall

Here's the full sample code

data Tile = Wall | Ground | Storage | Box | Blank

getTileAtXY :: Integer -> Integer -> Tile
getTileAtXY x y
  | x == 0 && y == 0 = Box
  | otherwise = Ground

tileToInteger :: Tile -> Integer
tileToInteger tile 
  | tile == Blank || tile == Wall = 1
  | otherwise = 2

main :: IO ()
main = print (tileToInteger (getTileAtXY 1 0))

Background

  • Exercise from Penn Engineering university course
  • Using stack version 1.9.3.1
like image 796
robor Avatar asked Feb 28 '26 08:02

robor


2 Answers

The error

No instance for (Eq Tile) arising from a use of ‘==’

Says that you use (==) with two Tiles, but that the compiler did not found an instance of Eq Tile where you defined the (==) function for Tiles.

You can make it an instance of the Eq typeclass:

data Tile = Wall | Ground | Storage | Box | Blank deriving Eq

If you automatically derive Eq, then Haskell considers two objects of Tile equal given the data constructors (Wall, Ground, ...) are the same, and all their arguments are the same. Since the data constructors of your Tile data type have no arguments, this thus simply means that Wall is equal to Wall, Ground is equal to Ground, etc.

In your function tileToInteger however, you do not need to use (==) at all, you can use pattern matching [Haskell-wiki], like:

tileToInteger :: Tile -> Integer
tileToInteger Blank = 1
tileToInteger Wall = 1
tileToInteger _ = 2

You can implement the (==) function for Tiles with pattern matching, like:

instance Eq Tile where
    Wall == Wall = True
    Ground == Ground = True
    Storage == Storage = True
    Box == Box = True
    Blank == Blank = True
    _ == _ = False

The above is however equivalent to what deriving Eq will do, so usually one only implements Eq manually if two Tiles are considered equivalent in a different way.

like image 98
Willem Van Onsem Avatar answered Mar 04 '26 09:03

Willem Van Onsem


You can derive the methods for comparison automatically:

data Tile = Wall | Ground | Storage | Box | Blank deriving (Eq)

And then you can use == and /= to compare Tiles for equality and inequality.

like image 22
ForceBru Avatar answered Mar 04 '26 10:03

ForceBru