2016-11-19 1 views
2

Ich bin ein modulares und erweiterbares Texteditor in Haskell zu schreiben, und ich mag Plugins so implementieren: Der Autor des Plugins bietet eine einzige Funktion, die etwa wie folgt aussieht:Wie kann ich Benutzer Plugins in meinen Typen behandeln?

handleEvent :: (PluginState, EditorState) -> Event -> (PluginState, EditorState) 

Wie Jedes Ereignis tritt auf, wenn das Plug-in den aktuellen Editorstatus und den angepassten Chunk seines eigenen Status verwendet, um einen neuen Editorstatus und einen neuen Status des Plugins zu berechnen. Natürlich wird jedes Plugin einen anderen Typ für den Plugin-Status haben, also bleibe ich dran, wie ich das in meinem System allgemein integrieren kann.

Wie kann ich schreiben vage etwas wie folgt aus:

type Plugin = (PluginState, EditorState) -> Event -> (PluginState, EditorState) 
data MyEditor = MyEditor EditorState [Plugin] [PluginState] 

Wenn PluginState kein konkreter ist?

TLDR; Wie kann ich eine Karte von Werten mit nicht-konkreten Typen auf eine barrierefreie Art und Weise speichern, ohne den Zustandstyp jedes Plugins in meinen globalen Zustand einzubetten? Ich bin okay, wenn ich den Editor neu kompiliere, wenn ein neues Plugin hinzugefügt wird.

Danke! Ich bin wirklich auf diesem einen fest:/

Wenn Sie irgendeine Erklärung benötigen, bitte nur fragen!

+0

Ein Ausgangspunkt dafür wäre, nachzuschauen, wie GHC Plugins verarbeitet und wie Yi die Konfiguration handhabt. Beide nehmen eine teilweise Neukompilierung Ansatz ich denke ... – Alec

+1

Klingt wie ein Fall für https://hackage.haskell.org/package/vault – Gurkenglas

Antwort

1

Natürlich jedes Plugin wird eine andere Art für den Plugin-Zustand haben, so dass ich stecken zu bleiben, wie ich das in mein System in allgemeiner Weise integrieren kann.

Vielleicht könnten Sie ein existential type verwenden, um den Plug-Zustand zu verbergen, so etwas wie

{-# LANGUAGE ExistentialQuantification #-} 

data Plugin = forall ps. Plugin { 
     currentState :: ps 
    , transition :: ps -> EditorState -> Event -> (ps, EditorState) 
    } 

handleEvent :: Plugin -> EditorState -> Event -> (Plugin,EditorState) 
handleEvent (Plugin ps t) es e = 
    let (ps',es') = t ps es e 
    in (Plugin ps' t,es') 

Jetzt jedes Plugin ist von der gleichen Art, und doch unterschiedliche Plugin Werte interne Zustände verschiedenen Typen haben:

charPlugin :: Plugin 
charPlugin = Plugin 'a' (\ps es e -> (succ ps,es)) 

intPlugin :: Plugin 
intPlugin = Plugin (1::Int) (\ps es e -> (succ ps,es)) 

(ich fand seine Inspiration aus dem Fold Typ aus dem Paket foldl, die Existenziale in einem ähnlichen verwendet Art und Weise)

Sie jetzt eine Liste von Plugins haben.

plugins :: [Plugin] 
plugins = [charPlugin,intPlugin] 

Eine mögliche Entwicklung des Designs der internen Zustände wäre zu beschränken Instanzen einiger typeclass zu sein:

data Plugin = forall ps. Show ps => Plugin { 
     currentState :: ps 
    , transition :: ps -> EditorState -> Event -> (ps, EditorState) 
    } 

Ich vermute, eine Monoid Instanz könnte für den Plugin Typ definiert werden.

Auch könnten wir explizit denken Plugin über die Art des Ereignisses Parametrisierung es, wie

data Plugin e = ... 

In diesem Fall akzeptiert könnte Plugin eine Instanz von Contravariant und vielleicht gemacht werden Divisible auch.

Und wenn wir wild und parametrieren im Editor Zustand

data Plugin es e = ... 

gehen dann könnten wir vielleicht einen Weg zu „Zoom“ ein bestimmtes Plugin in einem allgemeineren Zustand zu arbeiten finden als die, für die sie war definiert.

+0

Es ist ein bisschen peinlich mit zu arbeiten, wenn Sie sich bewerben (Ich muss die Übergangsfunktion entpacken, gelten es, dann packen Sie den Zustand und die Übergangsfunktion wieder in ein neues Plugin zurück, aber abgesehen davon funktioniert es wie ein Charme! Vielen Dank! –

+0

@Chris Penner Das packing-unpacking kann mit einer einzigen Funktion wie 'handleEvent' behandelt werden. Außerdem sollte der 'Plugin'-Typ strikte Felder haben, um Platzlecks zu vermeiden. – danidiaz

Verwandte Themen