我试图以通用方式匹配数据构造函数,以便执行某种类型的任何任务。
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/