2016-03-25 5 views
3

eine Klasse Gegeben"Free" Typ Variablen durch funktionale Abhängigkeiten in Typ Synonyme gebunden

class MonadSignal m sigs | m -> sigs where ... 

und eine Klasse

class CanSignal sigs sigs' where ... 

Ich möchte eine Art Synonym wie diese

type MonadSignal' sigs m = (MonadSignal m sigs', CanSignal sigs sigs') 
definieren

Hier wird die sigs' Variable im Kopf des MonadSignal' Typ Synonyms aber nicht erwähnt ist da, nur um die erste und die zweite Nebenbedingung zu verbinden, und es ist eindeutig durch m festgelegt, die ist im Kopf erwähnt.

Normalerweise denke ich, ich wäre in der Lage es auf der RHS forall aber da dies nur ein Constraint Synonym ist, gibt es keinen eigentlichen Körper für die Variable in erscheinen.

hier etwas getan werden kann? (Außer die Variable in den Kopf setzen und den falschen Eindruck, wenn das Synonym tatsächlich verwendet wird, dass es eine tatsächliche „Variable“)

Antwort

3

Es ist keine konservative Änderung, aber eine Lösung wäre, von funktionalen Abhängigkeiten zu Typfamilien zu wechseln. Sie könnten eine zugehörige Familie

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE ConstraintKinds #-} 
{-# LANGUAGE TypeFamilies #-} 
module TypeFamily where 

class Monad m => MonadSignal m where 
    type Sig m :: * 
    make :: m sig 

class CanSignal sigs sigs' where 

type MonadSignal' sigs m = (MonadSignal m, CanSignal sigs (Sig m)) 

Es ist machen vielleicht nicht ganz so hübsch wie Multitypklassen für beide verwenden, aber es sollte in Ausdruckskraft nur als gleichwertig sein und Sie müssen nicht über das Tun dieses „transitive closure“ Sorge irgendwie.

+0

Dies ist eine gültige Lösung, die ich zuvor verwendet habe, aber ich brauchte die zusätzliche Flexibilität, die ich aus funktionalen Abhängigkeiten bekomme. –

+0

Ah das ist eine Schande, Welche zusätzliche Flexibilität aus Neugier? – jozefg

+0

Ich brauchte ein paar weitere Typparameter, damit ihre gegenseitigen Abhängigkeiten ein bisschen komplexer werden, als was mit den zugehörigen Typen ausgedrückt werden kann. –

1

Sie die Art Synonym mit einer Klasse ersetzen:

class MonadSignal' sigs (m :: * -> *) 
instance (MonadSignal m sigs', CanSignal sigs sigs') => MonadSignal' sigs m 

In der Klasseninstanz, die Typvariable sigs' erscheint nicht im Klassenkopf, , also müssen Sie {-# LANGUAGE UndecidableInstances #-} verwenden, damit es funktioniert. Ich glaube zwar nicht, dass diese Klasse tatsächlich zur Beendigung führen kann.

+0

Das bin ich auf einer bestimmten Ebene stört, da es ziemlich viel ist ein Hack (da die Instanz passt alles zusammen). Bist du dir sicher, dass das funktioniert?Wenn ich irgendwo eine 'MonadSignal' sigs m'-Bedingung habe, kann ich den 'MonadSignal'- und den' CanSignal'-Kontext nicht verwenden, da diese von der Instanz abhängen. –

+0

Ja, die Instanz passt zu allem, aber ein Typ-Synonym kann auf jeden Typ angewendet werden - es ist genau die gleiche Situation. Die Einschränkung 'MonadSignal' wird immer sofort auf die rechte Seite der Instanz reduziert, da der Klassenkopf überhaupt * beliebigen * Typen entspricht. Ich weiß nicht, ob ich das einen Hack nennen würde - es gibt wirklich keine Möglichkeit, so etwas nur mit Typensynonymen zu machen. Sie benötigen eine Klasse oder eine Typfamilie. – user2407038

+0

Das Problem ist, dass Sie keine Superklassenbeschränkungen oder irgendwelche Methoden erhalten, also ist die 'MonadSignal'-Bedingung völlig wertlos. – dfeuer

2

Leider glaube ich, dass Sie diesen falschen Eindruck geben müssen, wenn Sie auf einem richtigen fempep bestehen (sehen Sie die Unterseite dieser Antwort, wenn Sie einen etwas anderen Ansatz akzeptieren). Ich dachte kurz, vielleicht könnten Sie stattdessen MonadSignal' eine Klasse machen, aber Sie würden nicht in der Lage sein, die Superklassenbeschränkung zu geben, die Sie wollen. Sie können das wettmachen, indem Sie wirklich explizit mit einer oder mehreren Dict Methoden arbeiten, aber dann wird Ihr Leben schwierig sein.

Obwohl dies nicht relevant ist, ist es wahrscheinlich besser

class MonadSignal sigs m | m -> sigs 

Vertauschen der Reihenfolge der Parameter zu definieren, die Ihnen weitere nützliche partielle Anwendung erhält und macht GeneralizedNewtypeDeriving Arbeit für Ihre Klasse.


Es gibt eine andere Art und Weise Art Familien zu verwenden, auf der Grundlage einer legitimen Umsetzung fundeps in Art Familien:

class (sigs ~ Sigs m) => MonadSignal sigs m where 
    type Sigs m :: * -- Or appropriate kind 
    ... 

type MonadSignal' sigs m = (MonadSignal (Sigs m) m, CanSignal sigs (Sigs m)) 
+0

Ja, wie ich schon sagte. Ich kann assoziierte Typen für meinen konkreten Anwendungsfall nicht wirklich verwenden, da ich einige Typenparameter mehr habe, die komplexe gegenseitige Abhängigkeiten haben. –

Verwandte Themen