2016-11-19 40 views
2

我寫在Haskell模塊化和可擴展的文本編輯器,我想實現這樣的插件:插件的作者提供了一個單一的功能,看起來是這樣的:如何處理我的類型中的用戶插件?

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

由於每個事件都會發生,插件可以使用當前編輯器狀態和自己狀態的自定義塊來計算新的編輯器狀態和新的插件狀態。當然,每個插件都會爲插件狀態設置不同的類型,所以我會陷入困境,不知道如何以通用的方式將它集成到我的系統中。

我怎麼能寫的東西隱約這樣的:

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

當PluginState不是一個具體的類型?

TLDR;如何以可訪問的方式存儲具有非具體類型的值的映射,而無需將每個插件的狀態類型烘焙到我的全局狀態?在添加新插件時,我可以重新編譯編輯器。

謝謝!我真的被困在這一個:/

如果你需要任何澄清,請問!

+0

這個的起點是查看GHC如何處理插件以及Yi如何處理配置。我認爲這兩種方法都採用了部分重新編譯的方法... – Alec

+1

聽起來像是https://hackage.haskell.org/package/vault的案例 – Gurkenglas

回答

1

當然,每個插件將有一個不同的類型的插件狀態,所以我陷入瞭如何以通用的方式將其整合到我的系統中。

也許你可以使用一個existential type隱藏插件狀態,像

{-# 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') 

現在,每個插件是同類型的,然而不同的插件值可以有不同類型的內部狀態:

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

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

(I了靈感來自Fold類型從與foldl包,它使用existentials以類似路)

你現在可以有插件列表:

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

設計的一個可能的演變將是約束內部狀態是某種類型類的實例:

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

我懷疑Monoid實例可以定義爲Plugin類型。

此外,我們能想到的,它接受的事件類型明確參數化Plugin,像

data Plugin e = ... 

在這種情況下,插件可以由Contravariant一個實例或許Divisible爲好。

如果我們去野外和參數的編輯狀態

data Plugin es e = ... 

那麼也許我們可以找到一個方法來「放大」給定的插件在一個比對,它是一個更普遍的工作狀態定義。

+0

應用時處理有點尷尬(我必須解壓轉換函數,apply它,然後重新包裝狀態和過渡功能備份到一個新的插件被返回),但除此之外,它就像一個魅力!謝謝! –

+0

@Chris Penner雖然打包解包可以通過一個函數來處理,例如'handleEvent'。此外,'Plugin'類型應該有嚴格的字段以避免空間泄漏。 – danidiaz

相關問題