2013-01-20 18 views
25

Co mówię jest to, że nie jest możliwe określenie:Dlaczego GHC Haskell nie obsługuje przeciążonych nazw parametrów rekordów?

data A = A {name :: String} 
data B = B {name :: String} 

wiem, że GHC tylko desugars to do prostych funkcji i idiomatyczne sposobem rozwiązania tego byłoby:

data A = A {aName :: String} 
data B = B {bName :: String} 

class Name a where 
    name :: a -> String 

instance Name A where 
    name = aName 

instance Name B where 
    name = bName 

Po napisaniu tego, nie podoba mi się to tak bardzo ... czy ten typ literowania nie mógł być częścią procesu usuwania?


Przyszła mi do głowy myśl, kiedy pisałem kilka analiz JSON Aesona. Gdzie byłoby zbyt łatwo po prostu dla każdego typu danych musiałem napisać wszystko ręcznie (obecnie> 1k linii i liczenie). Posiadanie nazw takich jak name lub po prostu value w rekordzie danych nie jest rzadkością.

http://www.haskell.org/haskellwiki/Performance/Overloading wspomina, że ​​przeciążenie funkcji wprowadza pewne obciążenie środowiska wykonawczego. Ale właściwie nie rozumiem, dlaczego kompilator nie byłby w stanie rozwiązać tego problemu podczas kompilacji i wewnętrznie nadawać im różne nazwy.

This SO question from 2012 więcej lub mniej państw historyczne powodów i punktów do wątku z roku 2006. Czy ostatnio coś się zmieniło?

Nawet gdyby nie było trochę czasu pracy, większość ludzi nie miałaby nic przeciwko, ponieważ większość kodu prawie nie jest krytyczna.

Czy jest jakieś ukryte rozszerzenie języka, które faktycznie pozwala na to? Znowu nie jestem pewien ... ale myślę, że Idris rzeczywiście to robi?

+0

Pomyślnie: Czy ktoś mógłby dodać tag Idris do SO i to pytanie? Może ktoś z tej społeczności też mógłby się rozwinąć. – fho

+0

