改善Coroutine请求类型安全性

时间:2016-02-13 11:31:24

标签: haskell coroutine type-safety

使用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仅{}返回ImportantDataOtherImportantStuff我只获得ImportantStuff等等?< / p>

2 个答案:

答案 0 :(得分:3)

而不是使用monad-coroutine提供的

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 :(得分:2)

幻影类型和GADT可以帮助您在这里实现更多的类型安全。

{u'this': 2, u'test': 1, u'is': 0}
{u'this': 3, u'test': 2, u'is': 0, u'not': 1}

{-# 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 a是幻像类型,因为类型参数Response a与基础值无关(例如a中的Int)。幻影类型广泛用于确保类型安全。

语言扩展GetImportantData允许构造函数的显式类型声明,可以很容易地区分数据类型的构造函数。

而不是

GADTs

其中data Foo = Bar | Qux Bar都有Qux类型,Foo可以定义

GADTs

这样做data Foo a where Bar :: Foo Int Qux :: Foo Float Bar有不同的类型。

在WikiBooks和Haskell wiki上有一些关于这个主题的精彩教程。