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

How to specify type of value via 'TypeRep'?

我的目标是编写函数,该函数采用一些多态值并列出表示具体类型的类型。它 returns 具有相同值但已转换为通过 typereps 指定的具体类型的新列表。

让我们有这样的值列表:["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 ]

可以使用模板Haskell,但是太丑了。

拿着我的啤酒。

{-# 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 TypeableDynamic 同构。您可能需要将 Forall, Exists :: Constraint -> * 泛化为 Forall, Exists :: [Constraint] -> * 以轻松地同时支持 IsStringTypeable,这有点类型级别的黑客攻击,但不会太费力。类型族可以给你一个Elem :: Constraint -> [Constraint] -> Bool,它可以用来替换上面所有地方的c ~ IsString

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