Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two-level type hierarchy with Haskell

I want to model 4 kinds of directions in my application: right, left, up and down. However, I want to be able to have a function that takes only the horizontal ones for a variable and the vertical ones for other variable.

To solve this, I can have two types: HorizontalDirection and VerticalDirection:

data HorizontalDirection = RightDir | LeftDir
data VerticalDirection = UpDir | DownDir

foo :: HorizontalDirection -> VerticalDirection -> String
foo hDir vDir = "This works" 

However, I also would like to be able to have a function that can take one type as well as the other, something like this:

bar :: Direction -> String
bar (HorizontalDirection _) = "Horizontal!"
bar (VerticalDirection _)   = "Vertical!"

but this wouldn't work since HorizontalDirection and VerticalDirection aren't data constructors.

I know I can use Either and make it work, like this:

bar :: (Either HorizontalDirection VerticalDirection) -> String
bar (Left _) = "Horizontal!"
bar (Right _) = "Vertical!"

however, I wonder if I can do this without the Either type.

I also tried using typeclasses:

data HorizontalDirection = RightDir | LeftDir
data VerticalDirection = UpDir | DownDir

class Direction a

instance Direction HorizontalDirection
instance Direction VerticalDirection

baz :: Direction d => d -> String
baz RightDir = "Horizontal"

but that gives me the following compiler error:

Direction.hs:21:5: error:
    • Couldn't match expected type ‘d’
                  with actual type ‘HorizontalDirection’
      ‘d’ is a rigid type variable bound by
        the type signature for:
          baz :: forall d. Direction d => d -> String
        at Direction.hs:20:1-33
    • In the pattern: RightDir
      In an equation for ‘baz’: baz RightDir = "Horizontal"
    • Relevant bindings include
        baz :: d -> String (bound at Direction.hs:21:1)

Is my way of thinking totally wrong here? Or am I just missing something?

like image 320
Gabriel Crispino Avatar asked May 15 '26 16:05

Gabriel Crispino


1 Answers

Instead of using Either you can declare a new data type with more meaningful names.

data Direction
  = Horizontal HorizontalDirection
  | Vertical   VerticalDirection

bar :: Direction -> String
bar (Horizontal _) = "Horizontal!"
bar (Vertical _) = "Vertical!"
like image 119
Li-yao Xia Avatar answered May 19 '26 01:05

Li-yao Xia