2016-12-30 1 views
4

Je suis en train de programmer une bibliothèque de type dépendante dans Haskell. En utilisant le profilage sur mon exécutable test que je vois quelque chose comme:Coût d'exécution de la programmation de type dépendante avec GHC

commutativity' Math 1189 4022787186 29.1 27.2 29.1 27.2 

commutativity' est essentiellement (récursive) la preuve de l'addition entière propriété de commutativité pour les entiers de niveau de type. Il est défini ceci:

commutativity' :: SNat n -> SNat m -> Plus (S n) m :~: Plus n (S m) 
commutativity' SZ m = Refl 
commutativity' (SS n) m = gcastWith (commutativity' n m) Refl 

Et puis utilisé dans ma bibliothèque gcastWith pour prouver l'équivalence des différents types. Donc ... 29% de mon temps d'exécution est dépensé pour quelque chose de complètement inutile, puisque la vérification de type se produit au moment de la compilation.

J'avais naïvement supposé que cela n'arriverait pas.

Y at-il quelque chose que je peux faire pour optimiser ces appels inutiles?

+8

Ceci est le coût de ne pas utiliser une langue totale. Tapez la sécurité repose sur l'évaluation des preuves à la forme canonique. Dans le total des langues, les épreuves peuvent être approuvées sans évaluation et ainsi effacées lors de l'exécution. Vous pouvez prendre la touche de confiance et utiliser 'unsafeCoerce', une fois que vous avez vérifié que votre programme dépasse le typechecker. – pigworker

+3

Voir section 4.4.4 "Exécution d'épreuves" de la thèse de Richard A. Eisenberg https://arxiv.org/abs/1610.07978 – danidiaz

+3

Je rêve de GHC intégrant un vérificateur de terminaison (résumé), et d'optimiser/réécrire automatiquement les "preuves" en utilisant le zéro coercitions-coût. Ce serait vraiment gentil. – chi

Répondre

1

Si vous êtes très que le terme de preuve, vous pouvez utiliser quelque chose se termine comme

unsafeProof :: proof -> proof 
unsafeProof _ = unsafeCoerce() 

someFunction :: forall n m. ... 
someFunction = case unsafeProof myProof :: Plus (S n) m :~: Plus n (S m) of 
    Refl -> ... 

Ce doit être utilisé que sur les types ayant un seul pas de constructeur de paramètres, par exemple Refl pour a :~: b. Sinon, votre programme va probablement se bloquer ou se comporter bizarrement. Caveat emptor!

Une variante plus sûre (mais toujours dangereux!) Pourrait être

unsafeProof :: a :~: b -> a :~: b 
unsafeProof _ = unsafeCoerce() 

Notez que vous pouvez toujours planter le programme avec cela, si vous passez en bas à elle.

J'espère qu'un jour, GHC effectuera cette optimisation de manière sûre et automatique, assurant une terminaison par analyse statique.

+0

Hmmm ... Je suppose que la demande pour cette analyse de terminaison va être beaucoup plus grande après que Richard Eisenberg ait implémenté des types dépendants. – Alec

+0

FWIW, l'idiome que je vois plus souvent dans les bibliothèques 'base' et autres ([exemple] (https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Data.Typeable.html#eqT)) est 'unsafeCoerce Refl', pas' unsafeCoerce() '.Je suppose qu'il est plus résilient à un changement de représentation de:: –

+1

@BenjaminHodgson Il clarifie mieux l'intention, oui. Pour le cas général, cependant, 'unsafeProof :: proof -> preuve' il n'y a pas de" meilleur "constructeur que nous pouvons utiliser, donc j'ai utilisé'() 'comme l'élément canonique du type final. – chi