2013-01-04 11 views
5

Sto lavorando a un functor applicativo che contiene un monoide per "visualizzare" l'esecuzione. Tuttavia, a volte non mi interessa affatto questa parte, quindi la scelta di monoid è irrilevante in quanto non verrà mai consumata. Ho semplificato ciò che ho in:Utilizzo di tipi di vincoli e famiglie di tipi con vincoli "limitati"

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE ConstraintKinds #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeFamilies #-} 

import GHC.Exts 

class Render a b where render :: a -> b 
instance Render a() where render = const() 

class Merge a where 
    type Renderer a b :: Constraint 
    merge :: Renderer a b => a -> b 

data Foo = Foo Bool 

instance Merge Foo where 
    type (Renderer Foo) m = (Render Bool m) 
    merge (Foo b) = render b 

Render viene utilizzato per trasformare i vari a s in un unico b. Merge è una grande semplificazione del mio attuale functor, ma il punto è che contiene un tipo family/constraint e la mia intenzione è quella di specificare esattamente cosa richiede Render a Merge.

Ora, potrei voler "correre" il Merge, ma eliminare la vista, che è simile a qualcosa di simile:

runFoo :: Merge a => a -> Int 
runFoo x = case merge x of() -> 5 

Ma questo non riuscirà perché:

Impossibile dedurre (Renderer a()) derivanti da un utilizzo di merge

ho scelto come mio () monoid perché f orall a, abbiamo un'istanza di Render a(). Quindi, se c'era un modo per dire che Merge a significa solo una raccolta Render vincoli allora funzionerebbe bene. Naturalmente, Merge a è più generale di quello - potrebbe aggiungere vincoli arbitrari, il che spiega l'errore di compilazione.

È comunque necessario ottenere ciò che voglio senza cambiando la firma di runFoo?

+0

Vuol 'renderer' includono sempre esattamente un' Render'? –

+0

@Tinctorius - no, generalmente dipende dalla quantità di tipi distinti nei campi di 'Foo' – ocharles

+0

C'è un motivo per cui" Merge' non ha anche 'b' come parametro? –

risposta

6

Questo potrebbe non scala se si dispone di un sacco di questi casi, ma questo funziona:

class Renderer a() => Merge a where 
    ... 
+0

Non l'avevo considerato. Potrei leggere quel vincolo come 'Merge's dovrebbe almeno supportare il rendering con '()' monoid'. Tuttavia, questo comporta "UndecidableInstances'. – ocharles

+0

"UndecidableInstances non è un flag pericoloso. Non farà mai in modo che il type-checker accetti un programma che" non funziona ". L'unica cattiva conseguenza dell'uso del flag è che il correttore di tipi potrebbe dirci che non può decidere se il nostro programma è ben tipizzato, dato il limite di stack di contesto - profondità. " http://okmij.org/ftp/Haskell/TypeClass.html#undecidable-inst-defense –

+0

Lo so, lo uso già in altri 3 posti;) Ma idealmente mi piace trovare soluzioni che non lo richiedano, anche se per ragioni puramente accademiche. Probabilmente accetterò questa risposta, lasciando però il terreno aperto ancora per un po '. Questo è davvero un piacere! – ocharles