通常查找值的类型是否属于类型类

时间:2015-01-28 14:10:18

标签: haskell reflection typeclass

我想将isShowable函数编写为此代码的一部分。

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 = ???

使用Data实例可以实现吗?如果没有,最好的方法是什么?

注意:如果没有其他选项,我愿意为此解决这个版本,该版本仅适用于通过导入到定义isShowable的模块可见的类型类实例

2 个答案:

答案 0 :(得分:4)

我不确定你的真实目的是什么,但看起来你想将Java习惯嵌入Haskell。

如上所述in other SO question你正在做的事情将变成反模式。

您已添加澄清说明:

  

如果我愿意解决“通过导入到isShowable定义的模块”可见的类型类实例。

为什么不包装你的类型:

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>>"

让您的功能在MaybeShowable a上运行,而不是普通a

然而这仍然是丑陋的。直接在Show a => aa上操作是不是更容易。

其他方法是尽早捕获Show a字典,即具有数据类型:

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

这种方法的变化用于例如QuickCheck's forAll。那部分是Haskell98。那里show x被关闭到关闭,可能执行或不执行。懒惰是关键点!

答案 1 :(得分:2)

您可以使用模板haskell询问哪些实例可用:

    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)是真的,但不幸的是 $(isInst ''Show) (1 :: Rational)为false,因为此处使用==并未说明Show (Ratio a)的实例可以与type Rational = Ratio Integer一起使用。因此,完整的解决方案必须知道如何选择实例。