在OO世界中,我有一个类(我们称之为"Suggestor"),它实现接近"策略模式"的东西,以在运行时提供不同的算法实现.作为学习Haskell的练习,我想重写一下.
实际的用例非常复杂,所以我将简单介绍一个简单的例子.
假设我有一个Suggester列出规则列表的类,并将每个规则作为过滤器应用于数据库结果列表.
每个规则都有三个阶段"构建查询","后查询过滤器"和"记分器".我们最终会得到满足以下要求的界面
buildQuery :: Query -> Query
postQueryFilter :: [Record] -> [Record]
scorer :: [Record] -> [(Record, Int)]
Run Code Online (Sandbox Code Playgroud)
Suggestor需要获取与此接口匹配的规则列表 - 在运行时动态 - 然后按顺序执行它们.必须首先在所有规则中运行buildQuery(),然后是postQueryFilter,然后是scorer.(即我不能只将一个规则的函数组合成一个函数).
在斯卡拉我只是做
// No state, so a singleton `object` instead of a class is ok
object Rule1 extends Rule {
def buildQuery ...
def postQueryFilter ...
def scorer ...
}
object Rule2 extends Rule { .... }
Run Code Online (Sandbox Code Playgroud)
然后可以通过传递相关规则来初始化服务(在运行时根据用户输入定义).
val suggester = new Suggester( List(Rule1, Rule2, Rule3) );
Run Code Online (Sandbox Code Playgroud)
如果规则是单个函数,那么这很简单 - 只需传递一个函数列表.但是,由于每个规则实际上是三个函数,我需要以某种方式将它们组合在一起,所以我有多个实现会议接口.
我的第一个想法是类型类,但是这些似乎不能满足我的需求 - 他们期望一个类型变量,并强制我的每个方法必须使用它 - …
(相关问题在运行时选择实例行为)
我想定义一种后端(可重用为独立api),然后提供多个实现并能够在运行时选择一个.
我的申请就像
data AppData =
AppData { ...
, backend :: Backend
}
Run Code Online (Sandbox Code Playgroud)
为简单起见,让我们的后端(概念代码)
data Backend key value =
Backend { get :: [(key, value)]
, cud :: key -> Maybe value -> () -- Create Update Delete
}
Run Code Online (Sandbox Code Playgroud)
现在定义我们Backend类型的正确/推荐方法是什么?
我认为这将是一元(当时Monad m),而且IO(当时MonadIO m),而且纯(那么我们需要改变-> ()的-> Backend key value)等等...
我怀疑,下一次尝试是monadic,IO纯粹,但可能是过度工程
data Backend m k v =
Backend { get :: MonadIO m …Run Code Online (Sandbox Code Playgroud)