Reputation: 45
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?
Upvotes: 3
Views: 163
Reputation: 477533
You are quite close, but you need to define the function in the class:
class Direction a where
baz :: a -> String
instance Direction HorizontalDirection where
baz _ = "Horizontal"
instance Direction VerticalDirection where
baz _ = "Vertical"
Note however that Haskell is statically typed, and the types are known at compile time.
Upvotes: 3
Reputation: 33519
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!"
Upvotes: 9