При преобразовании операторных секций,
between min max x = (min < x) && (x < max)
= ((&&) . (min <)) x ((< max) x)
Теперь Этот соответствует шаблону для S -комбинатора, <b>S</b> f g x = (f x) (g x)
.Есть много способов его кодирования в Haskell, но два основных - через Applicative и Arrows:
_S f g x = (f x) (g x)
= (f <*> g) x
= uncurry id . (f &&& g) $ x
Второй дает нам
between a z = uncurry (&&) . ((a <) &&& (< z))
И первый, еще более подходящий
between a z = (&&) <$> (a <) <*> (< z)
= liftA2 (&&) (a <) (< z)
= (a <) <^(&&)^> (< z) -- nice and visual
(<^) = flip (<$>)
(^>) = (<*>)
Но мы могли бы также поиграть с другими комбинаторами, но с гораздо менее удовлетворительными результатами:
_S f g x = f x (g x)
= flip f (g x) x
= (flip f . g) x x
= join (flip f <$> g) x
= (flip f =<< g) x
или
= (f x . g) x
= (. g) (f x) x
= ((. g) =<< f) x
, что хорошо иллюстрирует опасностиБессмысленность в погоне за бессмысленным.
Есть еще одна возможность, которая имеет смысл (синтаксически):
_S f g x = (f x) (g x)
-- = foldr1 ($) . sequence [f,g] $ x -- not valid Haskell
-- sequence [f,g] x = [f x,g x]
Это вообще не допустимый Haskell из-за проблем с типизацией, но в нашем конкретном случае это даетперейдем к еще одному правильному определению, которое, похоже, также хорошо следует его внутренней логике:
between a z = -- foldr1 ($) . sequence [(&&).(a <), (< z)] -- not OK
= foldr1 (&&) . sequence [(a <), (< z)] -- OK
= and . sequence [(a <), (> z)]
, потому что (a <)
и (> z)
имеют одинаковый тип.