haskell - 匹配数据构造函数

标签 haskell pattern-matching

我试图以通用方式匹配数据构造函数,以便执行某种类型的任何任务。

data Task = TaskTypeA Int | TaskTypeB (Float,Float)

genericTasks :: StateLikeMonad s
genericTasks = do
   want (TaskTypeA 5)

   TaskTypeA #> \input -> do 
       want (TaskTypeB (1.2,4.3))
       runTaskTypeA input

   TaskTypeB #> \(x,y) -> runTaskTypeB x y

main = runTask genericTasks

在此,genericTasks 函数遍历 do 指令,构建一个由某种状态 monad 处理的 want 要做的事情的列表,以及一个列表通过 (#>) 函数来实现这一点。 runTask 函数将运行 genericTasks,使用生成的待办事项和操作方法列表,并进行计算。

但是,我在弄清楚如何从 (#>) 中提取“类型”(TaskTypeA,B) 方面遇到了一些麻烦,以便稍后可以调用它。如果执行 :t TaskTypeA,您将获得 Int -> Task

即,如何编写(#>)

我也不完全相信可以用如此通用的方式实现我在这里的想法。作为引用,我正在尝试构建类似于 Shake 库的东西,其中 (#>) 类似于 (*>)。然而,Shake 使用字符串作为 (*>) 的参数,因此匹配完全使用字符串匹配来完成。我想在不需要字符串的情况下完成它。

最佳答案

您的直觉是正确的,不可能按照您指定的方式编写 (#>) 。数据构造函数唯一一次充当模式是当它处于模式位置时,即作为函数的参数出现

f (TaskTypeA z) = ...

作为 case 语句的替代方案之一

case tt of
    TaskTypeA z -> ...

或者在单子(monad)或模式绑定(bind)中

do TaskTypeA z <- Just tt
   return z

当用在值位置时(例如作为函数的参数),它失去了模式性质并成为常规函数。不幸的是,这意味着您无法如此轻松地对模式进行抽象。

但是,有一个简单的模式形式化:

type Pattern d a = d -> Maybe a

制作它们需要一点工作。

taskTypeA :: Pattern Task Int
taskTypeA (TaskTypeA z) = Just z
taskTypeA _ = Nothing

如果您还需要使用构造函数“forwards”(即 a -> d),那么您可以将两者配对在一起(加上一些与之配合使用的函数):

data Constructor d a = Constructor (a -> d) (d -> Maybe a)

apply :: Constructor d a -> a -> d
apply (Constructor f _) = f

match :: Constructor d a -> d -> Maybe a
match (Constructor _ m) = m

taskTypeA :: Constructor Task Int
taskTypeA = Constructor TaskTypeA $ \case TaskTypeA z -> Just z
                                          _ -> Nothing

这被称为“棱镜”,并且(一种非常通用的形式)它在 lens 中实现。 .

使用这样的抽象有很多优点——即,您可以构造比数据类型允许的结构更多的棱柱(例如,d可以是函数类型),并且您可以编写对构造函数进行操作的函数,通常将更简单的函数组合成更复杂的函数。

但是,如果您使用普通数据类型,则必须为每个构造函数实现 Constructor 对象(就像我上面为 TaskTypeA 所做的那样)是很痛苦的。如果您有很多要使用的,您可以使用 Template Haskell为您编写样板。必要的模板 Haskell 例程是 already implemented在镜头中——因此学习如何使用镜头库可能是值得的。 (但导航可能有点令人畏惧)

(风格说明:上面的第二个构造函数及其两个辅助函数可以使用一个小技巧等效地编写:

data Constructor d a = Constructor { apply :: a -> d, match :: d -> Maybe a }

)

有了这个抽象,现在就可以编写(#>)了。一个简单的例子是

(#>) :: Constructor d a -> (a -> State d ()) -> State d ()
cons #> f = do
    d <- get
    case match cons d of
        Nothing -> return ()
        Just a  -> f a

或者可能是更复杂的东西,具体取决于您想要什么。

关于haskell - 匹配数据构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17747927/

相关文章:

postgresql - Haskell 堆栈构建错误 : While building package postgresql-libpq-0. 9.4.3

haskell - 定义应用实例的问题

pattern-matching - jmeter中的管道符号模式匹配

haskell - 使函数成为向量类型类的实例

haskell - 与 Haskell 类型推断的混淆

haskell - 读者单子(monad)的目的是什么?

sed 在多行模式之前插入多行

scala - 从相同特征派生的案例类的模式匹配

pattern-matching - 如何匹配具有多个字段的选项?

pattern-matching - Elixir:使用模式匹配捕获 map 的其余部分