在Haskell中如何将构造函数作为参数传递并匹配它

Sku*_*uge 3 constructor haskell

我有这个代码

data Container = Box Int | Bag Int


inBox :: [Container] -> Int
inBox [] = 0
inBox (x:ls) | (Box i) <- x = i + inBox ls
             | otherwise = inBox ls


inBag :: [Container] -> Int
inBag [] = 0
inBag (x:ls) | (Bag i) <- x = i + inBag ls
             | otherwise = inBag ls
Run Code Online (Sandbox Code Playgroud)

显然InBox,InBag具有相同的结构.我想创建一个包含它们的功能.我不知道如何获取是将构造函数(Box或者Bag)作为参数传递.

理想情况下,一般功能看起来像这样:

inSome :: Constructor -> [Container] -> Int
inSome con [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome con ls
                  | otherwise = inSome con ls
Run Code Online (Sandbox Code Playgroud)

显然这不起作用,因为构造函数不是这里定义的类型.我该怎么做?

一个想法是将它作为一个函数传递:

inSome :: (Int -> Container) -> [Container] -> Int
inSome _ [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome ls
                  | otherwise = inSome ls
Run Code Online (Sandbox Code Playgroud)

但后来我得到了错误:

模式中的解析错误:con

因为它无法匹配这样的功能.

我想这样做的原因是因为我有一个包含二进制操作的复杂数据类型(例如+,#,::等等).我有几个函数对于这些构造函数几乎相同.我不想写所有这些并一起修改它们.我必须有办法在函数中完成它.也许有人可以在评论中提出另一种方法?

J. *_*son 8

您可以完全避免使用模式匹配.

data Container = Box Int | Bag Int

unBox, unBag :: Container -> Maybe Int

unBox (Box i) = Just i
unBox _       = Nothing

unBag (Bag i) = Just i
unBag _       = Nothing
Run Code Online (Sandbox Code Playgroud)

这些函数的类型捕获了Int在打开结构时获取包含的需要Container.然后可以使用它来构建您想要的功能.

inSome :: (Container -> Maybe Int) -> [Container] -> Int
inSome get []     = 0
inSome get (x:ls) = fromMaybe 0 (get x) + inSome ls

inBag = inSome unBag
inBox = inSome unBox
Run Code Online (Sandbox Code Playgroud)

正如leftroundabout所指出的那样,"获得或失败"的模式是(大规模地)在概念中概括Lens,或者在这种情况下,概括Prism.一般来说,Prisms可以形成一种弱的一流模式,但它们在这里的使用肯定会有点过分.

  • 你可以传递一个构造函数 - 它们只是函数 - 你只是不能参数化模式匹配,它们必须是具体的.也就是说,这些限制将在GHC 7.10中取消. (2认同)

Dan*_*ner 5

您可能喜欢first-class-patterns,这是一个可以传递和修改模式的软件包。