Если вы не хотите взламывать классы типов, которые лучше оставить для мысленных экспериментов и подтверждения концепции, вы просто не обобщаете на множественные аргументы. Не пытайся.
Что касается вашего основного вопроса, он наиболее элегантно решен с помощью комбинатора семантического редактора Conal Elliott . Комбинатор семантического редактора - это функция с типом:
(a -> b) -> F(a) -> F(b)
Где F(x)
- это некоторое выражение, включающее x
. Существуют также «контравариантные» комбинаторы редактора, которые вместо этого принимают (b -> a)
. Интуитивно понятно, что комбинатор редактора выбирает часть некоторого большего значения для работы. Тот, который вам нужен, называется result
:
result = (.)
Посмотрите на тип выражения, с которым вы пытаетесь оперировать:
a -> a -> Bool
Результат (codomain) этого типа равен a -> Bool
, а результат , который типа равен Bool
, и это то, к чему вы пытаетесь применить not
. Таким образом, чтобы применить not
к результату результата функции f
, вы пишете:
(result.result) not f
Это прекрасно обобщает. Вот еще несколько комбинаторов:
argument = flip (.) -- contravariant
first f (a,b) = (f a, b)
second f (a,b) = (a, f b)
left f (Left x) = Left (f x)
left f (Right x) = Right x
...
Итак, если у вас есть значение x
типа:
Int -> Either (String -> (Int, Bool)) [Int]
И вы хотите применить not
к Bool, вы просто прописываете путь, чтобы туда добраться:
(result.left.result.second) not x
О, и если вы еще дошли до Functors, вы заметите, что fmap
является комбинатором редактора. На самом деле вышеперечисленное можно записать так:
(fmap.left.fmap.fmap) not x
Но я думаю, что проще использовать расширенные имена.
Наслаждайтесь.