2017-10-28 4 views
1

Mein Ziel ist es, eine Funktion zu schreiben, die einige polymorphe Werte annimmt und mit Typreps auflistet, die konkrete Typen darstellen. Sie gibt eine neue Liste mit den gleichen Werten zurück, die jedoch bereits auf konkrete Typen angewendet wurden, die über typeReps angegeben wurden.Wie kann ich den Wert über 'TypeRep' festlegen?

Lassen Sie uns haben solche Liste von Werten: ["one", "two"] mit -XOverloadedStrings aktiviert.
Entsprechend ist jeder Typ IsString a => a.

Liste der typereps wir in einer solchen Art und Weise bekommen konnte:

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

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

Gibt es eine Möglichkeit String und "two" des Typs zu erhalten "one" vom Typ ByteString?

P.S. Um zu verhindern, Fehler nach Listenwert verschiedenen Typen enthalten, können wir jeden Wert in Dynamic., wie im Beispiel unten (Pseudo-Code) wickeln:

{-# LANGUAGE ParallelListComp #-} 

import Data.Dynamic (toDyn) 

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

es getan werden könnte Template Haskell verwenden, aber es wird auch sein hässlich.

+0

Was wäre der Typ der resultierenden Liste? So etwas ist wahrscheinlich mit der neuen Reflektionsmaschinerie möglich, aber Sie müssten mindestens einen HList zurückgeben. – Alec

+1

Eine Liste kann nur aus * einem * Elementtyp bestehen. –

+0

@WillemVanOnsem Bearbeitet. – errfrom

Antwort

5

Ich kann mir Ihren Zweck nicht wirklich vorstellen, aber der Code wird wahrscheinlich in etwa so aussehen. Ich benutze die neue Type.Reflection Schnittstelle, weil ich damit vertrauter bin als mit dem klassischen Data.Typeable, aber das sollte auch dafür funktionieren.

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

Klassisches 'Data.Typeable' wird auch dafür nicht funktionieren: es bietet' eqTypeRep' nicht an, um eine Gleichheit auf Typenebene zu erhalten, nur Gleichheit auf Termebene. Du brauchst stattdessen 'unsafeCoerce' oder ähnliches. –

+0

Dies ist genau die Lösung, die mir vorschwebte.Ich liebe das neue 'Type.Reflection' Modul. :) – Alec

+1

Auch dies scheint zu verlangen, dass Sie die Liste der möglichen Typen kennen, die man im Voraus fragen könnte, oder? (Hier könnte man 'SomeTypeRep (typeRep @ByteString)' nicht zu 'types' hinzufügen, ohne' convert' zu modifizieren - also kann 'convert' nicht mit neuen Instanzen von' IsString' vorwärtskompatibel sein.) –

3

Halten Sie mein Bier.

{-# 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 

mit der Frage, wie gefragt anzuschließen: Exists Typeable isomorph zu Dynamic. Sie müssen möglicherweise Forall, Exists :: Constraint -> * zu verallgemeinern, um sowohl IsString als auch Typeable gleichzeitig zu unterstützen, was ein bisschen Typ-Level-Hacking ist, aber nichts zu anstrengend. Typfamilien können Ihnen eine Elem :: Constraint -> [Constraint] -> Bool geben, die überall oben c ~ IsString ersetzen kann.

+0

P.S. Ich bin sehr traurig, dass '[] @ ByteString' nicht funktioniert. –

+0

Scheint, es löst nicht das Problem der Typangabe basierend auf [TypeRep] (https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Typeable.html#t:TypeRep). – errfrom

+0

@errrfrom Korrekt, Sie müssen es basierend auf 'Evidence' oder dem neuen [' TypeRep :: k -> * '] (http://hackage.haskell.org/package/base-4.10.0.0/docs/Type) angeben -Reflexion.html). Aber ich sehe das nicht als ein Problem: Ersetzen Sie einfach 'typeRep (Proxy :: Proxy a)' durch 'Evidence ([] :: [a])' ​​überall (oder speichern Sie beide, wenn Sie 'typeRep' für andere benötigen Verwendet); Es sind sogar Charaktere gespeichert. –

Verwandte Themen