Kryds type - Intersection type

I typeteori kan en krydstype tildeles værdier, der kan tildeles både typen og typen . Denne værdi kan gives krydstypen i et krydstypesystem . Generelt, hvis værdiområderne for to typer overlapper hinanden, kan en værdi, der tilhører skæringspunktet mellem de to områder, tildeles skæringstypen for disse to typer. En sådan værdi kan sikkert overføres som argument til funktioner, der forventer en af de to typer. For eksempel implementerer klassen både og og grænsefladerne i Java . Derfor kan et objekt af type sikkert overføres til funktioner, der forventer et argument af typen og til funktioner, der forventer et argument af typen . BooleanSerializableComparableBooleanSerializableComparable

Krydsstyper er sammensatte datatyper . I lighed med produkttyper bruges de til at tildele flere typer til et objekt. Produkttyper tildeles dog tupler , så hvert tupelelement tildeles en bestemt produkttypekomponent. Til sammenligning er underliggende objekter af skæringspunkter ikke nødvendigvis sammensatte. En begrænset form for krydstyper er forfiningstyper .

Krydstyper er nyttige til at beskrive overbelastede funktioner . Hvis f.eks. Funktionstypen tager et tal som et argument og returnerer et tal, og det er funktionstypen, der tager en streng som et argument og returnerer en streng, kan skæringspunktet mellem disse to typer bruges til at beskrive ( overbelastede) funktioner, der gør det ene eller det andet, baseret på hvilken type input de får. number => numberstring => string

Moderne programmeringssprog, herunder Ceylon , Flow, Java , Scala , TypeScript og Whiley (se sammenligning af sprog med krydsetyper ), bruger skæringstyper til at kombinere grænsefladespecifikationer og til at udtrykke ad hoc polymorfisme . Som supplement til parametrisk polymorfisme kan krydsetyper bruges til at undgå klassehierarkiforurening fra tværgående bekymringer og reducere kedelpladekode , som vist i TypeScript-eksemplet nedenfor.

Den form teoretisk studie af kryds typer omtales som den disciplin krydset typen . Bemærkelsesværdigt kan programafslutning præcist karakteriseres ved hjælp af krydsstyper.

TypeScript -eksempel

TypeScript understøtter skæringstyper, forbedrer typesystemets udtryksfuldhed og reducerer potentiel klassehierarkistørrelse, demonstreret som følger.

Følgende program kode definerer klasserne Chicken, Cowog RandomNumberGeneratorsom hver har en fremgangsmåde producereturnere et formål med begge typer Egg, Milkeller number. Derudover de funktioner eatEggog drinkMilkkræver argumenter af type Eggog Milkhhv.

class Egg { private kind: "Egg" }
class Milk { private kind: "Milk" }

//produces eggs
class Chicken { produce() { return new Egg(); } }

//produces milk
class Cow { produce() { return new Milk(); } }

//produces a random number
class RandomNumberGenerator { produce() { return Math.random(); } }

//requires an egg
function eatEgg(egg: Egg) {
    return "I ate an egg.";
}

//requires milk
function drinkMilk(milk: Milk) {
    return "I drank some milk.";
}

Den følgende programkode definerer den ad hoc polymorfe funktion, animalToFoodder påkalder medlemsfunktionen producefor det givne objekt animal. Funktionen animalToFoodhar to typeanmærkninger, nemlig og , forbundet via krydsetypekonstruktoren . Specifikt, når det anvendes på et argument af typen, returnerer et objekt af typen type , og når det anvendes på et argument af typen, returneres et objekt af typen . Ideelt set burde det ikke være anvendeligt på ethvert objekt, der (muligvis ved en tilfældighed) har en metode. ((_: Chicken) => Egg)((_: Cow) => Milk)&animalToFoodChickenEggCowMilkanimalToFoodproduce

//given a chicken, produces an egg; given a cow, produces milk
let animalToFood: ((_: Chicken) => Egg) & ((_: Cow) => Milk) =
    function (animal: any) {
        return animal.produce();
    };

Endelig demonstrerer følgende programkode typen sikker brug af ovenstående definitioner.

var chicken = new Chicken();
var cow = new Cow();
var randomNumberGenerator = new RandomNumberGenerator();

console.log(chicken.produce()); //Egg { }
console.log(cow.produce()); //Milk { }
console.log(randomNumberGenerator.produce()); //0.2626353555444987

console.log(animalToFood(chicken)); //Egg { }
console.log(animalToFood(cow)); //Milk { }
//console.log(animalToFood(randomNumberGenerator)); //ERROR: Argument of type 'RandomNumberGenerator' is not assignable to parameter of type 'Cow'

