如何通过'TypeRep'指定值的类型?

如何通过'TypeRep'指定值的类型?

问题描述:

我的目标是编写采用一些多态值的函数,并使用typereps表示具体类型的列表。它返回具有相同值的新列表,但已经转换为通过typereps指定的具体类型。如何通过'TypeRep'指定值的类型?

让我们有这样的值列表:["one", "two"]-XOverloadedStrings启用。
分别是,每个的类型是IsString a => a

typereps的

列表中,我们可以得到在这样的方式:

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 ] 

它可以使用模板哈斯克尔来完成,但是这将是太丑陋。

+0

结果列表的类型是什么?像这样的东西很可能使用新的反射机器,但你至少需要返回一个HList。 – Alec

+1

列表只能由* 1 *类型的元素组成。 –

+0

@WillemVanOnsem编辑。 – errfrom

我无法真正想象你的目的,但代码可能看起来像这样。我使用的是新的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 
+0

经典'Data.Typeable'也不适用于此:它不提供'eqTypeRep'来获得类型级别的平等,只有term级别的相等。你需要'unsafeCoerce'或类似的东西。 –

+0

这正是我想到的解决方案。我喜欢新的'Type.Reflection'模块。 :) – Alec

+1

此外,这似乎要求你知道可能的类型列表,可能会提前要求,对不对? (在这里''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] -> *,以便一次轻松支持IsStringTypeable,这是一种类型级别的黑客攻击,但不会太费劲。类型家庭可以给你一个Elem :: Constraint -> [Constraint] -> Bool可以用来代替上面各处的c ~ IsString

+0

P.S.我非常难过,'[] @ ByteString'不起作用。 –

+0

似乎,它不能解决基于[TypeRep](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Typeable.html#t:TypeRep)指定类型的问题。 – errfrom

+0

@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'则另存两者使用);甚至还保存了角色。 –