重构Haskell中的高阶函数,以避免通过多个函数传递运算符



我正在尝试重构这段代码,以避免必须通过多个函数传递相同的运算符。

我正在编写的程序依赖于在顶层传入的几个运算符,以便在较低层重用代码。我正在使用的模式的一个简化示例是:

add' = higherOrder (+)
subtract' = higherOrder (-)
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction a b operator = operator a b

其中要调用的有用函数是add'subtract'higherOrdersomeLowerFunctionsomeEvenLowerFunction只是为了删除两个公开函数之间共有的代码。

有没有办法保持这段代码的好处(即,higherOrdersomeLowerFunctionsomeEvenLowerFunction可以重用),而不必一遍又一遍地传递operator?我试图创造性地使用代数数据类型来实现这一目标,但到目前为止还没有取得好的结果。

我不完全理解你的问题,但我想你想要类似的东西。

为运算符定义类型,为所需的运算符命名。

data Operator = Add | Sub

然后,您可以定义它们的语义:

semOp :: Operator -> (Integer -> Integer -> Integer)
semOp Add = (+)
semOp Sub = (-)

然后,定义一个表达式的类型:

data Expr = Constant Integer | BinOp Expr Operator Expr

和表达式的语义:

semExpr :: Expr -> Integer
semExpr (Constant a) = a
semExpr (BinOp e1 op e2) = semOp op (semExpr e1) (semExpr e2)

这样,二元运算符的语法和语义就完全脱离了表达式的定义及其语义。

回顾这个问题,它本质上是一个关于将行为向下移动到更接近差异发生的地方的问题。从@chi上面的回答中汲取了很大的灵感(谢谢你@chi!),我得出了这个最小的变化,以获得预期的效果:

data Operator = Add | Sub
add' = higherOrder Add
subtract' = higherOrder Sub
higherOrder :: Operator -> Int -> Int -> Int -> Int -> Int
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction :: Operator -> Int -> Int -> Int -> Int
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction :: Int -> Int -> Operator -> Int
someEvenLowerFunction a b Add = a + b
someEvenLowerFunction a b Sub = a - b

值得注意的是,我仍在传递运算符,但是在需要传递多个运算符的情况下,它们都可以替换为允许以后匹配的代数数据类型。

作为更一般的评论,将代码交织在一起有点臭。事实上,除了后来的一个小差异之外,有这么多的共享表明应该有一些更高的概括可以用来完全解决问题。

最新更新