2016-02-13 41 views
2

monad-coroutine包的工作,我有一些協同程序做了很多工作,它需要一些投入,不時:提高協程請求類型安全

Coroutine (Request SomeRequest SomeResponse) (State MyState) a 

其中

data SomeRequest 
    = GetImportantData Int 
    | OtherImportantStuff Float 
    | SomethingElse (Vector Int) 
    | ... 

data SomeResponse 
    = ImprtantData (Vector Float) 
    | ImportantStuff Int 
    | ... 

正如你所看到的對於SomeRequest中的每個條目,我都有相應的條目SomeResponse

在這個協同程序的運行時我有這樣的事情:

... 
ImportantData info <- request (GetImportantData 666) 
... 

現在恐怕這種做法並不好,因爲我要的是確保每當我請求與GetImportantData重要數據唯一可能的迴應是ImportantData,沒有別的。使用我目前的方法,我必須在每次發出請求時都進行模式匹配(以確保輸入實際上是我想要的)。

什麼辦法可以提高設計/方法,以確保爲GetImportantData我得到ImportantData回來只爲OtherImportantStuff我得到ImportantStuff只有等?

+0

將每個構造函數轉換爲它自己的類型,並使用帶有相關數據或類型家族。 – user2407038

回答

2

幻影類型和GADT可以幫助您在此實現更多的類型安全。

{-# LANGUAGE GADTs #-} 

import qualified Data.Vector as V 

data Important 
data SomethingElse 

data Request a where 
    GetImportantData :: Int -> Request Important 
    OtherRequest :: Float -> Request SomethingElse 

data Response a where 
    ImportantData :: V.Vector Int -> Response Important 
    OtherResponse :: Int -> Response SomethingElse 

-- a typical use case 
controller :: Request Important -> Response Important 
controller (GetImportantData n) = ImportantData $ V.singleton n 

-- or, more generally 
controller' :: Request a -> Response a 
controller' (GetImportantData n) = ImportantData $ V.singleton n 

-- error: Couldn't match type 'Important' with 'SomethingElse' 
badController :: Request a -> Response a 
badController (GetImportantData n) = OtherResponse n 

Request aResponse a是幻影類型,因爲該類型參數a無關與下面的值(例如在GetImportantDataInt)。幻影式廣泛用於確保類型安全。

語言擴展GADTs允許顯式的構造函數的類型聲明,可以很容易區分數據類型的構造函數。

代替

data Foo = Bar | Qux 

其中BarQux都有類型Foo,與GADTs一個可以通過這樣做BarQux限定

data Foo a where 
    Bar :: Foo Int 
    Qux :: Foo Float 

有不同的類型。

WikiBooks和Haskell wiki上有關於此主題的一些精彩教程。

3

而不是使用單子,協程提供的

data Request request response x = Request request (response -> x) 

定義自己的懸浮式

data MySuspension x 
    = GetImportantData Int (Vector Float -> x) 
    | GetOtherImportantStuff Float (Int -> x) 
    | ... 
    deriving (Functor) 

或者可以使用一個GADT

data MyRequest r where 
    GetImportantData :: Int -> MyRequest (Vector Float) 
    GetOtherImportantStuff :: Float -> MyRequest Int 
    ... 

和涉及一種存在,如在operational包的相應懸浮液類型。 (monad-coroutine只是提供一個免費的monad變壓器,並且操作提供了一種稍微不同的自由monad變壓器。Coroutine MySuspension m rProgramT MyRequest m r基本相同。)

+1

糟糕,monad-coroutine當然。 –