Reputation: 5412
I want to write the isShowable
function as part of this code.
data MaybeShowable = forall a . Show a => Showable a | Opaque
f :: (Data d) => d -> String
f x = case isShowable x of
Showable s -> show s
Opaque -> "<<OPAQUE>>"
isShowable :: (Data d) => d -> MaybeShowable
isShowable = ???
Is this possible by using the Data
instance? If not, what is the best way to do it?
Note: If there's no other option, I'm willing to settle for this a version that works only for type class instances visible through the imports to the module in which isShowable
is defined.
Upvotes: 3
Views: 139
Reputation: 12123
I'm not sure what is your real intention is, but it looks like you'd like to embed Java idiom into Haskell.
As mentioned in other SO question what you are doing is going to turn into anti-pattern.
You have added a clarification:
If I would be willing to settle for "typeclass instances visible through the imports to the module in which
isShowable
is defined".
Why wouldn't you wrap your type in:
data MaybeShowable a where
Showable :: forall b. Show b => b -> MaybeShowable b
Opaque :: forall b. b -> MaybeShowable b
instance Show (MaybeShowable a) where
show (Showable x) = show x
show (Opaque x) = "<<OPAQUE>>"
And have your functions operate on MaybeShowable a
, instead of plain a
.
Yet this is ugly still. Isn't it be easier to operate directly on Show a => a
, or a
.
Other way is to capture Show a
dictionary early enough, i.e. have data type:
data MaybeShowable a = Showable a String -- Or even Showable a (a -> String)
| Opaque a
instance Show (MaybeShowable a) where
show (Showable x s) = s
show (Opaque x) = "<<OPAQUE>>"
wrapShow :: Show a => a -> MaybeShowable a
wrapShow x = Showable x (show x) -- Showable x show
wrapOpaque :: a -> MaybeShowable a
wrapOpaque = Opaque
The variation of this approach is used in e.g. QuickCheck's forAll
. That part is Haskell98. There the show x
is closed over into closure, which maybe executed or not. Lazyness is the key point here!
Upvotes: 4
Reputation: 1308
You can ask using template haskell which instances are available:
module IsInstance where
import Language.Haskell.TH; import Data.Typeable
import Data.Generics; import Data.Monoid
-- $(isInst ''Show) :: Typeable a => a -> Bool
isInst :: Name -> ExpQ
isInst className = do
ClassI _ insts <- reify className
ClassI _ typeableInsts <- reify ''Typeable
let typeOfs = [ [| typeRep (Proxy :: Proxy $(return ty)) |]
| InstanceD _ (AppT _ ty) _ <- insts,
hasNoVarT ty,
or [ ty_ == ty | InstanceD _ (AppT _ ty_) _ <- typeableInsts ] ]
[| \ val -> typeOf val `elem` $(listE typeOfs) |]
hasNoVarT xs = getAll $ everything
(<>)
(mkQ mempty (\ x -> case x of
VarT {} -> All False
_ -> mempty))
xs
$(isInst ''Show) (1 :: Int)
is true, but unfortunately
$(isInst ''Show) (1 :: Rational)
is false, since here using ==
doesn't say that an instance for Show (Ratio a)
can be used with type Rational = Ratio Integer
. So a complete solution is going to have to know how instances are selected.
Upvotes: 2