Très bonne question ! Il faut bien comprendre que le compilateur GHC n'a AUCUNE notion de type raffinés. Pour lui, un Integer reste un Integer, i.e. un entier de taille infinie.
La librairie refined propose simplement un type Refined qui, de manière simpliste, peut être vue comme un objet ne contenant qu'un seul membre privé de type Integer. Comme il n'y a pas de conversion implicite en Haskell, un Integer (comme 5) ne peut pas être vu comme un Refined, il faut le convertir.
On ne peut pas obtenir d'erreur à l’exécution : soit on utilise refineTH lors de la compilation (et l'erreur sera le cas échéant à la compilation), soit on utilise refine à l’exécution et on est forcé de tester le résultat de la conversion.
Ce type Refined est très limité, il n'accepte pas d'autres opérations (comme l'addition, la soustraction, ...) Ainsi il ne peut être vraiment pratique que pour des valeurs constante. On peut imaginer trois exemples de fonction division :
Celle qui ne gère pas l'erreur et qui va planter à l’exécution :
myDiv::Integer->Integer->IntegermyDivab=divab
Celle qui gère l'erreur et renvoie une valeur représentant la réussite ou l'échec, ce qui force l'appelant de la fonction à gérer le cas sur le résultat:
myDiv::Integer->Integer->MaybeIntegermyDiv_0=NothingmyDivab=Just(divab)-- plus tardcasemyDivabofJustres->putStrLn("C'est bon: "++showres)Nothing->putStrLn"Erreur"
L'approche qui ne peut pas échouer, mais force l'appelant à fournir le bon type "raffiné" :
myDiv::Integer->Refined(Or(LessThan0)(GreaterThan0))Integer->IntegermyDivab=divab-- plus tardcaserefinebofLefterreur->putStrLn"Erreur"JustbRefined->putStrLn("C'est bon"++show(myDivabRefined))
Dans ce dernier cas que je préfère, le type de la fonction est bien plus informatif, et le code de la fonction est plus simple. Et si tu possède déjà un type raffiné, tu peux t'affranchir des tests et ainsi il n'y a pas de coût à l’exécution :
Qui n'aura pas plus de coût à l’exécution que l'appel à la fonction div.
On pourrait cependant imaginer une libraire d'un peu plus haut niveau capable d'opérations arithmétiques entre les types. C'est le cas par exemple de Liquid Haskell qui est un analyseur statique de code Haskell. Par exemple, il peut prouver que div x (abs x + 1) est correct car :
quelque que soit x, abs x >= 0
abs x + 1 >= 1
div x (abs x + 1) est défini (car point précédant) et différent de 0.
Cet exemple est assez simple et on pourrait facilement réaliser un type Haskell qui permet ces opérations. Cependant des cas plus complexes ne sont pas possible à exprimer dans le système de type et demandent donc un outil externe, comme Liquid Haskell.
[^] # Re: Intérêt de refineTH ?
Posté par Guillaum (site web personnel) . En réponse au journal Portage de TapTempo en Haskell. Évalué à 4.
Très bonne question ! Il faut bien comprendre que le compilateur GHC n'a AUCUNE notion de type raffinés. Pour lui, un
Integerreste unInteger, i.e. un entier de taille infinie.La librairie refined propose simplement un type
Refinedqui, de manière simpliste, peut être vue comme un objet ne contenant qu'un seul membre privé de typeInteger. Comme il n'y a pas de conversion implicite en Haskell, unInteger(comme5) ne peut pas être vu comme unRefined, il faut le convertir.On ne peut pas obtenir d'erreur à l’exécution : soit on utilise
refineTHlors de la compilation (et l'erreur sera le cas échéant à la compilation), soit on utiliserefineà l’exécution et on est forcé de tester le résultat de la conversion.Ce type
Refinedest très limité, il n'accepte pas d'autres opérations (comme l'addition, la soustraction, ...) Ainsi il ne peut être vraiment pratique que pour des valeurs constante. On peut imaginer trois exemples de fonction division :Dans ce dernier cas que je préfère, le type de la fonction est bien plus informatif, et le code de la fonction est plus simple. Et si tu possède déjà un type raffiné, tu peux t'affranchir des tests et ainsi il n'y a pas de coût à l’exécution :
Qui n'aura pas plus de coût à l’exécution que l'appel à la fonction
div.On pourrait cependant imaginer une libraire d'un peu plus haut niveau capable d'opérations arithmétiques entre les types. C'est le cas par exemple de Liquid Haskell qui est un analyseur statique de code Haskell. Par exemple, il peut prouver que
div x (abs x + 1)est correct car :x,abs x >= 0abs x + 1 >= 1div x (abs x + 1)est défini (car point précédant) et différent de 0.Cet exemple est assez simple et on pourrait facilement réaliser un type Haskell qui permet ces opérations. Cependant des cas plus complexes ne sont pas possible à exprimer dans le système de type et demandent donc un outil externe, comme Liquid Haskell.