2016-02-13 92 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当然。 –