为Typeable编写实例声明而不导出

时间:2014-01-03 03:35:51

标签: haskell types instance

如何为Typeable编写实际的实例声明?例如,假设我想为Char写一个实例,其中每个字符都有不同的类型,例如

instance Typeable Char where
    typeOf 'a' = C
    typeOf 'b' = C -> D
    typeOf 'c' = D -> E
    typeOf     = String

显然这不会像这样写,因为typeOf的输出是TypeRep,但我无法弄清楚如何实际构造TypeRep。

这甚至可能吗?似乎所有关于Typeable的内容都假定您将使用DeriveDataTypeable。

1 个答案:

答案 0 :(得分:17)

这样做有一些问题。

  1. 它坏了。 typeOf在论证中不应该是严格的,因此typeOf (undefined :: Char)应该有效。

  2. 这是不安全的。实际上,如果您手动创建Typeable的实例,则不会在Safe Haskell下编译。

  3. 牢记这一点!

    {-# LANGUAGE DeriveDataTypeable #-}
    import Data.Typeable
    
    data C = C deriving (Typeable)
    data D = D deriving (Typeable)
    data E = E deriving (Typeable)
    newtype Foo = Foo Char
    
    instance Typeable Foo where
      typeOf (Foo 'a') = typeOf (undefined :: C)
      typeOf (Foo 'b') = typeOf (undefined :: C -> D)
      typeOf (Foo 'c') = typeOf (undefined :: D -> E)
      typeOf  _        = typeOf (undefined :: String)
    

    现在举例说明为什么会这么糟糕,请考虑

    what :: Char -> C
    what c = if isJust weird then fromJust weird else error "Sanity!"
      where weird = fromDynamic . toDyn $ Foo c
    

    现在

    > what 'a'
      C
    > what 'b'
      *** Exception: Sanity!
    

    取决于Foo如何表现各种有趣的事物,例如seg fault,吐出无意义的答案,或让猴子飞出你的耳朵。

    Robert Harper给出了一个更具戏剧性的例子

    {-# LANGUAGE DeriveDataTypeable #-}
    import Control.Exception
    import Data.Typeable
    
    newtype Foo = Foo (() -> IO ())
    
    {- set Foo’s TypeRep to be the same as ErrorCall’s -}
    instance Typeable Foo where
      typeOf _ = typeOf (undefined :: ErrorCall)
    instance Show Foo where  show _ = ""
    instance Exception Foo
    
    main = Control.Exception.catch (error "kaboom") (\ (Foo f) -> f ())
    

    哪个给出了

    <interactive>: internal error: stg_ap_v_ret
        (GHC version 7.6.3 for x86_64_unknown_linux)
        Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug
      C-c C-c
    Process haskell aborted (core dumped)