Ответ 1
Преобразование в бесстольный стиль может быть сделано полностью механически, хотя и трудно, без того, чтобы быть удобным с основами синтаксиса Haskell, такими как приложение с левой ассоциативной функцией, и x + y
совпадают с (+) x y
. Я предполагаю, что вам удобно синтаксис Haskell; Если нет, я предлагаю сначала сначала несколько глав LYAH.
Вам нужны следующие комбинаторы, которые находятся в стандартной библиотеке. Я также дал свои стандартные имена из расчета комбинатора.
id :: a -> a -- I
const :: a -> b -> a -- K
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- B
flip :: (a -> b -> c) -> (b -> a -> c) -- C
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S
Работайте с одним параметром за раз. Переместите параметры слева на лямбды справа, например.
f x y = Z
становится
f = \x -> \y -> Z
Мне нравится делать этот один аргумент за раз, а не сразу, он просто выглядит чище.
Затем устраните лямбду, которую вы только что создали, в соответствии со следующими правилами. Я буду использовать строчные буквы для буквенных переменных, заглавные буквы для обозначения более сложных выражений.
- Если у вас
\x -> x
, замените наid
- Если у вас
\x -> A
, гдеA
- любое выражение, в которомx
не встречается, замените наconst A
- Если у вас
\x -> A x
, гдеx
не встречается вA
, замените наA
. Это известно как "сокращение эта". - Если у вас
\x -> A B
, тогда- Если
x
встречается как вA
, так и вB
, замените на(\x -> A) <*> (\x -> B)
. - Если
x
встречается только вA
, замените наflip (\x -> A) B
- Если
x
происходит только вB
, замените наA . (\x -> B)
, - Если
x
не встречается ни вA
, ни вB
, ну, еще одно правило, которое мы должны были использовать уже.
- Если
И затем работайте внутри, устраняя лямбды, которые вы создали. Давайте работать с этим примером:
f x y z = foo z (bar x y)
-- Move parameter to lambda:
f x y = \z -> foo z (bar x y)
-- Remember that application is left-associative, so this is the same as
f x y = \z -> (foo z) (bar x y)
-- z appears on the left and not on the right, use flip
f x y = flip (\z -> foo z) (bar x y)
-- Use rule (3)
f x y = flip foo (bar x y)
-- Next parameter
f x = \y -> flip foo (bar x y)
-- Application is left-associative
f x = \y -> (flip foo) (bar x y)
-- y occurs on the right but not the left, use (.)
f x = flip foo . (\y -> bar x y)
-- Use rule 3
f x = flip foo . bar x
-- Next parameter
f = \x -> flip foo . bar x
-- We need to rewrite this operator into normal application style
f = \x -> (.) (flip foo) (bar x)
-- Application is left-associative
f = \x -> ((.) (flip foo)) (bar x)
-- x appears on the right but not the left, use (.)
f = ((.) (flip foo)) . (\x -> bar x)
-- use rule (3)
f = ((.) (flip foo)) . bar
-- Redundant parentheses
f = (.) (flip foo) . bar
Иди сюда, попробуй! На самом деле нет никакой уловки в определении того, какое правило использовать: используйте любое применимое правило, и вы достигнете прогресса.