2013-04-16 22 views
9

Na stronie 321 Real World HaskellDlaczego można pominąć konstruktora, odwołując się do typów liczb owiniętych typu new?

Są te kody,

...

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
newtype AInt = A { unA::Int } 
    deriving (Show, Eq, Num) 

instance Monoid AInt where 
    mempty = 0 

Moja dezorientacja dlatego jest

mempty = 0 

ale nie

mempty = A 0 

?


Zauważyłem również, że zarówno

ghci> 0 :: AInt 

i

ghci> A 0 :: AInt 

mi dać taką samą odpowiedź

A { unA = 0 } 

Czy ktoś mógłby mi powiedzieć, jaka jest różnica między tezami dwa?

Odpowiedz

13

Podstęp jest tutaj z rozszerzeniem GeneralizedNewtypeDeriving. W szczególności pozwala nam to uzyskać dowolną klasę dla newtype, o ile typem bazowym jest instancja. Wszystko to polega na skopiowaniu instancji ze starego typu do nowego typu.

W tym konkretnym przypadku AInt wywodzi się z Num. Oznacza to, że AInt jest instancją Num używającą tego samego kodu co Int (ze wszystkimi owiniętymi odpowiednio konstruktorami A). Obejmuje to funkcję Int '.

Funkcja fromInteger jest definiowana w kategoriach Int „s fromInteger, patrząc coś takiego:

fromInteger i = A (fromInteger i) 

Od 0 jest polimorficzny - ma rodzaj 0 :: Num a => a --it jest ważna stała dla dowolny wpisz Num. Dzięki nowemu pochodnemu, obejmuje to AInt, używając powyższej funkcji fromInteger. Oznacza to, że nie ma żadnej różnicy między 0 :: AInt a A 0 :: AInt.

+4

Więc to jest ten sam powód, dla którego możemy robić zarówno '1 :: Float' i' 1 :: Int'? – Znatz

+2

@Znatz: Tak. Ponieważ 'AInt' jest w' Num', może używać literałów takich jak 'Float',' Int' i mnóstwo innych typów. –

+0

: Wielkie dzięki! – Znatz

12

Literały numeryczne, takie jak 0, są przeciążone i mają typ 0 :: Num a => a, co oznacza, że ​​mogą być dowolnym typem, dla którego istnieje instancja Num, w zależności od kontekstu. Dzieje się tak za pośrednictwem funkcji fromInteger w klasie typu Num, więc po wpisaniu 0 traktowane jest tak, jakbyś napisał fromInteger 0.

Korzystając GeneralizedNewtypeDeriving, GHC ma (efektywnie) pisemne wystąpienie Num dla swojej klasie patrząc coś takiego:

instance Num AInt where 
    fromInteger n = A (fromInteger n) 
    ... 

Więc kiedy piszesz 0 :: AInt, to rozszerza się fromInteger 0 :: AInt jest (przez definicja powyżej) równa A (fromInteger 0), która jest taka sama, jak gdybyś napisał A 0.

w rzeczywistości nie pisze nowej intencji. Po prostu wykonuje niezbędne rzuty, aby użyć istniejącego.

+0

To faktycznie pisze nowe wystąpienie, jak mi powiedziano. Trudnym bitem jest to, że instancje nadklasy nie mogą pochodzić z GND, co unieważnia wskaźniki klasy nadklasy. Oznacza to, że możesz, jak sądzę, napisać 'instance Eq Foo where ...; wywodząca się instancja Ord Foo'. Aby uniknąć kłopotów w tych przypadkach, powiedziano mi, że GHC tworzy nowe słowniki, ale używa ich ponownie. – dfeuer