Podczas kompilowania mojej aplikacji Haskell z -Wall
opcją GHC narzeka na osierocone instancje, na przykład:
Publisher.hs:45:9:
Warning: orphan instance: instance ToSElem Result
Klasa typu ToSElem
nie jest moja, jest zdefiniowana przez HStringTemplate .
Teraz wiem, jak to naprawić (przenieść deklarację instancji do modułu, w którym zadeklarowano Wynik) i wiem, dlaczego GHC wolałby unikać osieroconych instancji , ale nadal uważam, że moja droga jest lepsza. Nie obchodzi mnie, czy kompilator jest niewygodny - raczej to niż ja.
Powodem, dla którego chcę zadeklarować moje ToSElem
wystąpienia w module Publisher, jest to, że to moduł Publisher zależy od HStringTemplate, a nie inne moduły. Staram się oddzielić obawy i uniknąć uzależnienia każdego modułu od HStringTemplate.
Pomyślałem, że jedną z zalet klas typów Haskella, w porównaniu na przykład z interfejsami Javy, jest to, że są one otwarte, a nie zamknięte, dlatego instancje nie muszą być deklarowane w tym samym miejscu, co typ danych. Wydaje się, że rada GHC to ignorowanie tego.
Więc to, czego szukam, to albo potwierdzenie, że moje myślenie jest prawidłowe i że miałbym prawo zignorować / stłumić to ostrzeżenie, albo bardziej przekonujący argument przeciwko robieniu rzeczy po swojemu.
Odpowiedzi:
Rozumiem, dlaczego chcesz to zrobić, ale niestety może to być tylko złudzenie, że zajęcia Haskell wydają się być „otwarte” w sposób, w jaki to mówisz. Wiele osób uważa, że możliwość zrobienia tego jest błędem w specyfikacji Haskell, z powodów, które wyjaśnię poniżej. W każdym razie, jeśli naprawdę nie jest to odpowiednie dla instancji, musisz być zadeklarowany albo w module, w którym zadeklarowano klasę, albo w module, w którym jest zadeklarowany typ, prawdopodobnie jest to znak, że powinieneś użyć
newtype
lub innego opakowania wokół Twojego typu.Powody, dla których należy unikać osieroconych instancji, są znacznie głębsze niż wygoda kompilatora. Ten temat jest dość kontrowersyjny, jak widać na podstawie innych odpowiedzi. Aby zrównoważyć dyskusję, wyjaśnię punkt widzenia, że nigdy, przenigdy nie powinno się pisać instancji osieroconych, co, jak sądzę, jest zdaniem większości wśród doświadczonych haskellerów. Moja własna opinia jest gdzieś pośrodku, co wyjaśnię na końcu.
Problem wynika z faktu, że gdy istnieje więcej niż jedna deklaracja instancji dla tej samej klasy i typu, w standardowym Haskellu nie ma mechanizmu określającego, którego użyć. Program jest raczej odrzucany przez kompilator.
Najprostszym skutkiem tego jest to, że możesz mieć doskonale działający program, który nagle przestałby kompilować z powodu zmiany dokonanej przez kogoś innego w jakiejś odległej zależności od twojego modułu.
Co gorsza, działający program może zacząć się zawieszać w czasie wykonywania z powodu odległej zmiany. Możesz użyć metody, która, jak zakładasz, pochodzi z określonej deklaracji instancji i może zostać po cichu zastąpiona inną instancją, która jest po prostu inna na tyle, aby spowodować, że program zacznie się zawieszać w niewytłumaczalny sposób.
Osoby, które chcą mieć gwarancję, że te problemy nigdy się im nie przytrafią, muszą przestrzegać zasady, że jeśli ktokolwiek, gdziekolwiek, kiedykolwiek zadeklarował wystąpienie określonej klasy dla określonego typu, żadne inne wystąpienie nie może być ponownie zadeklarowane w żadnym napisanym programie przez kogokolwiek. Oczywiście istnieje obejście polegające na użyciu a
newtype
do zadeklarowania nowej instancji, ale jest to zawsze przynajmniej niewielka niedogodność, a czasem poważna. W tym sensie ci, którzy celowo piszą przypadki osierocone, są raczej niegrzeczni.Więc co należy zrobić z tym problemem? Obóz anty-osieroconych-instancji twierdzi, że ostrzeżenie GHC jest błędem, musi to być błąd, który odrzuca każdą próbę zadeklarowania osieroconej instancji. W międzyczasie musimy ćwiczyć samodyscyplinę i unikać ich za wszelką cenę.
Jak widzieliście, są tacy, którzy nie przejmują się takimi potencjalnymi problemami. Jak sugerujesz, zachęcają do korzystania z instancji osieroconych jako narzędzia do oddzielania obaw i mówią, że należy po prostu upewnić się, że nie ma problemu w każdym przypadku z osobna. Wystarczająco wiele razy przeszkadzały mi przypadki sierot innych ludzi, aby przekonać się, że taka postawa jest zbyt nonszalancka.
Myślę, że właściwym rozwiązaniem byłoby dodanie rozszerzenia do mechanizmu importu Haskella, które kontrolowałoby import instancji. Nie rozwiązałoby to całkowicie problemów, ale pomogłoby w ochronie naszych programów przed uszkodzeniami powodowanymi przez instancje sieroce, które już istnieją na świecie. A potem, z czasem, mógłbym się przekonać, że w pewnych ograniczonych przypadkach przypadek sieroty może nie być taki zły. (I właśnie ta pokusa jest powodem, dla którego niektórzy w obozie przeciw instancji sierocym są przeciwni mojej propozycji).
Mój wniosek z tego wszystkiego jest taki, że przynajmniej na razie stanowczo radziłbym, abyście unikali ogłaszania jakichkolwiek przypadków osieroconych, aby byli uważni na innych, jeśli nie ma innego powodu. Użyj
newtype
.źródło
Śmiało i powstrzymaj to ostrzeżenie!
Jesteś w dobrym towarzystwie. Conal robi to w „TypeCompose”. Robią to „chp-mtl” i „chp-transformers”, „control-monad-extra-mtl” i „control-monad-extra-monadsfd” itd.
btw prawdopodobnie już to wiesz, ale dla tych, którzy tego nie robią i natkną się na Twoje pytanie podczas wyszukiwania:
{-# OPTIONS_GHC -fno-warn-orphans #-}
Edytować:
Uznaję problemy, które Yitz wymienił w swojej odpowiedzi, jako prawdziwe problemy. Jednak uważam, że nie traktuję osieroconych instancji również jako problemu i staram się wybrać „najmniejsze zło”, czyli roztropne korzystanie z instancji osieroconych.
W mojej krótkiej odpowiedzi użyłem tylko wykrzyknika, ponieważ z twojego pytania wynika, że jesteś już dobrze świadomy problemów. W przeciwnym razie byłbym mniej entuzjastyczny :)
Trochę urozmaicenia, ale uważam, że to idealne rozwiązanie w idealnym świecie bez kompromisów:
Uważam, że problemy, o których wspomina Yitz (nie wiedząc, która instancja jest wybrana) można rozwiązać w „holistycznym” systemie programowania, w którym:
Wracając ze świata fantasy (lub miejmy nadzieję, że z przyszłości), teraz: Zalecam unikanie instancji osieroconych, a jednocześnie ich używanie, gdy „naprawdę potrzebujesz”
źródło
Instancje sieroce są uciążliwe, ale moim zdaniem czasami są potrzebne. Często łączę biblioteki, w których typ pochodzi z jednej biblioteki, a klasa pochodzi z innej. Oczywiście od autorów tych bibliotek nie można oczekiwać, że zapewnią instancje dla każdej możliwej kombinacji typów i klas. Muszę je więc zapewnić, więc są sierotami.
Pomysł, że należy opakować typ nowym typem, gdy trzeba podać instancję, jest pomysłem o wartości teoretycznej, ale w wielu przypadkach jest po prostu zbyt uciążliwy; to rodzaj pomysłu ludzi, którzy nie piszą kodu Haskella na życie. :)
Więc idź dalej i zapewnij osierocone instancje. Są nieszkodliwe.
Jeśli możesz zawiesić ghc z osieroconymi instancjami, jest to błąd i powinien zostać zgłoszony jako taki. (Błąd, który miał / ma ghc dotyczący niewykrywania wielu instancji, nie jest trudny do naprawienia).
Ale pamiętaj, że w przyszłości ktoś inny może dodać taką instancję, jak już masz, i możesz otrzymać błąd (czas kompilacji).
źródło
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)
użycie QuickCheck.W tym przypadku uważam, że użycie instancji osieroconych jest w porządku. Ogólna zasada dla mnie jest taka - możesz zdefiniować instancję, jeśli "jesteś" właścicielem "typeklasy lub jeśli" posiadasz "typ danych (lub jakiś jego komponent - np. Instancja dla Może MyData też jest w porządku, przynajmniej czasami). W ramach tych ograniczeń miejsce, w którym zdecydujesz się umieścić instancję, jest Twoim własnym biznesem.
Jest jeszcze jeden wyjątek - jeśli nie jesteś właścicielem typeklasy ani typu danych, ale tworzysz plik binarny, a nie bibliotekę, to też jest w porządku.
źródło
(Wiem, że spóźniłem się na imprezę, ale to może być przydatne dla innych)
Możesz zachować osierocone instancje w swoim własnym module, a jeśli ktoś zaimportuje ten moduł, to specjalnie dlatego, że ich potrzebuje i może uniknąć importowania ich, jeśli powodują problemy.
źródło
W związku z tym rozumiem położenie bibliotek WRT w obozie instancji anty-osieroconych, ale czy w przypadku celów wykonywalnych nie powinno być w porządku?
źródło