2017-01-08 2 views
0

Je veux savoir quel est le type exact de fonctions primitives dans les langages de programmation fonctionnels paresseux comme Haskell.Type de fonctions primitives dans l'évaluation paresseuse

Supposons que les thunks soient évalués sur des objets de forme normale de tête faible. Alors, quel devrait être le type de fonctions primitives dans des langages stricts comme C?

Mes suppositions sont:

primitive1 : (thunk, thunk, ...) -> thunk 
primitive2 : (thunk, thunk, ...) -> object 

Je pense que les fonctions primitives doivent être transmises thunks comme arguments parce qu'ils ne peuvent pas besoin certains d'entre eux. Mais, je ne sais pas s'ils doivent renvoyer un thunk ou objet évalué tandis que le dernier doit être enveloppé avec une fonction comme celle ci-dessous pour le rendre paresseux.

lazy : ((thunk, thunk, ...) -> object) -> ((thunk, thunk, ...) -> thunk) 
+1

Eh bien, il n'y a pas de réponse unique à cette question, car cela dépend de la mise en œuvre. En ghc, les opérations primitives ont des types primitifs, c'est-à-dire, les mêmes types qu'en C. Vous avez une opération séparée pour envelopper et déballer les types primitifs dans les thunk. – augustss

+0

@augustss Que diriez-vous des expressions si et cas? Leurs primitifs doivent être passés directement aux thunks, je suppose. Ou sont-ils au curry? – raviqqe

+0

Une expression if est traduite en une expression case. Et il n'y a pas de primitive pour le cas, il est traduit en code machine (abstrait) qui dépend du type. Simplifié, d'abord le scrutinee est évalué à WHNF, puis vous extrayez son numéro de constructeur et l'employez comme décalage dans une table de saut. Le code sur lequel vous sautez déballera les champs du constructeur et procédera à l'évaluation. – augustss

Répondre

1

En GHC, une fonction primitive Haskell (parfois appelé PrimOp) est appelé avec un mélange de pointeurs (pour "objets tas") et unboxed types (y compris ints style C, double vitrage, etc.) . Le système de type Haskell garantit que PrimOps obtient toujours le nombre, l'ordre et les types des pointeurs et des valeurs non mises en boîte qu'ils attendent. Il est important de noter, cependant, que lorsqu'une primitive attend un pointeur vers un type spécifique d'objet en tas, par exemple un String, il attend un pointeur vers un objet tas qui peut soit un constructeur liste (depuis un Haskell String est une liste de caractères) ou un thunk qui peut être évalué par un constructeur de liste. Ainsi, le type "strict" d'un PrimOp Haskell ne différencie pas entre les thunks et les non-thunks. S'il y avait une fonction primitive à obtenir la longueur d'une liste, par exemple (il n'y a pas), il aurait probablement le type, en utilisant votre notation, de:

primitiveLength : (list_object) -> unboxed_int 

où le list_object serait un pointeur vers soit une liste constructeur ou un thunk qui peut générer un constructeur de liste.

C'est vraiment la seule approche raisonnable. Un PrimOp ne peut pas contrôler si son argument est toujours un thunk ou a été partiellement (ou entièrement!) Évalué par un calcul précédent, il doit donc être prêt à accepter l'un ou l'autre.

De même, si un Haskell PrimOp retours un objet tas, cet objet pourrait techniquement être un thunk ou non thunk, et le choix aurait aucun effet sur la signature de type « stricte » de la primitive.

En pratique, il n'est pas très utile pour un PrimOp de retourner un thunk. Dans un langage paresseux, le fait que la primitive soit appelée implique que sa valeur de retour est nécessaire. Si cela retourne un thunk, ce thunk devra être évalué tout de suite, alors pourquoi retourner un thunk? En passant, il n'y a rien de vraiment spécifique à PrimOps: les fonctions Haskell définies par l'utilisateur sont aussi appelées avec un mélange de pointeurs et de types sans boîte, et elles ne retournent jamais de thunks.

+0

Est-ce que la création et l'évaluation de thunk sont permises dans les fonctions primitives? – raviqqe

+0

Les primitives * peuvent * évaluer les thunks, mais peu le font. Par exemple, 'newArray #' peut prendre un thunk (comme valeur initiale pour les éléments du tableau), mais il ne l'évalue pas - il remplit simplement le tableau avec des pointeurs vers le thunk. Je suppose que "raise #" est un exemple de primitive qui accepte (et évalue) un thunk. Puisque l'évaluation d'un thunk peut créer de nouvelles thunk, cela signifie que 'raise # 'est capable de créer implicitement des thunk. Je ne connais pas de primitive qui crée explicitement un thunk, mais je ne vois pas pourquoi on ne peut pas, puisque les primitifs peuvent créer des objets tas, et un thunk est juste un autre objet tas. –

+0

@raviqqe, un primop qui peut créer un thunk est 'tryTakeMVar # :: MVar # s un -> State # s -> (# State # s, Int #, un #)'. Si le 'MVar' est vide, il retourne avec une valeur indéfinie (un thunk qui produira une erreur s'il est forcé). La même chose vaut pour 'tryReadMVar #'. Ce n'est pas documenté, mais il en va de même pour 'deRefWeak #' si quelqu'un essaie de déréférencer un pointeur faible dont la clé a été collectée. – dfeuer