У навчальній проблемі, з якою я заплутався, я зрозумів, що мені потрібен типовий клас для функцій із застосуванням, складанням тощо. Причини ...
Це може бути зручно ставитись до подання функції, як до самої функції, так що застосування функції неявно використовує інтерпретатор, а складання функцій отримує новий опис.
Після того, як у вас буде тип класу для функцій, ви можете отримати похідні класи для спеціальних функцій - у моєму випадку я хочу перевернутих функцій.
Наприклад, функції, які застосовують цілісні зсуви, можуть бути представлені ADT, що містить ціле число. Застосування цих функцій означає лише додавання цілого числа. Склад реалізується шляхом додавання загорнутих цілих чисел. Зворотна функція має ціле число заперечене. Функція ідентичності завершує нуль. Постійну функцію неможливо надати, оскільки для неї немає відповідного представлення.
Звичайно, не потрібно писати речі так, як ніби це значення були справжніми функціями Haskell, але як тільки я виникла ідея, я подумав, що така бібліотека повинна вже існувати і, можливо, навіть використовуючи стандартні написання. Але я не можу знайти такий тип класу в бібліотеці Haskell.
Я знайшов модуль Data.Function , але немає класу типу - лише деякі загальні функції, які також доступні в Prelude.
Отже - чому не існує типового класу для функцій? Це "просто тому, що немає" чи "тому, що це не так корисно, як ви думаєте"? Чи, можливо, є принципова проблема з ідеєю?
Найбільша можлива проблема, про яку я думав до цього часу, полягає в тому, що компілятор, ймовірно, повинен застосувати спеціальне застосування програми для фактичних функцій, щоб уникнути циклічної проблеми - для застосування цієї функції мені потрібно застосувати функцію додатка функції, і для цього мені потрібно викликати функцію додатка функції, і для цього ...
Більше підказок
Приклад коду, щоб показати, на що я прагну ...
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
-- In my first version, Doable only had the one argument f. This version
-- seemed to be needed to support the UndoableOffset type.
--
-- It seems to work, but it also seems strange. In particular,
-- the composition function - a and b are in the class, but c isn't,
-- yet there's nothing special about c compared with a and b.
class Doable f a b where
fwdApply :: f a b -> a -> b
compDoable :: f b c -> f a b -> f a c
-- In the first version, I only needed a constraint for
-- Doable f a b, but either version makes sense.
class (Doable f a b, Doable f b a) => Undoable f a b where
bwd :: f a b -> f b a
bwdApply :: f a b -> b -> a
bwdApply f b = fwdApply (bwd f) b
-- Original ADT - just making sure I could wrap a pair of functions
-- and there were no really daft mistakes.
data UndoableFn a b = UFN { getFwd :: a -> b, getBwd :: b -> a }
instance Doable UndoableFn a b where
fwdApply = getFwd
compDoable f g = UFN ((getFwd f) . (getFwd g)) ((getBwd g) . (getBwd f))
instance Undoable UndoableFn a b where
bwd f = UFN (getBwd f) (getFwd f)
bwdApply = getBwd
-- Making this one work led to all the extensions. This representation
-- can only represent certain functions. I seem to need the typeclass
-- arguments, but also to need to restrict which cases can happen, hence
-- the GADT. A GADT with only one constructor still seems odd. Perhaps
-- surprisingly, this type isn't just a toy (except that the whole thing's
-- a toy really) - it's one real case I need for the exercise. Still a
-- simple special case though.
data UndoableOffset a b where
UOFF :: Int -> UndoableOffset Int Int
instance Doable UndoableOffset Int Int where
fwdApply (UOFF x) y = y+x
compDoable (UOFF x) (UOFF y) = UOFF (x+y)
instance Undoable UndoableOffset Int Int where
bwdApply (UOFF x) y = y-x
bwd (UOFF x) = UOFF (-x)
-- Some value-constructing functions
-- (-x) isn't shorthand for subtraction - whoops.
undoableAdd :: Int -> UndoableFn Int Int
undoableAdd x = UFN (+x) (\y -> y-x)
undoableMul :: Int -> UndoableFn Int Int
undoableMul x = UFN (*x) (`div` x)
-- With UndoableFn, it's possible to define an invertible function
-- that isn't invertible - to break the laws. To prevent that, need
-- the UFN constructor to be private (and all public ops to preserve
-- the laws). undoableMul is already not always invertible.
validate :: Undoable f a b => Eq a => f a b -> a -> Bool
validate f x = (bwdApply f (fwdApply f x)) == x
-- Validating a multiply-by-zero invertible function shows the flaw
-- in the validate-function plan. Must try harder.
main = do putStrLn . show $ validate (undoableAdd 3) 5
putStrLn . show $ validate (undoableMul 3) 5
--putStrLn . show $ validate (undoableMul 0) 5
fb1 <- return $ UOFF 5
fb2 <- return $ UOFF 7
fb3 <- return $ compDoable fb1 fb2
putStrLn $ "fwdApply fb1 3 = " ++ (show $ fwdApply fb1 3)
putStrLn $ "bwdApply fb1 8 = " ++ (show $ bwdApply fb1 8)
putStrLn $ "fwdApply fb3 2 = " ++ (show $ fwdApply fb3 2)
putStrLn $ "bwdApply fb3 14 = " ++ (show $ bwdApply fb3 14)
Додаток передбачає певну уніфікацію, де уніфіковані значення не рівні, але пов'язані через ті неперевернуті функції - логіку стилю Prolog, але з a = f(b)
обмеженнями, а не a = b
. Більшість композицій буде результатом оптимізації структури пошуку об'єднання. Потреба в інверсах повинна бути очевидною.
Якщо жоден елемент в уніфікованому наборі не має точного значення, то певний предмет можна кількісно оцінити лише щодо іншого предмета в цьому уніфікованому наборі. Ось чому я не хочу використовувати "реальні" функції - обчислення цих відносних значень. Я міг би скинути весь аспект функції і просто мати абсолютні та відносні величини - мені, мабуть, потрібні лише числа / вектори і (+)
- але мій внутрішній космонавт-астронавт хоче його розваги.
Єдиний спосіб, коли я знову розриваю посилання, - це зворотний трекінг, і все чисто - пошук об'єднання буде зроблено за допомогою клавіш в якості IntMap
"покажчиків". У мене є просте з'єднання, яке працює, але оскільки я ще не додав обертові функції, тут немає сенсу перераховувати його.
Причини я не можу використовувати додатки, монаду, стрілку тощо
Основні операції, які мені потрібні для класу абстракції функцій, - це застосування та склад. Це звучить знайомо - наприклад Applicative
(<*>)
, Monad
(>>=)
і Arrow
(>>>)
всі функції композиції. Однак типи, які реалізують абстракцію функції в моєму випадку, будуть містити деяку структуру даних, яка представляє функцію, але яка не є (і не може містити) функцію і яка може представляти лише деякий обмежений набір функцій.
Як я вже згадував у поясненні коду, іноді я можу кількісно оцінити лише один елемент відносно іншого, оскільки жоден елемент у "об'єднаному" кластері не має точного значення. Я хочу мати змогу отримати уявлення про цю функцію, яка, як правило, буде складом декількох наданих функцій (підхід до спільного предка в союзі / пошуку дерева) та декількох зворотних функцій (повернення назад до іншої пункт).
Простий випадок - коли вихідні "функції" обмежуються цілочисловим зміщенням "функцій", я хочу, щоб складений результат був "цілочисельним зміщенням" функції "- додати компонентні компенсації. Це велика частина того, чому функція композиції повинна бути в класі, а також у функції застосування.
Це означає , що я не можу забезпечити операції pure
, return
або arr
для моїх типів, тому я не можу використовувати Applicative
, Monad
або Arrow
.
Це не провал цих типів - це невідповідність абстракцій. Я хочу отримати абстракцію простої чистої функції. Наприклад, немає побічних ефектів, і немає необхідності будувати зручні позначення для послідовності та складання функцій, відмінних від еквівалента стандарту (.), Який стосується всіх функцій.
Я міг би екземпляр Category
. Я впевнений, що всі мої функціональні речі зможуть надати ідентичність, хоча мені, мабуть, це і не потрібно. Але оскільки Category
не підтримує додаток, мені все одно потрібен похідний клас все одно, щоб додати цю операцію.
Applicative
це цілком правильно - він вимагає, щоб значення були обернені так само, як і функції, тоді як я хочу лише обернути функції, а загорнуті функції справді є функціями, тоді як моїх обернутих функцій зазвичай не буде (у найзагальніший випадок, це AST, що описують функції). Там, де <*>
є тип f (a -> b) -> f a -> f b
, я хочу, щоб оператор додатків з типом g a b -> a -> b
де a
і b
вказав домен та кодоміну обгорнутої функції, але те, що знаходиться всередині обгортки, не є (обов'язково) реальною функцією. На Стрілках - можливо, я буду дивитись.