2012-10-03 11 views
7

ho qualche tipo di artificioso:vincolo polimorfico

{-# LANGUAGE DeriveFunctor #-} 

data T a = T a deriving (Functor) 

... e quel tipo è l'istanza di una classe artificiosa:

class C t where 
    toInt :: t -> Int 

instance C (T a) where 
    toInt _ = 0 

Come posso esprimere in un vincolo di funzione che è T a un'istanza di qualche classe per tutti a?

Ad esempio, si consideri la seguente funzione:

f t = toInt $ fmap Left t 

Intuitivamente, mi sarei aspettato la funzione di cui sopra a lavorare dal toInt opere T a per tutti a, ma non posso esprimere che nel tipo. Questo non funziona:

f :: (Functor t, C (t a)) => t a -> Int 

... perché quando applichiamo fmap il tipo è diventato Either a b. Non riesco a risolvere questo problema utilizzando:

f :: (Functor t, C (t (Either a b))) => t a -> Int 

... perché b non rappresenta una variabile universalmente quantificata. Né posso dire:

f :: (Functor t, C (t x)) => t a -> Int 

... o utilizzare forall x a suggerire che il vincolo è valido per tutti x.

Quindi la mia domanda è se c'è un modo per dire che un vincolo è polimorfico su alcune delle sue variabili di tipo.

+0

presumo che qualcosa di simile a 'classe C t dove Toint :: ta -> Int' non funziona, ed è necessario' C' per essere di tipo '* -> Vincolo'? Un polimorfismo gentile ti può aiutare? –

+0

@ C.A.McCann Il costruttore di tipi concreti che ho in mente è 'Proxy' da' pipes' e la classe concreta è 'Monad'. Sono funzioni di utilità di classe di tipo per i tipi proxy-like, motivo per cui il vincolo è lì. Seguendo il tuo suggerimento, definirei una classe 'MonadP' specializzata nella forma del costruttore del tipo' Proxy' e la userei invece come un vincolo. Lo svantaggio è che se gli utenti volessero scrivere funzioni di utility proxy polimorfiche nel tipo proxy-like, dovrebbero rebindare la notazione per usare 'MonadP'. –

+2

Non è possibile farlo direttamente, ma è possibile simulare, come nella risposta di Roman. Ecco il relativo ticket GHC: http://hackage.haskell.org/trac/ghc/ticket/2893 – glaebhoerl

risposta

6

Usando il pacchetto constraints:

{-# LANGUAGE FlexibleContexts, ConstraintKinds, DeriveFunctor, TypeOperators #-} 

import Data.Constraint 
import Data.Constraint.Forall 

data T a = T a deriving (Functor) 

class C t where 
    toInt :: t -> Int 

instance C (T a) where 
    toInt _ = 0 

f :: ForallF C T => T a -> Int 
f t = (toInt $ fmap Left t) \\ (instF :: ForallF C T :- C (T (Either a b))) 
+3

Vale anche la pena guardare la fonte di quel 'ForallF', se non altro per ammirare quanto semplice/brutto/terrificante l'idea alla base è :) – copumpkin

+0

@copumpkin: In realtà ho una domanda a riguardo - http://stackoverflow.com/questions/12728159/how-does-the-constraints-package-work –