给定数据类型
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
相反。
或者这样的事情已经存在了吗?我已经尝试了类型和避风港发现了很多,但syb类型很难解释。错误返回Nothing
的函数也是可以接受的。
答案 0 :(得分: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
作为构造函数的参数,如果类型匹配。
基本上gunfold
开始使用Just IFoo
或Just SFoo
展开,然后下一步是尝试addArg
为其提供参数。
对于多参数构造函数,这将被重复调用,因此如果您定义了一个IIFoo
构造函数,该构造函数需要两个Int
,它也会被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)
正如评论中所讨论的,两个函数都可以使用<*>
从Control.Applicative
简化为以下内容,但是很难看到gunfold
中发生的事情。 {1}}:
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)