Gratulacje, że jest to pierwsze pytanie zręcznie idris. Jeśli chcesz przeczytać więcej na ten temat, znajduje się strona [ghc wiki] (http://hackage.haskell.org/trac/ghc/wiki/Records) i [konwersacja na reddit] (http: // www.reddit.com/r/haskell/comments/kgd4g/the_records_problem_in_haskell_help_build_a/). – Davorak

+0

Dzięki za linki. Dokumenty GHC nie wydają się mieć dobrej pozycji w wynikach Google. – fho

Odpowiedz

3

Korzystanie składnię rekordu

data A { name :: String } 

pośrednio definiuje funkcję

name :: A -> String 

Jeśli definiować zarówno A i B z { name :: String } mamy definicje typów sprzecznych dla name:

name :: A -> String 
name :: B -> String 

To jest nie jest jasne, w jaki sposób proponowane zajęcia typu ukryte będzie działać bo jeśli definiujemy dwa rodzaje

data A { name :: String } 
data B { name :: Text } 

wtedy właśnie przesunął problem z definicjami sprzecznych klasowych Typ:

class Has'name a where 
    name :: a -> String 

class Has'name a where 
    name :: a -> Text 

W zasadzie może to być rozwiązane jednego tak czy inaczej, ale jest to tylko jedna z kilku trudnych sprzecznych pożądanych właściwości dla rekordów. Kiedy zdefiniowano Haskella, zdecydowano, że lepiej jest mieć proste, ale ograniczone wsparcie, niż próbować zaprojektować coś bardziej ambitnego i skomplikowanego. Kilka udoskonaleń zapisów było omawianych w różnym czasie i są odwieczne dyskusje, np. this Haskell Cafe thread. Być może coś będzie wypracowane dla Haskell Prime.

+0

NIE! Już to wiedzieliśmy. I odrzucił to! Ten rodzaj "martwego mózgu" jest "dokładnie" problemem. Co miał na myśli, to mieć oddzielne spacje nazw na typ danych! Podobnie jak w przypadku OOP, gdzie A.name nie jest w konflikcie z B.name. (nazwa A) i (nazwa B) również nie powinny, z tego samego powodu. Chodzi mi o to, że mogłaby po prostu wewnętrznie wydzielić "dane A {nazwa :: ciąg}" i "niech a = A" Betty "z nazwy a" na "dane A {nazwa} ciąg} i" niech a = A " Betty "in a name a". Co to jest zatrzymanie? – Evi1M4chine

+4

@ Evi1M4chine: Być może powinieneś przeczytać niektóre z obszernych dyskusji na ten temat, zanim zaproponujesz coś, co w rzeczywistości nie działa bardzo dobrze, a następnie wymaga, aby inni ludzie je wdrożyli. –

+0

@ Evi1M4chine Więc jaki jest typ 'name' w twoim przykładzie? Musi mieć jakiś główny typ, w przeciwnym razie rodzaj wnioskowania w ogóle przerośnie potwornie. Teraz, jeśli nie przejmujesz się zbytnio wnioskami typu wstecznego, możesz wydzielić do otwartych rodzin bez iniekcji. – semicolon

4

Wiele, w większości drobnych powodów.Jednym z nich jest problem podniesiony przez a better answer, przeładowanie tylko na pierwszym argumencie jest niewystarczające, aby obsłużyć wszystkie przydatne przypadki.

Mogłabyś "desugar"

data A { name :: String } 
data B { name :: Text } 

do

class Has'name a b | a -> b where 
    name :: a -> b 

data A { aName :: String } 
instance Has'name A String where 
    name :: aName 

data B { bName :: Text } 
instance Has'name B Text where 
    name :: bName 

ale to wymagałoby rozszerzenia GHC (Zależności funkcjonalne), które nie znalazły się w standardzie, jeszcze. Wykluczałoby to używanie tylko "nazwy" do tworzenia rekordów, aktualizacji i dopasowywania wzorców (wzorce widoków mogą w tym pomóc), ponieważ "nazwa" w tych przypadkach nie jest "tylko" funkcją. Prawdopodobnie możesz wyciągnąć coś bardzo podobnego do szablonu Haskella.

+0

To nie odnosi się do polimorficznej aktualizacji ani do innych typów czcionek na stronie –

+0

Jeśli potrzebujesz aktualizacji polimorficznej, prawdopodobnie powinieneś mieć "nazwę" nadal zdefiniowaną w typie, ale upewnij się, że ma ona typ Functor f => (a -> fb) -> s -> ft (lub coś "blisko"), aby mógł być używany jako soczewka. W tym momencie generalnie błądziłem po stronie mającej różne, jeśli długie, nazwy różnych soczewek, poprzez kwalifikowany import. –

0

Najlepszym sposobem, jaki znalazłem, jest użycie preprocesora do rozwiązania tego zdecydowanie głupiego problemu.

Haskell i GHC to ułatwiają, ponieważ cały analizator składni Haskella jest dostępny jako normalna biblioteka. Możesz po prostu przeanalizować wszystkie pliki, zrobić schemat zmiany nazwy (np. "Dane A {nazwa :: ciąg}" i "niech a = A" Betty "z nazwy a" na "dane A {nazwa_tam} ciąg} i" let a = A "Betty" w aNaz a ») w zależności od typu danych, do których zastosowano funkcję nazwy, za pomocą przelicznika typów i zapisania ich do kompilacji.

Ale szczerze, to powinno być zintegrowane z GHC. Masz rację: To głupie, że to nie jest uwzględnione.

+0

Ale nie wszystkie typy są znane w czasie kompilacji, więc musisz się martwić o takie rzeczy, jak główne typy. Jaki jest typ "nazwy" w twoim przykładzie? Musi mieć JEDEN typ, aby dobrze działał z resztą Haskella w kategoriach wnioskowania typu i tym podobnych. Jeśli 'A' i' B' są na przykład instancją 'Monoid', co daje ci' name mempty'? – semicolon