/ / Dlaczego nie mogę dopasować wzorców do rodziny typów? - haskell, typy

Dlaczego nie mogę dopasować wzoru do rodziny typów? - haskell, typy

Rozważ następujący kod:

{-# LANGUAGE TypeFamilies #-}

data Twothings a b = Twothings a b

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing a
Leftthing a = a

leftthing :: a -> Leftthing a
leftthing (Twothings a b) = leftthing a
leftthing b = b

Nie kompiluje się z następującym błędem:

Couldn"t match expected type ‘a’
with actual type ‘Twothings a0 t0’
‘a’ is a rigid type variable bound by
the type signature for:
leftthing :: forall a. a -> Leftthing a

Narzeka na linię leftthing (Twothings a b) = leftthing a. Jeśli rozumiem poprawnie, nie można ujednolicić zmiennej typu a w podpisie typu z typem konstruktora Twothings. OK, wydaje się to mieć sens. Ale wtedy, jak mogę kiedykolwiek zdefiniować funkcję z rodzinami typów w podpisie typu?

Odpowiedzi:

11 dla odpowiedzi nr 1

Kiedy deklarujesz

leftthing :: a -> Leftthing a

mówisz, że gość z leftthing co wybrać a jest.

Kiedy następnie piszesz

leftthing (Twothings a b) = leftthing a

ty jesteś zarozumiały że wybrali Twothings wpisz, a ponieważ nie jest to konieczne, Twój program zostanie odrzucony.

Być może myślałeś, że jesteś testowanie, czy oni wybrali Twothings wpisz, ale nie! Informacje o typie są usuwane przed czasem wykonywania, więc nie ma sposobu na wykonanie takiego testu.

ty mogą spróbuj przywrócić niezbędne informacje o czasie wykonywania. Najpierw pozwól mi naprawić niespójność między twoimi Leftthing i leftthing.

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing{-you forgot the recursion!-} a
Leftthing a = a

Teraz możemy zdefiniować GADT świadków Twothingness.

data IsItTwothings :: * -> * where
YesItIs   :: IsItTwothings a -> IsItTwothings (Twothings a b)
NoItIsn"t :: Leftthing a ~ a => IsItTwothings a
-- ^^^^^^^^^^^^^^^ this constraint will hold for any type
-- which is *definitely not* a Twothings type

A potem możemy przekazać świadka jako argument:

leftthing :: IsItTwothings a -> a -> Leftthing a
leftthing (YesItIs r) (Twothings a b) = leftthing r a
leftthing NoItIsn"t   b               = b

W efekcie świadek jest jednoargumentowym kodowaniem liczby zagnieżdżonych w lewo Twothingses w katalogu głównym Twojego typu. To wystarczająca ilość informacji, aby w czasie wykonywania określić właściwą ilość rozpakowywania.

> leftthing (YesItIs (YesItIs NoItIsn"t)) (Twothings (Twothings True 11) (Twothings "strange" [42]))
True

Podsumowując, nie można znaleźć typu według wzorudopasowanie do wartości. Zamiast tego należy znać typ dopasowania wzoru (ponieważ typ określa układ pamięci i nie ma tagów typu run time). Nie można bezpośrednio dopasować typów do typów (ponieważ nie są one dostępne do dopasowania). Można zamiast tego konstruować typy danych, które są dowodami wykonawczymi typu struktury i dopasowania.

Być może pewnego dnia twój program zadziała, jeśli nadasz mu typ

leftthing :: pi a. a -> Leftthing a

gdzie pi jest kwantyfikatorem zależnym, co oznacza, że ​​argument ukrytego typu nie jest kasowany, ale jest przekazywany i dopasowywany w czasie wykonywania. Ten dzień jeszcze nie nadszedł, ale myślę, że tak będzie.