【问题标题】:How to specify type of value via 'TypeRep'?如何通过“TypeRep”指定值的类型?
【发布时间】:2018-04-10 02:29:42
【问题描述】:

我的目标是编写带有一些多态值和列表的函数,其中 typereps 表示具体类型。它返回具有相同值但已转换为通过 typereps 指定的具体类型的新列表。

让我们有这样的值列表:["one", "two"] 启用 -XOverloadedStrings
分别为IsString a => a

我们可以通过这种方式获得的 typerep 列表:

import Data.Typeable (Proxy(..), typeRep)
import Data.Text     (Text)

[typeRep (Proxy :: Proxy String), typeRep (Proxy :: Proxy ByteString)]

有什么方法可以得到String 类型的"one"ByteString 类型的"two"

附:为了防止根据包含不同类型值的列表出错,我们可以将每个值包装在Dynamic.中,如下例(伪代码):

 {-# LANGUAGE ParallelListComp #-}

 import Data.Dynamic (toDyn)

 [ toDyn (val :: type') | val <- vals | type' <- concreteTypes ]

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

【问题讨论】:

  • 结果列表的类型是什么?使用新的反射机制可能会发生这样的事情,但您至少需要返回一个 HList。
  • 一个列表只能包含一种类型的元素。
  • @WillemVanOnsem 已编辑。
  • @Alec 在我的情况下,它应该稍后变成动态的。

标签: haskell dynamic types polymorphism generic-programming


【解决方案1】:

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

【讨论】:

  • Classic Data.Typeable 也不适用于此:它不提供 eqTypeRep 来获得类型级别的相等,只有术语级别的相等。您需要 unsafeCoerce 或类似名称。
  • 这正是我想到的解决方案。我喜欢新的Type.Reflection 模块。 :)
  • 另外,这似乎要求您提前知道可能需要的类型列表,对吗? (这里不能在不修改convert的情况下将SomeTypeRep (typeRep @ByteString)添加到types——所以convert不能与IsString的新实例前向兼容。)
  • @DanielWagner convert 确实在从属映射中进行查找(键是类型T,值是函数String -&gt; T)然后应用函数。更通用的实现将接受任何此类映射作为输入,使其可以以这种方式扩展。
  • @user2407038 嘿,猜猜看,fromString 也在这样做!为什么不重复使用那台机器? (......就像我的回答一样,提示提示。)(此外,映射不依赖。依赖转到term -&gt; type;这转到type -&gt; term。)
【解决方案2】:

拿着我的啤酒。

{-# 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 -&gt; * 概括为Forall, Exists :: [Constraint] -&gt; * 以同时轻松地支持IsStringTypeable,这有点像类型级别的黑客攻击,但不会太费劲。类型族可以给你一个Elem :: Constraint -&gt; [Constraint] -&gt; Bool,它可以用来替换上面任何地方的c ~ IsString

【讨论】:

  • P.S.我很难过[] @ByteString 不起作用。
  • 好像没有解决基于TypeRep指定类型的问题。
  • @errfrom 正确,必须根据Evidence 或新的TypeRep :: k -&gt; * 指定。但我不认为这是一个问题:只需将typeRep (Proxy :: Proxy a) 替换为Evidence ([] :: [a]) 即可(或者如果您需要typeRep 用于其他用途,请同时保存);它甚至保存了字符。
  • @DanielWagner 是的,但是在OverloadedLists 面前应该怎么办?在这种情况下,应用程序可能应该是容器类型而不是包含类型。
  • @Alec 那么如果它不能与OverloadedLists 一起使用呢?当您打开 pragma 时,很多事情都会中断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-23
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多