Reputation: 1277
To be more specific, suppose I have some data constructor
data Foo = ... deriving Eq
And the following silly function
f :: Eq a => a -> Bool
In cases where the variable a is actually of type Foo, I want f to output True. In all other cases (i.e. for all other instances of Eq), I want f to output False.
At first I thought maybe I could define a new type class for this purpose
class IsFoo a where
isFoo :: a -> Bool
While it's easy to write an instance of IsFoo for Foo, obviously I don't want to do this for all types that are instances of Eq.
When answering, you can assume that Foo has A LOT of constructors and that I don't want to pattern match on all of them. I also don't want to use Data.Typeable (I've read that it's bad style). Is there a way to accomplish what I want in an elegant and natural (w.r.t. Haskell) way?
Upvotes: 1
Views: 250
Reputation: 63389
If this is really what you want to do, I'd suggest you to use Data.Typeable
, because it's suited exactly for this purpose:
import Data.Maybe (isJust)
import Data.Typeable
isFoo :: (Typeable a) => a -> Bool
isFoo x = isJust (cast x :: Maybe Foo)
The question of bad style isn't about using a particular library like Data.Typeable
. It's about not using Haskell's type system properly, in particular treating it like a dynamic OO language. If you need to determine if some generic type is Foo
or is not, then you somewhere forgot the type information. But in Haskell, you always have this at compile time, so there shouldn't be the need of determining it dynamically.
Perhaps explain what you want to achieve, it's likely that there is a more idiomatic way how to do that.
Upvotes: 5
Reputation: 7360
You shouldn't need to do this in my opinion. This seems like a serious XY problem to me, since Haskell's type system should generally do this stuff for you.
But nonetheless, it is possible. The easiest way to achieve this is indeed to use typeclasses:
data Foo = A | B | C | D | ... | Z deriving Eq
class IsFoo a where
isFoo :: a -> Bool
instance IsFoo Foo where
isFoo = const True
instance IsFoo x where
isFoo = const False
Using the FlexibleInstances
extension, you save yourself some work by simply returning True
when given an argument of type Foo
, which is specified in the instance for type Foo
and False
when calling isFoo
with a variable of any other type. Note that you also have to use the extension OverlappingInstances
, otherwise a runtime error will occur calling isFoo
with an argument of type Foo
because the program will not know which instance to use. To enable these extensions, simply include
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
at the top of your source file.
Still: I strongly suggest trying a different approach to your problem because in general you do not have to deal with such "low-level" typing stuff.
Upvotes: 5