Implementacja GHC Data.Reflection
z pakietu wykorzystuje sztuczkę obejmującą unsafeCoerce
, która wykorzystuje sposób, w jaki GHC kompiluje typologie przy użyciu przekazywanych słowników. Realizacja jest krótki, więc mogę odtworzyć go w całości tutaj:Czy możliwe jest zaimplementowanie sztuczki Data.Reflection za pomocą rodzin typów zamiast podpowiedzi?
class Reifies s a | s -> a where
reflect :: proxy s -> a
newtype Magic a r = Magic (forall (s :: *). Reifies s a => Proxy s -> r)
reify :: forall a r. a -> (forall (s :: *). Reifies s a => Proxy s -> r) -> r
reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy
To sprawia, że możliwe zreifikować wartość na poziomie typu, a następnie odbić go z powrotem:
ghci> reify (42 :: Integer) (\p -> reflect p) :: Integer
42
jestem zainteresowany w stosowaniu tej techniki, ale pomyślałem, że byłoby wygodne dla moich celów korzystania rodzinę typ z Reifies
zamiast zależności funkcjonalnej, więc próbowała przepisać realizacji przy użyciu zwykłych transformacja:
class Reifies s where
type Reflects s
reflect :: Proxy s -> Reflects s
newtype Magic a r = Magic (forall (s :: *). (Reifies s, Reflects s ~ a) => Proxy s -> r)
reify :: forall a r. a -> (forall (s :: *). (Reifies s, Reflects s ~ a) => Proxy s -> r) -> r
reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy
Niestety, to już nie działa! To zmienia kompilację na tyle znacząco, aby przełamać unsafeCoerce
trick:
ghci> reify (42 :: Integer) (\p -> reflect p) :: Integer
2199023255808
Jednak nie jestem wystarczająco zaznajomieni z jak działa GHC zrozumieć dlaczego. Czy możliwe jest zaimplementowanie Data.Reflection
przy użyciu skojarzonego typu zamiast zależności funkcjonalnej? Jeśli tak, to co trzeba zmienić? Jeśli nie, dlaczego nie?
Po pisałem to pytanie, Zerknąłem na rdzeniu i odkrytych, ku mojemu zaskoczeniu, że równości typu mają świadków uruchomieniowe przynajmniej na poziomie podstawowym. Czy są jakieś informacje o tym, jak działają świadkowie ds. Równości? Czy mogę (nie tylko) zapewnić coś innego zamiast świadka równości? Zmieniając nieco mój kod do "unsafeCoerce (Magic k :: Magic a r) (const a)() działa Proxy", ale obawiam się, że to nie zawsze działa. –
@AlexisKing, to dobre pytanie, na które nie znam odpowiedzi. – dfeuer
@AlexisKing, inna opcja (jak sądzę) może polegać na "odbiciu" zwracającym GADT dającym dowód, że 'a ~ odzwierciedla s'. Nie przepracowałem tego jeszcze. – dfeuer