Bil*_*ill 15 haskell types type-systems
我正在考虑如何使用Haskell的类型系统来强制执行程序中的模块化.举例来说,如果我有一个Web应用程序,我很好奇,如果有从纯代码分开CGI代码从文件系统代码的所有数据库代码的方式.
例如,我正在设想一个DB monad,所以我可以编写如下函数:
countOfUsers :: DB Int
countOfUsers = select "count(*) from users"
Run Code Online (Sandbox Code Playgroud)
我希望不可能使用DB monad支持的副作用.我描绘出了一个更高级别的单子,将被限制在直接URL处理器,将能够撰写的DB单子和IO单子电话.
这可能吗?这是明智的吗?
更新:我最终用Scala而不是Haskell实现了这个目标:http://moreindirection.blogspot.com/2011/08/implicit-environment-pattern.html
Don*_*art 13
我描绘出了一个更高级别的单子,将被限制在直接URL处理器,将能够撰写的DB单子和IO单子电话.
您当然可以实现这一点,并获得关于组件分离的非常强大的静态保证.
最简单的是,您需要一个受限制的IO monad.使用类似"污点"技术的东西,您可以创建一组IO操作,将其移植到一个简单的包装器中,然后使用模块系统隐藏类型的底层构造函数.
通过这种方式,您只能在CGI上下文中运行CGI代码,在DB上下文中运行DB代码.Hackage有很多例子.
另一种方法是为操作构造解释器,然后使用数据构造函数来描述您希望的每个基本操作.这些操作仍然应该构成一个monad,你可以使用do-notation,但你要建立一个描述要运行的动作的数据结构,然后通过解释器以受控的方式执行.
在典型情况下,这可能比您需要的内省更多内省,但该方法确实为您提供了在执行之前检查用户代码的全部功能.
我认为除了提到的两个唐斯图尔特之外还有第三种方式,甚至可能更简单:
class Monad m => MonadDB m where
someDBop1 :: String -> m ()
someDBop2 :: String -> m [String]
class Monad m => MonadCGI m where
someCGIop1 :: ...
someCGIop2 :: ...
functionWithOnlyDBEffects :: MonadDB m => Foo -> Bar -> m ()
functionWithOnlyDBEffects = ...
functionWithDBandCGIEffects :: (MonadDB m, MonadCGI m) => Baz -> Quux -> m ()
functionWithDBandCGIEffects = ...
instance MonadDB IO where
someDBop1 = ...
someDBop2 = ...
instance MonadCGI IO where
someCGIop1 = ...
someCGIop2 = ...
Run Code Online (Sandbox Code Playgroud)
这个想法非常简单,您可以为要分离的各种操作子集定义类型类,然后使用它们对函数进行参数化.即使你创建类的唯一具体monad是IO,在任何MonadDB上参数化的函数仍然只允许使用MonadDB操作(以及从它们构建的操作),因此您可以获得所需的结果.在IO monad中的"可以执行任何操作"功能中,您可以无缝地使用MonadDB和MonadCGI操作,因为IO是一个实例.
(当然,如果你愿意,你可以定义其他实例.通过各种monad变换器来提升操作将是直截了当的,我认为实际上并没有什么能阻止你为"包装器"和"解释器"monad Don Stewart编写实例提到,从而结合了这些方法 - 虽然我不确定你是否有理由这么做.)
| 归档时间: |
|
| 查看次数: |
711 次 |
| 最近记录: |