如何通过'TypeRep'指定值的类型?
我的目标是编写采用一些多态值的函数,并使用typereps表示具体类型的列表。它返回具有相同值的新列表,但已经转换为通过typereps指定的具体类型。如何通过'TypeRep'指定值的类型?
让我们有这样的值列表:["one", "two"]
与-XOverloadedStrings
启用。
分别是,每个的类型是IsString a => a
。
列表中,我们可以得到在这样的方式:
import Data.Typeable (Proxy(..), typeRep)
import Data.Text (Text)
[typeRep (Proxy :: Proxy String), typeRep (Proxy :: Proxy ByteString)]
有没有什么办法让String
类型的"one"
和ByteString
型"two"
?
P.S.根据含有不同类型的值列表防止错误,我们可以在Dynamic.
包的每个值,如下面的例子(伪):
{-# LANGUAGE ParallelListComp #-}
import Data.Dynamic (toDyn)
[ toDyn (val :: type') | val <- vals | type' <- concreteTypes ]
它可以使用模板哈斯克尔来完成,但是这将是太丑陋。
我无法真正想象你的目的,但代码可能看起来像这样。我使用的是新的Type.Reflection
界面,因为我比使用经典Data.Typeable
更熟悉它,但这也适用于此。
import Type.Reflection
types :: [SomeTypeRep]
types = [SomeTypeRep (typeRep @String), SomeTypeRep (typeRep @Text)]
strings :: [String]
strings = ["one", "two"]
converted :: [Dynamic]
converted = fromJust $ zipWithM convert types strings
convert :: SomeTypeRep -> String -> Maybe Dynamic
convert (SomeTypeRep rep) s
| Just HRefl <- eqTypeRep rep (typeRep @String) = Just $ toDynamic s
| Just HRefl <- eqTypeRep rep (typeRep @Text) = Just $ toDynamic (fromString s)
| otherwise = Nothing
经典'Data.Typeable'也不适用于此:它不提供'eqTypeRep'来获得类型级别的平等,只有term级别的相等。你需要'unsafeCoerce'或类似的东西。 –
这正是我想到的解决方案。我喜欢新的'Type.Reflection'模块。 :) – Alec
此外,这似乎要求你知道可能的类型列表,可能会提前要求,对不对? (在这里''SomeTypeRep(typeRep @ByteString)'不需要修改'convert'就可以添加'types' - 所以'convert'不能与'IsString'的新实例兼容。) –
抱着我的啤酒。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString (ByteString)
import Data.String
import Data.Text (Text)
data Forall c where Forall :: (forall a. c a => a) -> Forall c
data Exists c where Exists :: c a => a -> Exists c
data Evidence c where Evidence :: c a => proxy a -> Evidence c
instance c ~ IsString => IsString (Forall c) where
fromString s = Forall (fromString s)
asProxyType :: proxy a -> a -> a
asProxyType = const id
downcast :: Evidence c -> Forall c -> Exists c
downcast (Evidence proxy) (Forall v) = Exists (asProxyType proxy v)
polymorphicStrings :: c ~ IsString => [Forall c]
polymorphicStrings = ["one", "two"]
types :: c ~ IsString => [Evidence c]
types = [Evidence ([] :: [ByteString]), Evidence ([] :: [Text])]
monomorphicStrings :: c ~ IsString => [Exists c]
monomorphicStrings = zipWith downcast types polymorphicStrings
处理此问题的问连接:Exists Typeable
同构于Dynamic
。您可能需要将Forall, Exists :: Constraint -> *
概括为Forall, Exists :: [Constraint] -> *
,以便一次轻松支持IsString
和Typeable
,这是一种类型级别的黑客攻击,但不会太费劲。类型家庭可以给你一个Elem :: Constraint -> [Constraint] -> Bool
可以用来代替上面各处的c ~ IsString
。
P.S.我非常难过,'[] @ ByteString'不起作用。 –
似乎,它不能解决基于[TypeRep](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Typeable.html#t:TypeRep)指定类型的问题。 – errfrom
@errfrom正确,您必须基于'Evidence'或新的['TypeRep :: k - > *']来指定它(http://hackage.haskell.org/package/base-4.10.0.0/docs/Type -Reflection.html)。但我不认为这是一个问题:只需将'typeRep(Proxy :: Proxy a)'替换为'Evidence([] :: [a])'(或者如果您需要'typeRep'则另存两者使用);甚至还保存了角色。 –
结果列表的类型是什么?像这样的东西很可能使用新的反射机器,但你至少需要返回一个HList。 – Alec
列表只能由* 1 *类型的元素组成。 –
@WillemVanOnsem编辑。 – errfrom