Ну, я посмотрел Hoogle на подпись типа, такую как в Томас Юнг ответ , и есть on
. Это то, что я искал:
(a -> b) -> (b -> b -> Bool) -> a -> a -> a
Где (a -> b)
соответствует foo
, (b -> b -> Bool)
соответствует <
. К сожалению, подпись для on
возвращает что-то еще:
(b -> b -> c) -> (a -> b) -> a -> a -> c
Это почти то же самое, если заменить c
на Bool
и a
в двух местах, которые появляются соответственно.
Итак, сейчас я подозреваю, что его не существует. Мне пришло в голову, что есть более общая сигнатура типа, поэтому я тоже попробовал:
(a -> b) -> ([b] -> b) -> [a] -> a
Этот ничего не дал.
EDIT:
Теперь я не думаю, что я был так далеко вообще. Рассмотрим, например, это:
Data.List.maximumBy (on compare length) ["abcd", "ab", "abc"]
Функция maximumBy
сигнатура (a -> a -> Ordering) -> [a] -> a
, которая в сочетании с on
довольно близка к той, что вы указали изначально, учитывая, что Ordering
имеет три значения - почти логическое значение! : -)
Итак, скажем, вы написали on
в Scala:
def on[A, B, C](f: ((B, B) => C), g: A => B): (A, A) => C = (a: A, b: A) => f(g(a), g(b))
Вы можете написать select
так:
def select[A](p: (A, A) => Boolean)(a: A, b: A) = if (p(a, b)) a else b
И используйте это так:
select(on((_: Int) < (_: Int), (_: String).length))("a", "ab")
Что действительно лучше работает с каррингом и без точек. :-) Но давайте попробуем это с последствиями:
implicit def toFor[A, B](g: A => B) = new {
def For[C](f: (B, B) => C) = (a1: A, a2: A) => f(g(a1), g(a2))
}
implicit def toSelect[A](t: (A, A)) = new {
def select(p: (A, A) => Boolean) = t match {
case (a, b) => if (p(a, b)) a else b
}
}
Тогда вы можете написать
("a", "ab") select (((_: String).length) For (_ < _))
Очень близко. Я не нашел никакого способа удалить оттуда спецификатор типа, хотя подозреваю, что это возможно. Я имею в виду, не идя путем Томаса ответ. Но, может быть, это это путь. На самом деле, я думаю, что on (_.length) select (_ < _)
читается лучше, чем map (_.length) select (_ < _)
.