2015-10-05 34 views
6

unsafeVacuous in Data.Void.Unsafe e .# e #. in Data.Profunctor.Unsafe entrambi avvertono sui pericoli dell'utilizzo di quelle funzioni con funtori/profuncatori che sono GADT. Alcuni esempi pericolosi sono evidenti:Proprio quando non sono sicuri, #., E. # Non sicuri?

data Foo a where 
    P :: a -> Foo a 
    Q :: Foo Void 

instance Functor Foo where 
    fmap f (P x) = P (f x) 
    fmap f Q = P (f undefined) 

Qui, unsafeVacuous Q produrrà un costruttore Q con un tipo fasullo.

Questo esempio non è molto preoccupante perché non sembra nemmeno lontanamente come un'istanza ragionevole Functor. Ci sono esempi che fanno? In particolare, sarebbe possibile costruire quelli che obbediscono alle leggi del funtore/profunctor quando vengono manipolati usando solo le loro API pubbliche, ma si rompono orribilmente di fronte a queste funzioni non sicure?

+3

Per quanto sopra 'Foo' penso che sia impossibile creare un'istanza di functor rispettosa della legge. Mi chiedo quali tipi di GADT permettano di farlo. Sembra che avere un costruttore 'K :: ... -> Foo T' impedisce qualsiasi istanza di functor a meno che' T' sia una variabile di tipo. (Forse sto trascurando qualcosa, però ...) – chi

risposta

3

Non credo che ci sia un vero functor in cui unsafeVacuous causerebbe un problema. Ma se scrivi un pessimo Functor puoi creare il tuo unsafeCoerce, il che significa che deve essere etichettato con {-# LANGUAGE Unsafe #-}. C'era an issue about it in void.

Ecco un unsafeCoerce mi si avvicinò con

{-# LANGUAGE GADTs    #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE TypeFamilies  #-} 
import Data.Void 
import Data.Void.Unsafe 

type family F a b where 
    F a Void = a 
    F a b = b 

data Foo a b where 
    Foo :: F a b -> Foo a b 

instance Functor (Foo a) where 
    fmap = undefined 

unsafeCoerce :: forall a b. (F a b ~ b) => a -> b 
unsafeCoerce a = (\(Foo b) -> b) $ (unsafeVacuous (Foo a :: Foo a Void) :: Foo a b) 

main :: IO() 
main = print $ (unsafeCoerce 'a' :: Int) 

che stampa 97.

+1

È possibile costruire funtori che imbrogliano, per esempio avendo un costruttore 'Fmap' per fondere le applicazioni' fmap', nascondendole al limite del modulo. Sono per lo più preoccupato che eventuali imbrogli di questo tipo possano essere mescolati con un GADT in qualche modo sottilmente pericoloso. – dfeuer

+1

Qualcosa come 'data Fmap f a dove Fmap :: (x -> a) -> f x -> Fmap f a'? Ciò non causerebbe alcun problema. È solo un problema quando si tratta in modo specifico di 'Void' in modo diverso (come nel mio esempio). Non mi aspetto che qualcuno possa farlo per errore. Finché tutto soddisfa le leggi del fonorizzatore dovrebbe andare bene. – cchalmers