3

给定一个数据类型

data Foo = IFoo Int | SFoo String deriving (Data, Typeable)

什么是简单的定义

gconstr :: (Typeable a, Data t) => a -> t

这样

gconstr (5 :: Int) :: Foo == IFoo 5
gconstr "asdf" :: Foo == SFoo "asdf"
gconstr True :: Foo == _|_

它本质上与 syb 的gfindtype.

还是这样的事情已经存在?我试过 hoogle-ing 类型并没有找到太多,但是 syb 类型有点难以解释。返回Nothing错误的函数也是可以接受的。

4

1 回答 1

2

这似乎是可能的,尽管它并非完全微不足道。

预赛:

{-# LANGUAGE DeriveDataTypeable #-}
import Control.Monad ( msum )
import Data.Data
import Data.Maybe

首先是一个辅助函数gconstrn,它尝试做与所需相同的事情gconstr,但仅适用于特定的构造函数:

gconstrn :: (Typeable a, Data t) => Constr -> a -> Maybe t
gconstrn constr arg = gunfold addArg Just constr
    where
        addArg :: Data b => Maybe (b -> r) -> Maybe r
        addArg Nothing = Nothing
        addArg (Just f) =
            case cast arg of
                Just v -> Just (f v)
                Nothing -> Nothing

关键部分是,如果类型匹配,该addArg函数将arg用作构造函数的参数。

基本上以orgunfold开始展开,然后下一步是尝试为其提供参数。Just IFooJust SFooaddArg

对于多参数构造函数,这将被重复调用,所以如果你定义了一个IIFoo需要两个Ints 的构造函数,它也会被成功填充gconstrn。显然,通过更多的工作,你可以做一些更复杂的事情,比如提供一个参数列表。

那么这只是一个尝试使用所有可能的构造函数的问题。result和之间的递归定义dt只是为了获得正确的类型参数dataTypeOf,传入的实际值根本不重要。ScopedTypeVariables将是实现这一目标的替代方案。

gconstr :: (Typeable a, Data t) => a -> Maybe t
gconstr arg = result
    where result = msum [gconstrn constr arg | constr <- dataTypeConstrs dt]
          dt = dataTypeOf (fromJust result)

正如评论中所讨论的,这两个函数都可以使用<*>fromControl.Applicative到以下进行简化,尽管很难看到 中发生了什么gunfold

gconstr :: (Typeable a, Data t) => a -> Maybe t
gconstr arg = result
    where
        result = msum $ map (gunfold (<*> cast arg) Just) (dataTypeConstrs dt)
        dt = dataTypeOf (fromJust result)
于 2014-07-13T20:47:59.463 回答