2013-01-04 16 views
5

Ich arbeite an einem anwendungsspezifischen Funktor, der ein Monoid enthält, um die Ausführung zu "sehen". Manchmal interessiert mich dieser Teil überhaupt nicht, daher ist die Wahl des Monoids irrelevant, da er niemals konsumiert wird. Ich habe vereinfacht, was ich habe in:Verwenden von Constraint-Typen und Typfamilien mit 'begrenzten' Constraints

{-# 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 verwendet wird, um verschiedene a s in einem einzigen b zu verwandeln. Merge ist eine große Vereinfachung meiner eigentlichen Funktor, aber der Punkt ist, enthält es eine Art Familie/Constraint und meine Absicht ist es, genau anzugeben, was Render ers Merge erfordert.

Nun, ich könnte auf „run“ wollen die Merge, aber verwerfen die Ansicht, die so etwas wie verwandt ist:

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

Aber diese fehl, weil:

konnte nicht ableiten (Renderer a()) aus einer Verwendung von merge

entstehen I () als meine Monoid gewählt, weil f orall a, wir haben eine Instanz von Render a(). Also, wenn es eine Möglichkeit gab zu sagen, dass Merge a nur eine Sammlung Render Einschränkungen bedeutet, dann würde dies gut funktionieren. Natürlich ist Merge a allgemeiner als das - es könnte beliebige Einschränkungen hinzufügen, was den Kompilierungsfehler erklärt.

Gibt es trotzdem zu erreichen, was ich will ohne die Signatur von runFoo ändern?

+0

Hat 'Renderer' immer auch genau ein' Render'? –

+0

@Tinctorius - Nein, hängt im Allgemeinen von der Anzahl der verschiedenen Typen in den Feldern von 'Foo' ab. – ocharles

+0

Gibt es einen Grund, warum' Merge' nicht auch 'b' als Parameter hat? –

Antwort

6

Dies könnte nicht skaliert, wenn Sie viele dieser Fälle haben, aber dies funktioniert:

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

Ich hatte das nicht bedacht. Ich könnte diese Einschränkung lesen, da "Merge's zumindest Rendering mit dem'() 'Monoid" unterstützen sollte. Dies zieht jedoch "UnentscheidbareInstanzen" nach sich. – ocharles

+0

"UndecidableInstances ist keine gefährliche Flagge. Es wird niemals dazu führen, dass der Typ-Checker ein Programm akzeptiert, das" schief läuft ". Die einzige schlechte Konsequenz der Verwendung des Flags ist, dass Typ-Checker uns sagen, dass er nicht entscheiden kann, ob unser Programm gut typisiert ist, vorausgesetzt, das Kontext-Stack - Tiefenlimit. " http://okmij.org/ftp/Haskell/TypeClass.html#undecidable-inst-defense –

+0

Ich weiß das, ich benutze es bereits an 3 anderen Stellen;) Aber im Idealfall finde ich Lösungen, die es nicht erfordern, auch aus rein akademischen Gründen. Ich werde diese Antwort wahrscheinlich akzeptieren, lasse aber den Boden noch etwas länger offen. Dies ist in der Tat ein Vergnügen! – ocharles