2013-01-14 11 views
13

Mam sytuację, w której w tej chwili używam niezwykle niebezpiecznej funkcji unsafeCoerce. Na szczęście nie jest to nic ważnego, ale zastanawiałem się, czy to bezpieczne korzystanie z tej funkcji, czy też istnieje inny sposób rozwiązania tego konkretnego problemu, o którym wiedzą inni.Czy to bezpieczne korzystanie z niebezpiecznego samochodu?

Kod Mam coś jak poniżej:

data Token b = Token !Integer 

identical :: Token a -> Token b -> Bool 
identical (Token a) (Token b) = a == b 

data F a = forall b. F (Token b) (a -> b) 

retrieve :: Token b -> F a -> Maybe (a -> b) 
retrieve t (F t' f) = if identical t t' then Just (unsafeCoerce f) else Nothing 

Dwa dodatkowe rzeczy, aby pamiętać, jest to, że te tokeny są używane wewnątrz monady, które używam w celu zapewnienia, że ​​dostawa liczb dla nich jest wyjątkowy (nie tworzę tego samego dwa razy). Używam również zmiennej ilościowej typu cienia, podobnie jak monady ST, aby upewnić się, że (zakładając, że użyte są tylko metody, które eksponuję w module), nie ma sposobu na zwrócenie tokena (lub w rzeczywistości nawet F) z monady bez błędu typu. Nie ujawniam również konstruktora tokena.

Myślę, że, o ile widzę, powinno to być bezpieczne korzystanie z niebezpiecznego samochodu, ponieważ mogę powiedzieć z (mam nadzieję) dość wysokim przekonaniem, że wartość, którą wymuszam, jest w rzeczywistości dokładnie taka, wymuszam to, ale mogę się mylić. Próbowałem również przy użyciu Data.Typeable, który działa ładnie, ale w tej chwili próbuję tego uniknąć ograniczenia Typeable, zwłaszcza, że ​​gcast wydaje się robić coś na wiele sposobów podobne, i nadal potrzebowałbym tokenów, aby rozróżnić między różne F tego samego typu.

Dziękuję bardzo za pomoc/radę.

+4

Wygląda to trochę jak 'Data.Typeable', który używa' unsafeCoerce' pod kołdrą do implementacji 'cast'. –

+1

Jest to bardzo podobne - w rzeczywistości Iirc powiedziałem tak samo w drugiej części ostatniego akapitu pytania. Dzięki i tak. – DarkOtter

+2

Jeśli skutecznie kopiujesz 'cast', użycie' unsafeCoerce' jest bezpieczne, ale tracisz wygenerowany kompilator 'typeOf' /' TypeRep'. Możesz rozważyć użycie 'TypeRep' zamiast' Integer' w tokenie. –

Odpowiedz

9

To nie jest bezpieczne per se:

oops :: F Bool 
oops = F (Token 12) not 

bad :: Token Int 
bad = Token 12 

*Token> maybe 3 ($ True) $ retrieve bad oops 
1077477808 

F a jest typem egzystencjalnie ilościowo, nie wiem jakiego rodzaju b poszedł do niego. Od identical nie dba o parametrach typu do Token, że nie może sprawdzić, czy dostarczony b z pierwszego argumentu retrieve „s nie ma nic wspólnego z tym, co poszedł do F a.

Niezależnie, czy ochrona

Dwa dodatkowe rzeczy, aby pamiętać, że są te tokeny są używane wewnątrz monady, które używam w celu zapewnienia, że ​​dostawa liczb dla nich jest unikatowy (czyli nie zrobić tokeny dwa razy). Również użycie forall ilościowo zmienny typ cień w taki sam sposób, jak monadzie ST, aby upewnić się, że (przy założeniu, że tylko metody wystawiać w module stosuje) nie ma możliwości powrotu token (albo w rzeczywistości nawet F) z monady bez błędu typu. Nie ujawniam również konstruktora tokena.

jest wystarczająco silny, aby zapewnić bezpieczeństwo w praktyce, nie mogę powiedzieć, nie widząc go. Jeśli faktycznie nie Token s mogą być tworzone poza obliczeń, a wartość Integer z Token jednoznacznie charakteryzuje parametr typu, byłoby bezpieczne.

14

Zaimplementowałeś ograniczoną formę dynamicznego typowania, zasadniczo zgodnie ze stylem Data.Dynamic - a mianowicie, dzieląc wartość (nieprzezroczystą) na dowód jej typu. W czasie wykonywania można wykonać niebezpieczny przymus, w oparciu o dowody dostarczone z danymi.

fromDyn (Dynamic t v) def 
    | typeOf def == t = unsafeCoerce v 
    | otherwise  = def 

To canoncial podejście, z długą historią, sięgającą:

Mart'ın Abadi Luca Cardelli Benjamin Pierce, a Gordon Plotkin. Dynamiczne pisanie w statycznie napisanym języku. ACM Transactions on języków programowania i systemy, 13 (2): 237-268, kwietnia 1991.

Bezpieczeństwo podejścia polega na unforgeability żetonów typu Runtime. W twoim przypadku każdy może zbudować token, który równoważy dwa typy - musisz zagwarantować mapowanie 1-1 z typów na tokeny i upewnić się, że złośliwy użytkownik nie może skonstruować niepoprawnych tokenów. W przypadku GHC ufamy wystąpieniom Typeable (i modułowi abstraction).

Powiązane problemy