Подтвердить что ты не робот

Как составить `не` с функцией произвольной arity?

Когда у меня есть функция типа типа

f :: (Ord a) => a -> a -> Bool
f a b = a > b

Мне бы хотелось сделать функцию, которая не переносит эту функцию.

например. выполните функцию, подобную этой

g :: (Ord a) => a -> a -> Bool
g a b = not $ f a b

Я могу сделать комбинатор вроде

n f = (\a -> \b -> not $ f a b)

Но я не знаю, как.

*Main> let n f = (\a -> \b -> not $ f a b)
n :: (t -> t1 -> Bool) -> t -> t1 -> Bool
Main> :t n f
n f :: (Ord t) => t -> t -> Bool
*Main> let g = n f
g :: () -> () -> Bool

Что я делаю неправильно?

И бонусный вопрос, как я могу это сделать для функции с большим количеством параметров и, кроме того,

t -> Bool
t -> t1 -> Bool
t -> t1 -> t2 -> Bool
t -> t1 -> t2 -> t3 -> Bool
4b9b3361

Ответ 1

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

Что касается вашего основного вопроса, это наиболее элегантно решается с помощью комбинаторов семантического редактора Conal Elliott. Комбинатор семантического редактора - это функция с типом типа:

(a -> b) -> F(a) -> F(b)

Где F (x) - некоторое выражение, включающее x. Существуют также "контравариантные" комбинаторы редакторов, которые вместо этого принимают (b -> a). Интуитивно, комбинатор редакторов выбирает часть некоторого большего значения для работы. Тот, который вам нужен, называется result:

result = (.)

Посмотрите на тип выражения, которое вы пытаетесь использовать:

a -> a -> Bool

Результат (codomain) этого типа является a → Bool, а результатом этого типа является Bool, и это то, что вы пытаетесь применить not to. Поэтому, чтобы применить 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

Но я думаю, что яснее использовать расширенные имена.

Enjoy.

Ответ 2

На самом деле выполнение произвольной сущности с типами классов оказывается невероятно легким:

module Pred where

class Predicate a where
  complement :: a -> a

instance Predicate Bool where
  complement = not

instance (Predicate b) => Predicate (a -> b) where
  complement f = \a -> complement (f a)  
  -- if you want to be mysterious, then
  -- complement = (complement .)
  -- also works

ge :: Ord a => a -> a -> Bool
ge = complement (<)

Спасибо, что указали эту крутую проблему. Я люблю Хаскелла.

Ответ 3

Ваш n комбинатор можно записать:

n = ((not .) .)

Что касается вашего бонусного вопроса, типичным способом было бы создать несколько из них:

lift2 = (.).(.)
lift3 = (.).(.).(.)
lift4 = (.).(.).(.).(.)
lift5 = (.).(.).(.).(.).(.)

и др.

Ответ 4

Re: Что я делаю неправильно?:

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

Prelude> :ty (n f)
(n f) :: (Ord t) => t -> t -> Bool
Prelude> let g = n f
Prelude> :ty g
g :: () -> () -> Bool

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

module X where

n f = (\a -> \b -> not $ f a b)
f a b = a > b

g :: Ord a => a -> a -> Bool
g = n f

Бонусный вопрос. Чтобы сделать это с большим количеством параметров типа, вы можете попробовать сыграть хитроумные трюки с системой типа класса. Две статьи для справки: Hughes and Claessen бумага в QuickCheck и бумага Ральфа Хинзе Общие для масс.