2014-10-10 6 views
12

Ich arbeite mit Data.Typeable und insbesondere möchte ich in der Lage sein, korrekte Typen einer bestimmten Art zu erzeugen (zB *). Das Problem, das ich in laufen ließe, ist, dass TypeRep uns die folgende (Arbeiten mit der Version in GHC 7.8) erlaubt, zu tun:Ist es möglich, den Typ eines Typ-Konstruktors in Haskell zu bekommen?

let maybeType = typeRep (Proxy :: Proxy Maybe) 
let maybeCon = fst (splitTyConApp maybeType) 
let badType = mkTyConApp maybeCon [maybeType] 

Hier badType ist in einem gewissen Sinne die Darstellung des Typs Vielleicht Vielleicht das ist kein gültiger Typ jeglicher Art:

> :k Maybe (Maybe) 

<interactive>:1:8: 
    Expecting one more argument to ‘Maybe’ 
    The first argument of ‘Maybe’ should have kind ‘*’, 
     but ‘Maybe’ has kind ‘* -> *’ 
    In a type in a GHCi command: Maybe (Maybe) 

ich sehe nicht diese bei Typ-Ebene für die Durchsetzung, aber ich möchte in der Lage sein, ein Programm zu schreiben, das intelligent genug, um eine solche Typen zur Laufzeit zu vermeiden, zu konstruieren. Ich kann dies mit Datenebenenbegriffen mit TypeRep tun. Im Idealfall würde ich so etwas wie

data KindRep = Star | KFun KindRep KindRep 

hat und eine Funktion kindOf mit kindOf Int = Star hat (wahrscheinlich wirklich kindOf (Proxy :: Proxy Int) = Star) und kindOf Maybe = KFun Star Star, so dass ich „Art-check“ meinen TypeRep Wertes.

Ich denke, ich kann das manuell mit einer polykinded typeclass wie Typeable tun, aber ich würde lieber nicht meine eigenen Instanzen für alles schreiben müssen. Ich würde es auch vorziehen, nicht zu GHC 7.6 zurückzukehren und die Tatsache zu verwenden, dass es für typeable Typen verschiedener Arten separate Typklassen gibt. Ich bin offen für Methoden, die diese Informationen von GHC erhalten.

+0

'typeOf1' und' Typeable1' (und Freunde) werden immer noch aus 'Data.Typeable' exportiert ... sie sind in 7.8 verwendbar. –

Antwort

12

Wir können die Art eines Typs bekommen, aber wir müssen eine ganze Reihe von Spracherweiterungen bei GHC dazu werfen, einschließlich der (in diesem Fall) fragwürdigen und AllowAmbiguousTypes.

{-# LANGUAGE KindSignatures #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE PolyKinds #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE AllowAmbiguousTypes #-} 

import Data.Proxy 

Mit Ihrer Definition für eine KindRep

data KindRep = Star | KFun KindRep KindRep 

definieren wir die Klasse von Kindable Dinge, deren Art kann

class Kindable x where 
    kindOf :: p x -> KindRep 

Die erste Instanz dafür ist einfach, alles von Art bestimmt werden * ist Kindable:

instance Kindable (a :: *) where 
    kindOf _ = Star 

Es ist schwierig, die Art von höher-geknoteten Typen zu bekommen. Wir werden versuchen zu sagen, dass, wenn wir die Art seines Arguments und die Art des Ergebnisses der Anwendung auf ein Argument finden können, wir seine Art herausfinden können. Da es kein Argument gibt, wissen wir leider nicht, welcher Typ sein Argument sein wird; Deshalb brauchen wir AllowAmbiguousTypes.

instance (Kindable a, Kindable (f a)) => Kindable f where 
    kindOf _ = KFun (kindOf (Proxy :: Proxy a)) (kindOf (Proxy :: Proxy (f a))) 

Combined erlauben diese Definitionen uns Dinge zu schreiben, wie

kindOf (Proxy :: Proxy Int) = Star 
kindOf (Proxy :: Proxy Maybe) = KFun Star Star 
kindOf (Proxy :: Proxy (,)) = KFun Star (KFun Star Star) 
kindOf (Proxy :: Proxy StateT) = KFun Star (KFun (KFun Star Star) (KFun Star Star)) 

Nur nicht versuchen, wie Proxy

kindOf (Proxy :: Proxy Proxy) 

die glücklicherweise in der Art eines polykinded Typ zu bestimmen Ergebnisse ein Compilerfehler in nur einer endlichen Zeitmenge.

+0

FYI, die meisten Haskell-Meister, denen ich begegnet bin, betrachten "UndecidableInstances" als vernünftige Sache. Ich könnte mir vorstellen, dass 'AllowAmiguousTypes' ein bisschen auf der Schattenseite liegt - macht das den Schluss ein bisschen flockig? – dfeuer