我想写下面这个函数:
data TypeEnum = IntType | FloatType | BoolType | DateType | StringType
data Schema = Object [Schema] |
Field TypeEnum |
Array Schema
schema :: Typeable a => a -> Schema
现在,我可以获得给定构造函数的TypeReps
列表,但我似乎找不到将TypeRep
转换为Schema
的方法来处理更复杂的类型:
field :: TypeRep t => t -> Schema
field t | t == typeOf (undefined :: String) = Field StringType
| t == typeOf (undefined :: Int) = Field IntType
| t == typeOf (undefined :: ???) = Array ???
| t == typeOf (undefined :: ???) = Object ???
适用于Int
和String
之类的简单类型,但Typeable
或[Typeable]
呢?
答案 0 :(得分:2)
您可以使用typeOf1
获取单参数类型构造函数的类型:
> typeOf (undefined :: Array Int Int)
Array Int Int
> typeOf1 (undefined :: Array Int Int)
Array Int
> typeOf2 (undefined :: Array Int Int)
Array
> typeOf2 (undefined :: Array a b)
Array
编辑:对不起,我误解了这个问题。以下是一些更有帮助的建议......您可以使用splitTyConApp
和朋友将TypeRep
拆分为其组成位:
> splitTyConApp (typeOf (undefined :: Array Int Int))
(Array,[Int,Int])
> let arr = typeRepTyCon (typeOf (undefined :: Array Int Int))
> mkTyConApp arr [] == typeOf2 (undefined :: Array a b)
True
答案 1 :(得分:1)
如果你有一组封闭的类型是你的程序想要推理的唯一类型,你可以考虑回避Data.Typeable
并使用GADT滚动你自己的类型表示,如下所示。此标准与Typeable
之间的差异如下:
TypeRep
的Data.Typeable
没有表示他们所代表的类型的类型变量,而在下面的替代中,您获得了TypeRep a
,其中a
是您的TypeRep
代表的类型(例如typeOf "foo" :: TypeRep [Char]
)。TypeRep
定义需要列出所有可表示的类型和类型构造函数。为什么我建议走这条复杂的路线?因为您可以使用此技术消除field
:
data Schema a = ...
| Field (TypeRep a) -- my TypeRep from below, not the standard one!
| ...
field :: TypeRep a -> Schema a
field t = Field typeRep
这里的缺点是GADT TypeRep
有一个类型参数,它需要一些其他方法来处理你的Object :: [Schema] -> Schema
构造函数的情况,因为这会取代[Schema]
[Schema a]
。也许你可以尝试这样的事情:
{-# LANGUAGE GADTs #-}
data Schema a where
Field :: TypeRep a -> Schema a
Array :: Schema a -> Schema (Array a)
Object2 :: Schema a -> Schema b -> Schema (a, b)
Object3 :: Schema a -> Schema b -> Schema c -> Schema (a, b, c)
...
但我认为,如果您研究下面的代码,您可能会发现一些可以纳入您正在做的事情的想法 - 您的TypeEnum
类型与下面的TypeRep
类型类似,不同的是除了原子类型之外,我还能够表示类型构造函数。
所以这里是代码(对于你选择的类型应该很容易修改):
{-# LANGUAGE GADTs #-}
import Control.Applicative
----------------------------------------------------------------
----------------------------------------------------------------
--
-- | Type representations. If @x :: TypeRep a@, then @x@ is a singleton
-- value that stands in for type @a@.
data TypeRep a where
Integer :: TypeRep Integer
Char :: TypeRep Char
Maybe :: TypeRep a -> TypeRep (Maybe a)
List :: TypeRep a -> TypeRep [a]
Pair :: TypeRep a -> TypeRep b -> TypeRep (a, b)
-- | Typeclass for types that have a TypeRep
class Representable a where
typeRep :: TypeRep a
instance Representable Integer where typeRep = Integer
instance Representable Char where typeRep = Char
instance Representable a => Representable (Maybe a) where
typeRep = Maybe typeRep
instance Representable a => Representable [a] where
typeRep = List typeRep
instance (Representable a, Representable b) => Representable (a,b) where
typeRep = Pair typeRep typeRep
typeOf :: Representable a => a -> TypeRep a
typeOf = const typeRep
----------------------------------------------------------------
----------------------------------------------------------------
--
-- | Type equality proofs.
data Equal a b where
Reflexivity :: Equal a a
-- | Induction rules for type equality proofs for parametric types
induction :: Equal a b -> Equal (f a) (f b)
induction Reflexivity = Reflexivity
induction2 :: Equal a a' -> Equal b b' -> Equal (f a b) (f a' b')
induction2 Reflexivity Reflexivity = Reflexivity
-- | Given two TypeReps, prove or disprove their equality.
matchTypes :: TypeRep a -> TypeRep b -> Maybe (Equal a b)
matchTypes Integer Integer = Just Reflexivity
matchTypes Char Char = Just Reflexivity
matchTypes (List a) (List b) = induction <$> (matchTypes a b)
matchTypes (Maybe a) (Maybe b) = induction <$> (matchTypes a b)
matchTypes (Pair a b) (Pair a' b') =
induction2 <$> matchTypes a a' <*> matchTypes b b'
matchTypes _ _ = Nothing
----------------------------------------------------------------
----------------------------------------------------------------
--
-- Example use: type-safe coercions and casts
--
-- | Given a proof that a and b are the same type, you can
-- actually have an a -> b function.
coerce :: Equal a b -> a -> b
coerce Reflexivity x = x
cast :: TypeRep a -> TypeRep b -> a -> Maybe b
cast a b x = coerce <$> (matchTypes a b) <*> pure x
----------------------------------------------------------------
----------------------------------------------------------------
--
-- Example use: dynamic data
--
data Dynamic where
Dyn :: TypeRep a -> a -> Dynamic
-- | Inject a value of a @Representable@ type into @Dynamic@.
toDynamic :: Representable a => a -> Dynamic
toDynamic = Dyn typeRep
-- | Cast a @Dynamic@ into a @Representable@ type.
fromDynamic :: Representable a => Dynamic -> Maybe a
fromDynamic = fromDynamic' typeRep
fromDynamic' :: TypeRep a -> Dynamic -> Maybe a
fromDynamic' :: TypeRep a -> Dynamic -> Maybe a
fromDynamic' target (Dyn source value) = cast source target value
编辑:我忍不住再玩上述内容了:
{-# LANGUAGE StandaloneDeriving #-}
import Data.List (intercalate)
--
-- And now, I believe this is very close to what you want...
--
data Schema where
Field :: TypeRep a -> Schema
Object :: [Schema] -> Schema
Array :: Schema -> Schema
deriving instance Show (TypeRep a)
deriving instance Show (Schema)
example :: Schema
example = Object [Field (List Char), Field Integer]
describeSchema :: Schema -> String
describeSchema (Field t) = "Field of type " ++ show t
describeSchema (Array s) = "Array of type " ++ show s
describeSchema (Object schemata) =
"an Object with these schemas: "
++ intercalate ", " (map describeSchema schemata)
有了这个,describeSchema example
会产生"an Object with these schemas: Field of type List Char, Field of type Integer"
。