使用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
等等?< / p>
答案 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 r
与ProgramT 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上有一些关于这个主题的精彩教程。