Existe-t-il un cas d'utilisation pour le type inférieur comme type de paramètre de fonction?

12

Si une fonction a un type de retour ⊥ ( type inférieur ), cela signifie qu'elle ne revient jamais. Il peut par exemple sortir ou lancer, deux situations assez ordinaires.

Vraisemblablement, si une fonction avait un paramètre de type ⊥, elle ne pourrait jamais (en toute sécurité) être appelée. Y a-t-il jamais des raisons de définir une telle fonction?

bdsl
la source

Réponses:

17

L' une des propriétés définissant des type ou vide est qu'il existe une fonction A pour chaque type A . En fait, il existe une telle fonction unique . Il est donc assez raisonnable que cette fonction soit fournie dans le cadre de la bibliothèque standard. Souvent, cela s'appelle quelque chose comme absurd. (Dans les systèmes avec sous-typage, cela peut être géré simplement en ayant un sous-type de chaque type. Ensuite, la conversion implicite est absurd. Une autre approche connexe consiste à définir comme α.α qui peut simplement être instancié à n'importe quel type.)

E+AEthrow:EAf:ABBBabsurd

AabsurdabsurdA

Même s'il n'y a pas beaucoup de raisons d'écrire une telle fonction, elle devrait généralement être autorisée . L'une des raisons est qu'il simplifie les outils / macros de génération de code.

Derek Elkins a quitté SE
la source
Cela signifie donc que quelque chose comme (x ? 3 : throw new Exception())est remplacé à des fins d'analyse par quelque chose de plus (x ? 3 : absurd(throw new Exception()))?
bdsl
α.αabsurdabsurdthrowα.α
6

Pour ajouter à ce qui a été dit sur la fonction, absurd: ⊥ -> aj'ai un exemple concret où cette fonction est réellement utile.

Considérons le type de données Haskell Free f aqui représente une structure arborescente générale avec des fnœuds en forme de feuilles et des feuilles contenant as:

data Free f a = Op (f (Free f a)) | Var a

Ces arbres peuvent être pliés avec la fonction suivante:

fold :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
fold gen alg (Var x) = gen x
fold gen alg (Op x) = alg (fmap (fold gen alg) x)

Brièvement, cette opération se place algaux nœuds et genaux feuilles.

Maintenant au point: toutes les infrastructures de données récursives peuvent être représentées en utilisant un type de données à virgule fixe. Dans Haskell, c'est Fix fet cela peut être défini comme type Fix f = Free f ⊥(c'est-à-dire des arbres avec des fnœuds en forme et sans feuilles à l'extérieur du foncteur f). Traditionnellement, cette structure a également un pli, appelé cata:

cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg x = fold absurd alg x

Ce qui donne une utilisation assez soignée de l'absurde: puisque l'arbre ne peut pas avoir de feuilles (puisque ⊥ n'a pas d'habitants autres que undefined), il n'est jamais possible d'utiliser le genpour ce pli et absurdillustre cela!

J_mie6
la source
2

Le type inférieur est un sous-type de tous les autres types, ce qui peut être extrêmement utile dans la pratique. Par exemple, le type de NULLdans une version théorique sûre de type de C doit être un sous-type de tout autre type de pointeur, sinon vous ne pourriez pas, par exemple, retourner NULLoù a char*était attendu; de la même manière, le type de undefinedcode JavaScript de type sécurisé théorique doit être un sous-type de tous les autres types du langage.

exit()throw()IntIntexit()

TST

Draconis
la source
3
NULLest un type d'unité n'est-ce pas, différent de ⊥ qui est le type vide?
bdsl
Je ne sais pas trop ce que signifie ≺ dans la théorie des types.
bdsl
1
@bdsl L'opérateur courbe ici est "est un sous-type de"; Je ne sais pas si c'est standard, c'est juste ce que mon professeur a utilisé.
Draconis
1
@ gnasher729 Vrai, mais C n'est pas non plus particulièrement sûr pour le type. Je dis que si vous ne pouviez pas simplement convertir un entier en void*, vous auriez besoin d'un type spécifique qui pourrait être utilisé pour n'importe quel type de pointeur.
Draconis
2

Il y a une utilisation à laquelle je peux penser, et c'est quelque chose qui a été considéré comme une amélioration du langage de programmation Swift.

Swift a une maybeMonade, orthographiée Optional<T>ou T?. Il existe de nombreuses façons d'interagir avec.

  • Vous pouvez utiliser le déballage conditionnel comme

    if let nonOptional = someOptional {
        print(nonOptional)
    }
    else {
        print("someOptional was nil")
    }
    
  • Vous pouvez utiliser map, flatMappour transformer les valeurs

  • L'opérateur forcer le déballage ( !, de type (T?) -> T) pour dérouler le contenu avec force, déclenchant sinon un crash
  • L'opérateur coalescent nul ( ??, de type (T?, T) -> T) pour prendre sa valeur ou utiliser une valeur par défaut:

    let someI = Optional(100)
    print(someI ?? 123) => 100 // "left operand is non-nil, unwrap it.
    
    let noneI: Int? = nil
    print(noneI ?? 123) // => 123 // left operand is nil, take right operand, acts like a "default" value
    

Malheureusement, il n'y avait aucun moyen concis de dire "déballer ou jeter une erreur" ou "déballer ou planter avec un message d'erreur personnalisé". Quelque chose comme

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

ne compile pas, car fatalErrora un type () -> Never( ()est Void, le type d'unité de NeverSwift , est le type inférieur de Swift). L'appeler produit Never, ce qui n'est pas compatible avec l' Tattendu comme opérande droit de ??.

Pour tenter d'y remédier, Swift Evolution propsoal SE-0217- L'opérateur «Déballer ou mourir» a été proposé. Il a finalement été rejeté , mais il a suscité l'intérêt de faire de ce Nevertype un sous-type de tous types.

Si a Neverété créé pour être un sous-type de tous types, alors l'exemple précédent sera compilable:

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

car le site d'appel de ??type a (T?, Never) -> T, ce qui serait compatible avec la (T?, T) -> Tsignature de ??.

Alexander - Rétablir Monica
la source
0

Swift a un type "Never" qui semble être tout à fait comme le type du bas: Une fonction déclarée comme retournant Never ne peut jamais retourner, une fonction avec un paramètre de type Never ne peut jamais être appelée.

Ceci est utile en relation avec les protocoles, où il peut y avoir une restriction en raison du système de type du langage qu'une classe doit avoir une certaine fonction, mais sans aucune exigence que cette fonction soit appelée, et aucune exigence quant aux types d'arguments serait.

Pour plus de détails, vous devriez jeter un œil aux nouveaux articles de la liste de diffusion de swift-evolution.

gnasher729
la source
7
"Les nouveaux messages sur la liste de diffusion de swift-evolution" ne sont pas une référence très claire ou stable. N'y a-t-il pas une archive Web de la liste de diffusion?
Derek Elkins a quitté SE