console.log(eatEgg(animalToFood(chicken))); //I ate an egg.
//console.log(eatEgg(animalToFood(cow))); //ERROR: Argument of type 'Milk' is not assignable to parameter of type 'Egg'
console.log(drinkMilk(animalToFood(cow))); //I drank some milk.
//console.log(drinkMilk(animalToFood(chicken))); //ERROR: Argument of type 'Egg' is not assignable to parameter of type 'Milk'

Ovenstående programkode har følgende egenskaber:

  • Linjer 1-3 skabe objekter chicken, cowog randomNumberGeneratori deres respektive type.
  • Linje 5-7 udskrives for de tidligere oprettede objekter de respektive resultater (leveres som kommentarer) ved påkaldelse produce.
  • Linje 9 (hhv. 10) viser typen sikker brug af metoden animalToFoodanvendt på chicken(hhv. cow).
  • Linje 11, hvis den ikke blev kommenteret, ville resultere i en typefejl på kompileringstidspunktet. Selv om gennemførelsen af animalToFoodkunne påberåbe sig producemetode randomNumberGenerator, den typen annotation af animalToFoodtillader det ikke. Dette er i overensstemmelse med den tilsigtede betydning af animalToFood.
  • Linje 13 (hhv. 15) viser, at anvendelse animalToFoodchicken(hhv. cow) Resulterer i et objekt af typen Egg(hhv. Milk).
  • Linje 14 (hhv. 16) viser, at ansøgning animalToFoodom cow(hhv. chicken) Ikke resulterer i et objekt af typen Egg(hhv. Milk). Derfor ville linje 14 (hhv. 16) resultere i en typefejl på kompileringstidspunktet, hvis den ikke kommenterede.

Sammenligning med arv

Ovenstående minimalistiske eksempel kan realiseres ved hjælp af arv , for eksempel ved at udlede klasserne Chickenog Cowfra en basisklasse Animal. I en større indstilling kan dette imidlertid være ufordelagtigt. Indførelse af nye klasser i et klassehierarki er ikke nødvendigvis berettiget til tværgående bekymringer eller måske direkte umuligt, for eksempel ved brug af et eksternt bibliotek. Det er forestillingsværdigt, at ovenstående eksempel kan udvides med følgende klasser:

  • en klasse Horse, der ikke har en producemetode;
  • en klasse, Sheepder har en producemetode, der vender tilbage Wool;
  • en klasse, Pigder har en producemetode, som kun kan bruges én gang, vender tilbage Meat.

Dette kan kræve yderligere klasser (eller grænseflader), der angiver, om en produktionsmetode er tilgængelig, om produktmetoden returnerer mad, og om produktmetoden kan bruges gentagne gange. Samlet set kan dette forurene klassehierarkiet.

Sammenligning med andeskrivning

Ovenstående minimalistiske eksempel viser allerede, at andetypning er mindre egnet til at realisere det givne scenario. Selvom klassen RandomNumberGeneratorindeholder en producemetode, skal objektet randomNumberGeneratorikke være et gyldigt argument for animalToFood. Ovenstående eksempel kan realiseres ved hjælp af andetypning, f.eks. Ved at introducere et nyt felt argumentForAnimalToFoodtil klasserne Chickenog Cowangive, at objekter af tilsvarende type er gyldige argumenter for animalToFood. Dette ville imidlertid ikke kun øge størrelsen af ​​de respektive klasser (især med introduktionen af ​​flere metoder, der ligner animalToFood), men er også en ikke-lokal tilgang mht animalToFood.

Sammenligning med funktionel overbelastning

Ovenstående eksempel kan realiseres ved hjælp af funktionsoverbelastning , for eksempel ved at implementere to metoder og . I TypeScript er en sådan løsning næsten identisk med det angivne eksempel. Andre programmeringssprog, f.eks. Java , kræver forskellige implementeringer af den overbelastede metode. Dette kan føre til enten kodeduplikation eller kogepladekode . animalToFood(animal: Chicken): EgganimalToFood(animal: Cow): Milk

Sammenligning med besøgsmønsteret

Ovenstående eksempel kan realiseres ved hjælp af besøgsmønsteret . Det ville kræve, at hver dyreklasse implementerede en acceptmetode, der accepterer et objekt, der implementerer grænsefladen AnimalVisitor(tilføjelse af ikke-lokal kedelpladekode ). Funktionen animalToFoodville blive realiseret som visitmetoden til en implementering af AnimalVisitor. Desværre ville forbindelsen mellem inputtypen ( Chickeneller Cow) og resultattypen ( Eggeller Milk) være vanskelig at repræsentere.

