Tout comme le titre l'indique: quelles garanties existe-t-il pour qu'une unité de retour de fonction Haskell soit évaluée? On pourrait penser qu'il n'est pas nécessaire d'exécuter une sorte d'évaluation dans un tel cas, le compilateur pourrait remplacer tous ces appels par une ()
valeur immédiate à moins que des demandes explicites de rigueur ne soient présentes, auquel cas le code pourrait avoir à décider s'il devrait retour ()
ou bas.
J'ai expérimenté cela dans GHCi, et il semble que le contraire se produise, c'est-à-dire qu'une telle fonction semble être évaluée. Un exemple très primitif serait
f :: a -> ()
f _ = undefined
L'évaluation f 1
renvoie une erreur en raison de la présence de undefined
, donc une évaluation se produit définitivement. Cependant, la profondeur de l'évaluation n'est pas claire; parfois, il semble aller aussi loin qu'il est nécessaire d'évaluer tous les appels aux fonctions renvoyées ()
. Exemple:
g :: [a] -> ()
g [] = ()
g (_:xs) = g xs
Ce code boucle indéfiniment s'il est présenté avec g (let x = 1:x in x)
. Mais alors
f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()
peut être utilisé pour montrer que les h (f 1)
retours ()
, dans ce cas, toutes les sous-expressions unitaires ne sont pas évaluées. Quelle est la règle générale ici?
ETA: bien sûr, je connais la paresse. Je demande ce qui empêche les rédacteurs du compilateur de rendre ce cas particulier encore plus paresseux que d'habitude.
ETA2: résumé des exemples: GHC semble traiter ()
exactement comme tout autre type, c'est-à-dire comme s'il y avait une question sur la valeur régulière occupant le type qui devrait être renvoyée par une fonction. Le fait qu'il n'existe qu'une seule de ces valeurs ne semble pas être (ab) utilisé par les algorithmes d'optimisation.
ETA3: quand je dis Haskell, je veux dire Haskell tel que défini par le rapport, pas Haskell-le-H-dans-GHC. Semble être une hypothèse qui n'est pas partagée aussi largement que je l'imaginais (qui était «à 100% des lecteurs»), ou j'aurais probablement pu formuler une question plus claire. Néanmoins, je regrette d'avoir changé le titre de la question, car elle demandait à l'origine quelles garanties existe pour une telle fonction appelée.
ETA4: il semblerait que cette question ait suivi son cours, et je la considère comme sans réponse. (Je cherchais une fonction de `` question proche '' mais je n'ai trouvé que `` répondre à votre propre question '' et comme il ne peut pas être répondu, je n'ai pas suivi cette voie.) Personne n'a soulevé quoi que ce soit du rapport qui déciderait de toute façon , que je suis tenté d'interpréter comme une réponse forte mais non définitive «aucune garantie pour la langue en tant que telle». Tout ce que nous savons, c'est que la mise en œuvre actuelle du GHC n'ignorera pas l'évaluation d'une telle fonction.
J'ai rencontré le problème réel lors du portage d'une application OCaml vers Haskell. L'application d'origine avait une structure mutuellement récursive de nombreux types, et le code a déclaré un certain nombre de fonctions appelées assert_structureN_is_correct
N dans 1..6 ou 7, chacune renvoyant unité si la structure était effectivement correcte et lançant une exception si elle n'était pas . De plus, ces fonctions se sont appelées l'une l'autre en décomposant les conditions d'exactitude. À Haskell, cela est mieux géré en utilisant la Either String
monade, donc je l'ai transcrite de cette façon, mais la question en tant que problème théorique est restée. Merci pour toutes les contributions et réponses.
la source
h1::()->() ; h1 () = ()
eth2::()->() ; h2 _ = ()
. Exécutez les deuxh1 (f 1)
eth2 (f 1)
, et voyez que seul le premier l'exige(f 1)
.f 1
est "remplacé" parundefined
dans tous les cas.... -> ()
peut 1) se terminer et retourner()
, 2) se terminer avec une erreur d'exception / d'exécution et ne rien retourner, ou 3) diverger (récursion infinie). GHC n'optimise pas le code en supposant que 1) peut se produire: s'ilf 1
est demandé, il ne saute pas son évaluation et son retour()
. La sémantique de Haskell consiste à l'évaluer et à voir ce qui se passe entre 1,2,3.()
(ni le type ni la valeur) dans cette question. Toutes les mêmes observations se produisent si vous remplacez() :: ()
par, disons,0 :: Int
partout. Ce ne sont que de vieilles conséquences ennuyeuses d'une évaluation paresseuse.()
type,()
etundefined
.Réponses:
Vous semblez provenir de l'hypothèse que le type
()
n'a qu'une seule valeur possible()
, et vous vous attendez donc à ce que tout appel de fonction renvoyant une valeur de type()
soit automatiquement supposé produire effectivement la valeur()
.Ce n'est pas ainsi que Haskell fonctionne. Chaque type a une valeur de plus dans Haskell, à savoir aucune valeur, erreur ou soi-disant «bas», codé par
undefined
. Ainsi, une évaluation est en cours:est équivalent au langage Core
ou même (*)
et Core de
_Case
se forçaient :La valeur est forcée à une forme normale de tête faible. Cela fait partie de la définition du langage.
Haskell n'est pas un langage de programmation déclaratif .
(*)
print x = putStr (show x)
etshow () = "()"
, ainsi l'show
appel peut être compilé complètement.La valeur est en effet connue à l'avance comme
()
, et même la valeur deshow ()
est connue à l'avance comme"()"
. Toujours la sémantique Haskell accepté exigent que la valeur(f 1)
est forcée à la tête forme normale faible avant d'imprimer celle connue dans la chaîne d'avance,"()"
.modifier: Réfléchissez
concat (repeat [])
. Cela devrait-il être[]
ou devrait-il être une boucle infinie?La réponse d'un "langage déclaratif" à cela est très probablement
[]
. La réponse de Haskell est, la boucle infinie .La paresse est "la programmation déclarative du pauvre", mais ce n'est toujours pas la vraie chose .
edit2 :
print $ h (f 1) == _Case (h (f 1)) _Of () -> print ()
et seulementh
est forcé, nonf
; et produire sa réponseh
ne doit rien forcer, selon sa définitionh _ = ()
.remarques d'adieu: La paresse peut avoir une raison d'être, mais ce n'est pas sa définition. La paresse est ce qu'elle est. Il est défini comme toutes les valeurs étant initialement des thunks qui sont forcées à WHNF en fonction des demandes en provenance
main
. S'il aide à éviter le fond dans un certain cas spécifique en fonction de ses circonstances spécifiques, il le fait. Sinon, non. C'est tout.Cela aide à le mettre en œuvre vous-même, dans votre langue préférée, pour vous en faire une idée. Mais nous pouvons également retracer l'évaluation de toute expression en nommant soigneusement toutes les valeurs intermédiaires à mesure qu'elles se produisent.
D'après le rapport , nous avons
puis
et avec
il continue
Maintenant, la section 3.17.3 Sémantique formelle de correspondance de modèles du rapport dit
et le cas
(r)
dans la figure 3.2 états()
est le constructeur de données de l'arité 0, il est donc le même queet le résultat global de l'évaluation est donc
⊥
.la source
case
Core en fait, et j'ignorais le trou béant. :) J'ai édité pour mentionner le Core.show
invoqué parprint
? Quelque chose commeshow x = case x of () -> "()"
case
Core, pas à Haskell lui-même. Haskell est traduit en Core, qui a un forçagecase
, AFAIK. Vous avez raison de direcase
qu'Haskell ne force pas par lui-même. Je pourrais écrire quelque chose dans Scheme ou ML (si je pouvais écrire ML), ou pseudocode.print
force autant que nécessaire pour imprimer. il ne regarde pas le type, les types ont disparu, effacés, au moment où le programme s'exécute, le sous-programme d'impression correct est déjà choisi et compilé, selon le type, au moment de la compilation; ce sous-programme va toujours forcer sa valeur d'entrée à WHNF au moment de l'exécution, et s'il n'était pas défini, cela entraînera une erreur.