J'apprends la programmation en ML (OCaml), et plus tôt j'ai posé des questions sur les fonctions ML de type'a -> 'b
. Maintenant, j'expérimente un peu avec les fonctions de type 'a list -> 'b list
. Il y a quelques exemples simples évidents:
let rec loop l = loop l
let return_empty l = []
let rec loop_if_not_empty = function [] -> []
| l -> loop_if_not_empty l
Ce que je ne peux pas comprendre, c'est comment créer une fonction qui fait autre chose que retourner la liste ou la boucle vide (sans utiliser de fonction de bibliothèque). Cela peut-il être fait? Existe-t-il un moyen de renvoyer des listes non vides?
Edit: Oui, si j'ai une fonction de type 'a -> 'b
, alors je peux en faire une autre, ou une fonction de type 'a list -> 'b list
, mais ce que je me demande ici, c'est comment faire la première.
programming-languages
typing
functional-programming
Gilles 'SO- arrête d'être méchant'
la source
la source
Réponses:
Eh bien, quelque chose connu sous le nom de paramétricité nous dit que si nous considérons le sous-ensemble pur de ML (c'est-à-dire pas de récursion infinie,
ref
et toutes ces choses étranges), il n'y a aucun moyen de définir une fonction avec ce type autre que celle renvoyant le vide liste.Tout a commencé avec l'article de Wadler « Theorems for free! ". Cet article, en gros, nous dit deux choses:
ref
et tout ce truc bizarre) remplit ces conditions.De l'paramétricité Théorème nous savons que si nous avons une fonction
f : 'a list -> 'b list
, puis pour tous'a
,'b
,'c
,'d
et pour toutes les fonctionsg : 'a -> 'c
,h : 'b -> 'd
nous avons:(Remarque,
f
à gauche a le type'a list -> 'b list
etf
à droite est'c list -> 'd list
.)Nous sommes libres de choisir ce que
g
nous voulons, alors laissez'a = 'c
etg = id
. Or depuismap id = id
(facile à prouver par récurrence sur la définition demap
), on a:Maintenant, laissez
'b = 'd = bool
eth = not
. Supposons que pour certains,zs : bool list
cela arrivef zs ≠ [] : bool list
. Il est clair quemap not ∘ f = f
cela ne tient pas , carSi le premier élément de la liste à droite est
true
, alors à gauche le premier élément estfalse
et vice versa!Cela signifie que notre hypothèse est fausse et
f zs = []
. Avons-nous fini? Non.Nous avons supposé que
'b
estbool
. Nous avons montré que lorsquef
est invoqué avec typef : 'a list -> bool list
pour any'a
,f
doit toujours renvoyer la liste vide. Se peut-il que lorsque nous appelonsf
,f : 'a list -> unit list
cela renvoie quelque chose de différent? Notre intuition nous dit que c'est un non-sens: nous ne pouvons tout simplement pas écrire en ML pur une fonction qui retourne toujours la liste vide quand nous voulons qu'elle nous donne une liste de booléens et pourrait retourner une liste non vide sinon! Mais ce n'est pas une preuve.Ce que nous voulons dire, c'est que
f
c'est uniforme : s'il retourne toujours la liste vide pourbool list
, alors il doit retourner la liste vide pourunit list
et, en général, tout'a list
. C'est exactement le sujet du deuxième point de la liste à puces au début de ma réponse.Le papier nous dit qu'en ML
f
doit prendre des valeurs liées à celles qui sont liées . Je n'entre pas dans les détails des relations, il suffit de dire que les listes sont liées si et seulement si elles ont des longueurs égales et leurs éléments sont liés par paires (c'est-à-dire,[x_1, x_2, ..., x_m]
et[y_1, y_2, ..., y_n]
sont liés si et seulement sim = n
etx_1
sont liés ày_1
etx_2
est lié ày_2
et ainsi de suite). Et la partie amusante est, dans notre cas, puisqu'ellef
est polymorphe, on peut définir n'importe quelle relation sur les éléments des listes!Choisissons
'a
-en'b
et regardonsf : 'a list -> 'b list
. Regardez maintenantf : 'a list -> bool list
; nous avons déjà montré que dans ce casf
retourne toujours la liste vide. Nous postulons maintenant que tous les éléments de'a
sont liés à eux-mêmes (rappelez-vous, nous pouvons choisir n'importe quelle relation que nous voulons), cela implique que toutzs : 'a list
est lié à lui-même. Comme nous le savons,f
prend des valeurs liées à celles liées, cela signifie quef zs : 'b list
est lié àf zs : bool list
, mais la deuxième liste a une longueur égale à zéro, et puisque la première est liée à elle, elle est également vide.Pour être complet, je mentionnerai qu'il y a une section sur l'impact de la récursivité générale (non-résiliation possible) dans l'article original de Wadler, et il y a aussi un article explorant les théorèmes libres en présence de
seq
.la source
g
eth
dans ce cas) aller directement avec des relations générales sur mesure…Revenons à des objets plus simples: vous ne pouvez pas construire un objet de type approprié
'a
car cela signifierait que cet objetx
peut être utilisé partout où il'a
conviendrait. Et cela signifie partout: comme un entier, un tableau, même une fonction. Par exemple, cela signifierait que vous pouvez faire des choses commex+2
,x.(1)
et(x 5)
. Les types existent exactement pour éviter cela.C'est la même idée qui s'applique à une fonction de type
'a -> 'b
, mais il existe des cas où ce type peut exister: lorsque la fonction ne retourne jamais un objet de type'b
: lors de la boucle ou de la levée d'une exception.Cela s'applique également aux fonctions qui renvoient une liste. Si votre fonction est de type
t -> 'b list
et que vous générez un objet de typet
et l'appliquez à cette fonction, cela signifie que si vous accédez avec succès à un élément de cette liste, vous accéderez à un objet de tous types. Vous ne pouvez donc accéder à aucun élément de la liste: la liste est vide ou ... il n'y a pas de liste.Cependant, le type
'a list -> 'b list
apparaît dans les exercices habituels, mais ce n'est que lorsque vous avez déjà une fonction de type'a -> 'b
:Mais vous connaissez probablement celui-ci.
la source
'a -> 'b
ou'a list -> 'b list
, mais ce n'est pas une observation si intéressante. En fait, je vais modifier la question pour bien faire comprendre que ce n'est pas ce que les jeunes élèves apprennent à propos des programmes d'apprentissage.f : 'a list -> 'b list
et det
telle sorte que ,f t <> []
alors ce programme tapera-chèque , mais peut faire façon pire que soulever une exception:let y = List.hd (f t) in (y y) (y + y.(0) + y.(0).(0))
.Théorème de paramétrie des «Théorèmes gratuits!» le papier nous dit que les termes ML ont une propriété très spéciale: si nous considérons le type d'un terme comme une relation sur des valeurs de ce type, alors la valeur de ce terme sera liée à lui-même. Voici comment afficher les types en tant que relations:
'a -> 'b
correspond à la relation définie en disant que deux fonctions sont liées si elles prennent des valeurs liées à des valeurs liées (en supposant'a
et'b
correspondant à certaines relations).'a list
correspond à la relation définie en disant que deux listes sont liées si elles ont la même longueur et si leurs éléments correspondants sont liés (en supposant que cela'a
correspond à une relation).Voici un exemple. Supposons que nous ayons un termeUNE1 et UNE2 , choisissez absolument n'importe quelle relation UNE entre les éléments de ce type, et si nous prenons tout une1:UNE1 et une2:UNE2 , tels qu'ils sont liés selon UNE , puis fooune1 et fooune2 sera également lié selon UNE :
foo : 'a -> 'a
. Le théorème de paramétrie dit que celafoo
est lié à lui-même. Cela signifie que nous pouvons choisir deux types, par exemple,Maintenant, si nous prenons la relationUNE ne pas être une relation arbitraire, mais une fonction F:UNE1→UNE2 , ce qui précède devient:
ou, en d'autres termes:
ce qui est exactement le théorème libre pour la
id
fonction:f . id = id . f
.Si vous effectuez des étapes similaires pour votre fonctionUNE1 et UNE2 , toute relation UNE entre leurs éléments, deux types quelconques B1 et B2 , toute relation B entre leurs éléments, puis prendre deux listes quelconques, d’abord constituées d’éléments de UNE1 , deuxième composé d'éléments de UNE2 , appliquez votre fonction aux deux listes (obtenir une liste de B1 dans le premier cas et une liste de B2 dans le second), et les résultats seront liés, si les entrées étaient liées.
foo : 'a list -> 'b list
, vous obtiendrez que vous pouvez choisir deux typesMaintenant, nous utilisons cela pour prouver que pour deux types quelconques
A
etB
la fonctionfoo
renvoie une liste vide pour toute entréeas : A list
.A
et laisseØ
,B
etas
est lié à lui-même (comme nous avons choisi la relation d'identitéA
),foo as : Ø list
est donc lié àfoo as : B list
.Ø
type.Par conséquent, pour tout
A
,B
etas : A list
nous avons que celafoo as : B list
doit être une liste vide.la source