Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Haskell';s类型系统以强制模块化_Haskell_Types_Type Systems - Fatal编程技术网

使用Haskell';s类型系统以强制模块化

使用Haskell';s类型系统以强制模块化,haskell,types,type-systems,Haskell,Types,Type Systems,我正在考虑如何使用Haskell的类型系统在程序中强制模块化。例如,如果我有一个web应用程序,我很好奇是否有办法将所有数据库代码从CGI代码中分离出来,将文件系统代码从纯代码中分离出来 例如,我设想了一个DB monad,因此我可以编写如下函数: countOfUsers :: DB Int countOfUsers = select "count(*) from users" 我想它是不可能使用的副作用以外的DB单子支持。我正在描绘一个更高级别的monad,它将仅限于直接的URL处理程序,

我正在考虑如何使用Haskell的类型系统在程序中强制模块化。例如,如果我有一个web应用程序,我很好奇是否有办法将所有数据库代码从CGI代码中分离出来,将文件系统代码从纯代码中分离出来

例如,我设想了一个DB monad,因此我可以编写如下函数:

countOfUsers :: DB Int
countOfUsers = select "count(*) from users"
我想它是不可能使用的副作用以外的DB单子支持。我正在描绘一个更高级别的monad,它将仅限于直接的URL处理程序,并且能够编写对DB monad和IO monad的调用

这可能吗?这明智吗


更新:我最终用Scala而不是Haskell实现了这一点:

谢谢你的提问

我在客户机/服务器web框架上做了一些工作,该框架使用monads来区分不同的执行环境。最明显的是客户端和服务器端,但它也允许您编写客户端代码(可以在客户端和服务器上运行,因为它不包含任何特殊功能)和异步客户端代码,用于在客户端上编写非阻塞代码(基本上是客户端上的延续monad)。这听起来与您区分CGI代码和DB代码的想法非常相关

以下是有关我的项目的一些资源:

  • 从我做的关于这个项目的报告中
  • 我和Don Syme一起写的
  • 我也写了关于这个主题的文章(虽然很长)
我认为这是一种有趣的方法,它可以为您提供有关代码的有趣保证。不过,还有一些棘手的问题。如果您有一个服务器端函数,它接受
int
并返回
int
,那么该函数的类型应该是什么?在我的项目中,我使用了
int->intserver
(但也可以使用
server(int->int)

如果您有两个这样的函数,那么编写它们就不那么简单了。您需要编写以下代码,而不是编写
goo(foo(bar 1))

do b <- bar 1
   f <- foo b
   return goo f
dob
我正在描绘一个更高级别的monad,它将仅限于直接的URL处理程序,并且能够编写对DB monad和IO monad的调用

您当然可以实现这一点,并获得关于组件分离的非常强大的静态保证

在最简单的情况下,您需要一个受限的IO monad。使用类似于“污染”的技术,您可以创建一组提升到简单包装器中的IO操作,然后使用模块系统隐藏类型的底层构造函数

这样,您只能在CGI上下文中运行CGI代码,而在DB上下文中运行DB代码

另一种方法是为操作构造一个解释器,然后使用数据构造函数来描述您希望的每个基本操作。这些操作仍然应该形成一个monad,您可以使用do表示法,但您将构建一个描述要运行的操作的数据结构,然后通过int以受控方式执行这些操作埃普雷特


这可能比在典型情况下需要更多的内省,但这种方法确实能让您在执行用户代码之前充分检查用户代码。

我认为除了Stewart提到的两种方法之外,还有第三种方法,可能更简单:

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 = ...
其思想非常简单,您可以为要分离出来的各种操作子集定义类型类,然后使用它们对函数进行参数化。即使您为这些类创建实例的唯一具体monad是IO,在任何MonadDB上参数化的函数仍然只允许使用MonadDB操作在IO monad中的“可以做任何事情”功能中,可以无缝地使用MonadDB和MonadCGI操作,因为IO是一个实例


(当然,如果您愿意,您可以定义其他实例。通过各种monad转换器提升操作将非常简单,而且我认为实际上没有什么可以阻止您为“包装器”和“解释器”编写实例monads没有Stewart提到,因此结合了这些方法-虽然我不确定你是否有这样做的理由。)

import Control.Monad;(是的,在F中情况更糟,你还想编写方法调用,比如
o.Bar().Foo().goo()
使用组合器是无法做到这一点的。使用
谢谢,唐!前一种解决方案听起来像是我正在寻找的。你知道有没有使用这种技术的特定软件包,或者谷歌的好术语(“受限IO monad”没有出现多少)?这是“污染monad”概念的一个很好的例子,谢谢。如果我选择使用“受污染的monad”模式对于我的DB monad,如何从DB monad中提取数据?我的HTTP操作处理程序是否必须使用包含DB的monad转换器?大多数monad都带有“run”命令。通常的模式是monad类型Foo具有“runFoo::Foo a->a”另一方面,您可能希望限制输出的目的地,因此您可能需要“runFoo::Foo a->Bar a”也可以查看“Monads a la carte”。(我想这就是标题…)