Begrænsninger

På den ene side, skæringspunkterne typer kan anvendes til lokalt at Påtegn forskellige typer til en funktion uden at indføre nye klasser (eller grænseflader) til klassen hierarkiet. På den anden side kræver denne fremgangsmåde, at alle mulige argumenttyper og resultattyper skal specificeres eksplicit. Hvis en funktions adfærd kan specificeres præcist ved enten en samlet grænseflade, parametrisk polymorfisme eller andetypning , er krydsetypernes verbale karakter ugunstig. Derfor bør krydsningstyper betragtes som komplementære til eksisterende specifikationsmetoder.

Afhængig skæringstype

En afhængig skæringstype , betegnet , er en afhængig type , hvor typen kan afhænge af termvariablen . Navnlig hvis et udtryk har den afhængige vejkryds typen , så udtrykket har både type og typen , hvor er den type, der resulterer fra at erstatte alle forekomster af udtrykket variabel i med udtrykket .

Scala eksempel

Scala understøtter typedeklarationer som objektmedlemmer. Dette gør det muligt for en type af et objektmedlem at afhænge af værdien af ​​et andet medlem, som kaldes en stiafhængig type . For eksempel definerer følgende programtekst en Scala -egenskab Witness, som kan bruges til at implementere singleton -mønsteret .

trait Witness {
  type T
  val value: T {}
}

Ovenstående træk Witnesserklærer medlemmet T, som kan tildeles en type som dets værdi, og medlemmet value, som kan tildeles en værdi af typen T. Den følgende programtekst definerer et objekt booleanWitnesssom forekomst af ovenstående træk Witness. Objektet booleanWitnessdefinerer typen Tsom Booleanog værdien valuesom true. For eksempel udførelse af udskrifter på konsollen. System.out.println(booleanWitness.value)true

object booleanWitness extends Witness {
  type T = Boolean
  val value = true
}

Lad være typen (specifikt en rekordtype ) af objekter, der har et medlem af typen . I ovenstående eksempel kan objektet tildeles den afhængige skæringstype . Begrundelsen er som følger. Objektet har det medlem, der er tildelt typen som dens værdi. Siden er en type, har objektet typen . Derudover har objektet det medlem, der er tildelt værdien af typen . Da værdien af er , har objektet typen . Samlet set har objektet skæringstypen . Derfor præsenterer objektet selvreference som afhængighed og har den afhængige skæringstype . booleanWitnessbooleanWitnessTBooleanBooleanbooleanWitnessbooleanWitnessvaluetrueBooleanbooleanWitness.TBooleanbooleanWitnessbooleanWitnessbooleanWitness

Alternativt kan ovenstående minimalistiske eksempel beskrives ved hjælp af afhængige posttyper . I sammenligning med afhængige skæringspunkter udgør afhængige registreringstyper et strengt mere specialiseret type teoretisk begreb.

Skæringspunkt for en type familie

Et skæringspunkt mellem en typefamilie , betegnet , er en afhængig type , hvor typen kan afhænge af termvariablen . Navnlig hvis et udtryk har den type , så for hver sigt af typen , udtrykket har typen . Denne forestilling kaldes også implicit Pi -type , idet man bemærker, at argumentet ikke holdes på terminsniveau.

Sammenligning af sprog med krydsetyper

Sprog Aktivt udviklet Paradigme (r) Status Funktioner
C# Ja Under diskussion ?
Ceylon Ja Understøttet
  • Skriv forfining
  • Grænsefladesammensætning
  • Subtyping i bredden
F# Ja Under diskussion ?
Flyde Ja Understøttet
  • Skriv forfining
  • Grænsefladesammensætning
Forsythe Ingen Understøttet
  • Funktionstype kryds
  • Distributiv, co- og kontravariant funktionstype undertype
Java Ja Understøttet
  • Skriv forfining
  • Grænsefladesammensætning
  • Subtyping i bredden
PHP Ja Understøttet
  • Kun rene krydsstyper (kan ikke kombineres med Unionstyper)
  • Skriv forfining
  • Grænsefladesammensætning
Scala Ja Understøttet
  • Skriv forfining
  • Egenskabssammensætning
  • Subtyping i bredden
TypeScript Ja Understøttet
  • Tilfældigt kryds
  • Grænsefladesammensætning
  • Subtyping i bredde og dybde
Mens Ja Understøttet ?

Referencer