Рефакторинг функций высшего порядка в Haskell, чтобы избежать передачи операторов через несколько функций - PullRequest
0 голосов
/ 11 марта 2019

Я пытаюсь реорганизовать этот бит кода, чтобы избежать необходимости передавать одни и те же операторы через несколько функций.

Программа, которую я пишу, использует несколько операторов для передачи на верхнем уровне, чтобы повторно использовать код на нижних уровнях.Упрощенный пример шаблона, который я использую:

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' и higherOrder, someLowerFunction и someEvenLowerFunction просто для дедупликации кода, который является общим для двух открытых функций.

Есть ли какой-нибудь способ сохранить преимущества этого кода (а именно, что higherOrder, someLowerFunction и someEvenLowerFunction можно использовать повторно), не пропуская operator снова и снова?Я пытался проявить творческий подход к алгебраическим типам данных, чтобы добиться этого, но пока не получил хороших результатов.

Ответы [ 2 ]

1 голос
/ 11 марта 2019

Я не совсем понимаю ваш вопрос, но, полагаю, вы хотите что-то похожее на это.

Определите тип для операторов, дав имя тем, которые вы хотите.

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)

Таким образом, синтаксис и семантика бинарных операторов полностью исключаются из определения выражений и их семантики.

0 голосов
/ 24 марта 2019

Оглядываясь назад на этот вопрос, по сути, речь идет о перемещении поведения ближе к месту возникновения различий. Вдохновленный ответом @ 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

Примечательно, что я все еще передаю оператор вниз, но в случае, когда необходимо передать несколько операторов, все они могут быть заменены только алгебраическим типом данных, который позволяет это позднее сопоставление.

В качестве более общего комментария, наличие этого переплетенного кода - немного запах. Тот факт, что существует так много общего, за исключением небольшого различия позже, показывает, что должно быть какое-то более высокое обобщение, которое можно использовать для полного решения проблемы.

...