2017-10-28 276 views
1

我的目標是編寫採用一些多態值的函數,並使用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

回答

5

我無法真正想象你的目的,但代碼可能看起來像這樣。我使用的是新的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'的新實例兼容。) –

3

抱着我的啤酒。

{-# 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'則另存兩者使用);甚至還保存了角色。 –

相關問題