2

我正在尝试以通用方式匹配数据构造函数,以便执行特定类型的任何任务。

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-instructions,通过该函数构建一个want由某种状态 monad 处理的要执行的操作列表,以及执行此操作的方法列表(#>)。该runTask函数将运行 genericTasks,使用生成的待办事项和如何做的列表,并进行计算。

但是,我在弄清楚如何TaskTypeA,B从 (#>) 中提取“类型”( ) 以便以后可以调用它时遇到了一些麻烦。如果你做一个:t TaskTypeA,你会得到一个Int -> Task

即,如何写(#>)

我也不完全相信有可能以如此通用的方式来做我在想的事情。作为参考,我正在尝试构建类似于Shake库的东西,其中(#>)类似于(*>). 但是 Shake 使用 String 作为 的参数(*>),因此匹配完全使用 String 匹配完成。我想在不需要字符串的情况下做到这一点。

4

1 回答 1

2

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

f (TaskTypeA z) = ...

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

case tt of
    TaskTypeA z -> ...

或在单子或模式绑定中

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为你编写你的样板。必要的 Template Haskell 例程已经在 lens 中实现了——因此学习如何使用 lens 库可能是值得的。(但导航可能有点令人生畏)

(风格说明:Constructor上面的第二个和它的两个辅助函数可以用一个小技巧等效地编写:

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

或者更复杂的东西,这取决于你想要什么。

于 2013-07-19T20:11:24.930 回答