Как са свързани анкъри и фанин в теорията на категориите?

В библиотека, която пиша, открих, че е привидно елегантно да напиша клас, който е подобен на (но малко по-общ от) следния, който съчетава както обичайните uncurry над продукти, така и функцията fanin (от тук или тук, ако предпочитате):

{-# LANGUAGE TypeOperators, TypeFamilies,MultiParamTypeClasses, FlexibleInstances #-}
import Prelude hiding(uncurry)
import qualified Prelude 

class Uncurry t r where
    type t :->-> r
    uncurry :: (t :->-> r) -> t -> r

instance Uncurry () r where
    type () :->-> r = r
    uncurry = const

instance Uncurry (a,b) r where
    type (a,b) :->-> r = a -> b -> r
    uncurry = Prelude.uncurry

instance (Uncurry b c, Uncurry a c)=> Uncurry (Either a b) c where
    type Either a b :->-> c = (a :->-> c, b :->-> c)
    uncurry (f,g) = either (uncurry f) (uncurry g)

Обикновено преглеждам пакета categories на Edward Kmett (свързан по-горе), за да се ориентирам за подобни неща, но в този пакет имаме fanin и uncurry, разделени съответно на CoCartesian и CCC класове.

Прочетох малко за BiCCC, но все още не ги разбирам наистина.

Въпросите ми са

  1. Горната абстракция оправдана ли е от някакъв начин на присвиване на теорията на категориите?

  2. Ако е така, какъв би бил правилният език, основан на CT, за да се говори за класа и неговите екземпляри?


РЕДАКТИРАНЕ: В случай, че помага и опростяването по-горе изкривява нещата: в действителното си приложение работя с вложени продукти и съпродукти, напр. (1,(2,(3,()))). Ето истинския код (въпреки че по скучни причини последният екземпляр е опростен и не работи сам, както е написан)

instance Uncurry () r where
    type () :->-> r = r
    uncurry = const

instance (Uncurry bs r)=> Uncurry (a,bs) r where
    type (a,bs) :->-> r = a -> bs :->-> r
    uncurry f = Prelude.uncurry (uncurry . f)

-- Not quite correct
instance (Uncurry bs c, Uncurry a c)=> Uncurry (Either a bs) c where
    type Either a bs :->-> c = (a :->-> c, bs :->-> c)
    uncurry (f,fs) = either (uncurry f) (uncurry fs) -- or as Sassa NF points out:
                                                     -- uncurry (|||)

Така че екземплярът const за екземпляр () се появи естествено като рекурсивен основен случай за екземпляра на n-ary tuple uncurry, но виждането на трите заедно изглеждаше като... нещо непроизволно.


Актуализация

Открих това мислене от гледна точка на алгебрични операции, a.la Крис Тейлър блогове за "алгебрата на ADT". Правейки това изясни, че моят клас и методи наистина са просто законите на степента (и причината, поради която последният ми пример не беше правилен).

Можете да видите резултата в моя пакет shapely-data, в Exponent и Base класове; вижте също източника за бележки и маркиране на док.


person jberryman    schedule 09.10.2013    source източник


Отговори (1)


Последният ви екземпляр на Uncurry е точно uncurry (|||), така че няма нищо "по-общо" в него.

Curry намира за всяка стрелка f: ABC стрелка curryf: ACB, така че уникална стрелка eval: CBBC пътува. Можете да видите eval като ($). Казването на "CCC" е съкращение за "в тази категория имаме всички продукти, всички показатели и терминален обект" - с други думи, къри работи за всяка двойка типове и всяка функция в haskell. Едно важно следствие от това да си CCC е, че A=1A=A1 (или a е изоморфно на (a,()) и изоморфно на ((),a)).

Uncurry в haskell е обратното етикетиране на същия процес. Започваме със стрелка f=uncurryg. Всяка двойка има две проекции, така че композиция от proj1 и curryf=g дава CB. Тъй като говорим за състав и продукти, некъри в CCC дефинира уникално некъриg за всяко g: ACB. В CCC имаме всички продукти, така че имаме CBB, което може да бъде оценено на C.

По-специално, припомнете си A=A1. Това означава, че всяка функция AB също е функция A1B. Можете също така да разглеждате това като "за всяка функция AB има функция A1B", доказана чрез тривиално неизчистване, от което вашият първи екземпляр прави само половината (само доказва това за id).

Не бих нарекъл последната инстанция „некъри“ в същия смисъл, както се дефинира къри. Последният пример е конструкция на дефиницията на ко-продукт - за всяка двойка стрелки f: AC и g: BC има уникална стрелка [f,g]: (A+B)C. В този смисъл изглежда като злоупотреба с интерфейса - това е обобщение на значението от "нетърпелив" до "дадено нещо, дайте ми нещо" или "вярно съответствие между :->-> и haskell функции". Може би бихте могли да преименувате класа на Arrow.

person Sassa NF    schedule 09.10.2013
comment
Благодаря много. В действителното си приложение имам работа с вложени кортежи и продукти, напр. (1,(2,(3,()))), откъдето идва екземплярът () (и това имах предвид, когато казах по-общо). Ще редактирам отговора си с допълнение с истинския проблем и ще се радвам, ако можете да погледнете и да видите дали имате повече представа. - person jberryman; 09.10.2013
comment
@jberryman Вашата редакция изглежда, че се нуждае от хакване. haskell.org/package/recursion-schemes-2.0/docs/. data PP a b = PP a b deriving (Functor); type P a = Fix (PP a) - сега P a е вложен кортеж. Не съм сигурен как да накарам това да работи с (,) вместо PP. - person Sassa NF; 09.10.2013
comment
@jberryman тогава вашите два екземпляра дефинират алгебра за функтор F(X)=1+AX - чрез дефиниране на 1-›X и AX-›X - person Sassa NF; 09.10.2013
comment
Благодаря, пакетът recursion-schemes изглежда би бил полезен за мен да разбера. Това, което всъщност правя, е да дефинирам единичен копродукт тип, който се използва като основен случай за рекурсивните вложени екземпляри Either. - person jberryman; 09.10.2013
comment
Също така предполагам, че е интересно, че с моята рекурсивна версия (от EDIT) получаваме: (a -> r) -> ((a,()) -> r) - person jberryman; 09.10.2013
comment
@jberryman type RecursiveNestedEither a = Fix (Either a) - полезен при ко-рекурсия. - person Sassa NF; 09.10.2013