absurd
Funkcja w Data.Void
ma następujący podpis, gdzie Void
jest logicznie niezamieszkana typ eksportowane przez ten pakiet:
-- | Since 'Void' values logically don't exist, this witnesses the logical
-- reasoning tool of \"ex falso quodlibet\".
absurd :: Void -> a
Znam wystarczająco logikę, aby uzyskać uwagę dokumentacji, że odpowiada to, poprzez zgodność twierdzeń jako typów, prawidłowej formule ⊥ → a
.
Jestem zdziwiony i ciekawy: w jakich praktycznych problemach programistycznych ta funkcja jest przydatna? Myślę, że być może jest to przydatne w niektórych przypadkach jako bezpieczny dla typu sposób wyczerpującej obsługi przypadków „nie może się zdarzyć”, ale nie wiem wystarczająco dużo o praktycznych zastosowaniach Curry-Howarda, aby stwierdzić, czy ten pomysł jest w w ogóle właściwy tor.
EDYCJA: Przykłady najlepiej w Haskell, ale jeśli ktoś chce używać zależnie pisanego języka, nie będę narzekać ...
źródło
absurd
funkcja została użyta w tym artykule dotyczącymCont
monady: haskellforall.com/2012/12/the-continuation-monad.htmlabsurd
jako jeden kierunek izomorfizmu międzyVoid
aforall a. a
.Odpowiedzi:
Życie jest trochę trudne, ponieważ Haskell nie jest surowy. Ogólnym przypadkiem użycia jest obsługa niemożliwych ścieżek. Na przykład
Okazuje się, że jest to dość przydatne. Rozważ prosty typ dla
Pipes
jest to ścisła i uproszczona wersja standardowego typu potoków z
Pipes
biblioteki Gabriela Gonzalesa . Teraz możemy zakodować potok, który nigdy nie daje (tj. Konsument) jakoto naprawdę nigdy nie ustąpi. Wynika z tego, że prawidłowa reguła fold dla a
Consumer
jestlub alternatywnie, że możesz zignorować przypadek rentowności w kontaktach z konsumentami. To jest ogólna wersja tego wzorca projektowego: użyj polimorficznych typów danych i
Void
pozbądź się możliwości, gdy zajdzie taka potrzeba.Prawdopodobnie najbardziej klasycznym zastosowaniem
Void
jest CPS.to znaczy a
Continuation
jest funkcją, która nigdy nie zwraca.Continuation
jest wersją typu „not”. Z tego otrzymujemy monadę CPS (odpowiadającą logice klasycznej)ponieważ Haskell jest czysty, nie możemy nic z tego wyciągnąć.
źródło
Void
jest niezamieszkany. W Haskell zawiera_|_
. W ścisłym języku konstruktor danych, który przyjmuje argument typu,Void
nigdy nie może zostać zastosowany, więc prawa strona dopasowania wzorca jest nieosiągalna. W Haskell musisz użyć a,!
aby to wymusić, a GHC prawdopodobnie nie zauważy, że ścieżka jest nieosiągalna._|_
? i czy cierpi z powodu tego samego ograniczenia?Rozważ tę reprezentację dla wyrażeń lambda sparametryzowanych przez ich wolne zmienne. (Patrz artykuły Bellegarde i Hook 1994, Bird i Paterson 1999, Altenkirch i Reus 1999.)
Z pewnością możesz to uczynić
Functor
, przechwytując pojęcie zmiany nazwy iMonad
przechwytując pojęcie zastępowania.Rozważmy teraz zamknięte warunki: to są mieszkańcy
Tm Void
. Powinieneś być w stanie osadzić zamknięte warunki w terminach z dowolnymi wolnymi zmiennymi. W jaki sposób?Haczyk polega oczywiście na tym, że ta funkcja przejdzie przez termin dokładnie nic nie robiąc. Ale to odrobinę bardziej szczere niż
unsafeCoerce
. I dlategovacuous
został dodany doData.Void
...Lub napisz ewaluatora. Oto wartości z wolnymi zmiennymi w
b
.Przedstawiłem właśnie lambdy jako zamknięcia. Ewaluator jest parametryzowany przez środowisko odwzorowujące wolne zmienne
a
na wartości powyżejb
.Zgadłeś. Aby ocenić zamknięty termin w dowolnym celu
Mówiąc bardziej ogólnie,
Void
jest rzadko używany samodzielnie, ale jest przydatny, gdy chcesz utworzyć wystąpienie parametru typu w sposób wskazujący na jakąś niemożliwość (np. Tutaj, używając wolnej zmiennej w zamkniętym terminie). Często te parametryzowane typy pochodzą z funkcji wyższego rzędu operacji podnoszenia od parametrów operacji na całym typu (na przykład tutajfmap
,>>=
,eval
). Więc przekazujeszabsurd
jako operację ogólnego przeznaczeniaVoid
.Na przykład, wyobraź sobie użycie
Either e v
do przechwytywania obliczeń, które, miejmy nadzieję, dają ci,v
ale mogą wywołać wyjątek typue
. Możesz użyć tego podejścia do jednolitego dokumentowania ryzyka złego zachowania. Aby uzyskać doskonale wykonane obliczenia w tym ustawieniu, weźe
jeVoid
, a następnie użyjbezpiecznie biegać lub
aby osadzić bezpieczne komponenty w niebezpiecznym świecie.
Aha, i ostatni hurra, radzenie sobie z „nie może się zdarzyć”. Pojawia się w ogólnej konstrukcji zamka błyskawicznego, wszędzie tam, gdzie nie może znajdować się kursor.
Postanowiłem nie usuwać reszty, mimo że nie jest to do końca istotne.
Właściwie może to ma znaczenie. Jeśli masz ochotę na przygodę, ten niedokończony artykuł pokazuje, jak
Void
skompresować reprezentację terminów za pomocą wolnych zmiennychw dowolnej składni generowanej swobodnie z funktora
Differentiable
i . Używamy do reprezentowania regionów bez wolnych zmiennych i do przedstawiania tuneli rurowych przez regiony bez wolnych zmiennych albo do izolowanej zmiennej wolnej, albo do skrzyżowania ścieżek do dwóch lub więcej zmiennych wolnych. Muszę kiedyś skończyć ten artykuł.Traversable
f
Term f Void
[D f (Term f Void)]
Dla typu bez wartości (a przynajmniej takich, o których warto mówić w grzecznym towarzystwie),
Void
jest niezwykle przydatne. Iabsurd
jak tego używasz.źródło
forall f. vacuous f = unsafeCoerce f
byłaby prawidłowa reguła przepisywania GHC?Functor
przypadki mogłyby być GADTs, które nie są faktycznie coś podobnego funktorów.Functor
nie złamałobyfmap id = id
reguły? A może to właśnie masz na myśli mówiąc „fałszywy”?Dokładnie tak.
Można powiedzieć, że
absurd
nie jest to bardziej przydatne niżconst (error "Impossible")
. Jednak jest to typ ograniczony, więc jego jedyne wejście może być czymś typuVoid
, typem danych, który jest celowo pozostawiony niezamieszkany. Oznacza to, że nie ma rzeczywistej wartości, do której można przejśćabsurd
. Jeśli kiedykolwiek znajdziesz się w gałęzi kodu, w której kontroler typów myśli, że masz dostęp do czegoś w rodzajuVoid
, to cóż, jesteś w absurdalnej sytuacji. Więc po prostu używaszabsurd
do zaznaczenia, że ta gałąź kodu nigdy nie powinna zostać osiągnięta.„Ex falso quodlibet” dosłownie oznacza „z [a] fałszywego [zdania], wszystko następuje”. Kiedy więc stwierdzisz, że trzymasz w ręku dane, których typ jest
Void
, wiesz, że masz w rękach fałszywe dowody. Możesz zatem wypełnić dowolną lukę (przezabsurd
), ponieważ z fałszywej propozycji wszystko wynika.Napisałem post na blogu o ideach Conduit, który ma przykład użycia
absurd
.http://unknownparallel.wordpress.com/2012/07/30/pipes-to-conduits-part-6-leftovers/#running-a-pipeline
źródło
Ogólnie można go użyć, aby uniknąć pozornie częściowych dopasowań wzorców. Na przykład, pobierając przybliżenie deklaracji typu danych z tej odpowiedzi :
Wtedy możesz użyć w
absurd
ten sposób, na przykład:źródło
Istnieją różne sposoby przedstawiania pustego typu danych . Jeden to pusty algebraiczny typ danych. Innym sposobem jest utworzenie aliasu dla ∀α.α lub
w Haskell - w ten sposób możemy zakodować to w Systemie F (zobacz Rozdział 11, Dowody i typy ). Te dwa opisy są oczywiście izomorficzne, a izomorfizm jest obserwowany przez
\x -> x :: (forall a.a) -> Void
i przezabsurd :: Void -> a
.W niektórych przypadkach preferujemy wariant jawny, zwykle jeśli pusty typ danych pojawia się w argumencie funkcji lub w bardziej złożonym typie danych, takim jak Data.Conduit :
W niektórych przypadkach preferujemy wariant polimorficzny, zwykle pusty typ danych jest zaangażowany w typ zwracany przez funkcję.
absurd
pojawia się, gdy dokonujemy konwersji między tymi dwoma reprezentacjami.Na przykład
callcc :: ((a -> m b) -> m a) -> m a
uses (implicit)forall b
. Może to być również typ((a -> m Void) -> m a) -> m a
, ponieważ wywołanie kontinacji w rzeczywistości nie zwraca, ale przekazuje kontrolę do innego punktu. Gdybyśmy chcieli pracować z kontynuacjami, moglibyśmy zdefiniować(Moglibyśmy użyć,
type Continuation' r a = forall b . a -> Cont r b
ale wymagałoby to typów rangi 2.) A potemvacuousM
konwertuje toCont r Void
naCont r b
.(Pamiętaj również, że możesz użyć witryny haskellers.com, aby wyszukać użycie (odwrotne zależności) określonego pakietu, na przykład sprawdzić, kto i jak używa void pakietu.)
źródło
TypeApplications
mogą być wykorzystywane do być bardziej wyraźne o szczegółyproof :: (forall a. a) -> Void
:proof fls = fls @Void
.W językach zależnych, takich jak Idris, jest to prawdopodobnie bardziej przydatne niż w Haskell. Zwykle w funkcji total, gdy dopasujesz wzorzec do wartości, której w rzeczywistości nie można wstawić do funkcji, konstruujesz wartość typu niezamieszkanego i używasz
absurd
do sfinalizowania definicji przypadku.Na przykład ta funkcja usuwa element z listy z ograniczeniem kosztu na poziomie typu, który jest tam obecny:
Gdzie drugi przypadek mówi, że na pustej liście jest pewien element, co jest całkiem absurdalne. Generalnie jednak kompilator tego nie wie i często musimy to wyraźnie określić. Wtedy kompilator może sprawdzić, czy definicja funkcji nie jest częściowa i uzyskujemy silniejsze gwarancje czasu kompilacji.
Z punktu widzenia Curry-Howarda, gdzie są twierdzenia,
absurd
jest to rodzaj QED w dowodzie przez sprzeczność.źródło