• Nenhum resultado encontrado

[PENDING] Εγχειρίδιο αναφοράς της γλώσσας προγραμματισμού Scala.

N/A
N/A
Protected

Academic year: 2024

Share "Εγχειρίδιο αναφοράς της γλώσσας προγραμματισμού Scala."

Copied!
150
0
0

Texto

(1)

ΠΤΥΧΙΑΚΗ ΕΡΓΑΣΙΑ

‘‘ Εγχειρίδιο αναφοράς της γλώσσας προγραμματισμού Scala ’’

Ιωαννίδου Μαρία

Αρ. μητρώου 834

Επιβλέπων καθηγητής Παπαδημητρίου Αστέριος

Τ.Ε.Ι. ΚΑΒΑΛΑΣ ΣΧΟΛΗ ΔΙΟΙΚΗΣΗΣ ΚΑΙ ΟΙΚΟΝΟΜΙΑΣ

ΤΜΗΜΑ ΔΙΑΧΕΙΡΙΣΗΣ ΠΛΗΡΟΦΟΡΙΩΝ

(2)

The Scala Language Specification

Version 2.7

DRAFT January 13, 2009

Martin Odersky

PROGRAMMING METHODS LABORATORY EPFL

SWITZERLAN

(3)

ΠΕΡΙΕΧΟΜΕΝΑ

1. Λεξικολογική σύνταξη . . . .3

1.1 Identifiers . . . 4

1.2 Newline Characters . . . 5

1.3 Literals . . . 8

1.3.1 Integer Literals . . . 9

1.3.2 Floating Point Literals . . . 9

1.3.3 Boolean Literals . . . 10

1.3.4 Character Literals . . . . . . 10

1.3.5 String Literals . . . 10

1.3.6 Escape Sequences . . . . 12

1.3.7 Symbol literals . . . 12

1.4 Whitespace and Comments . . . 12

1.5 XML mode . . . .. . . 13

2. Identifiers, Names and Scopes . . . .. . . .15

3. Types. . . .17

3.1 Paths . . . 18

3.2 Value Types . . . 18

3.2.1 Singleton Types . . . 19

3.2.2 Type Projection . . . 19

3.2.3 Type Designators . . . 19

3.2.4 Parameterized Types . . . 20

3.2.5 Tuple Types . . . 20

3.2.6 Annotated Types . . . 21

3.2.7 Compound Types . . . .21

3.2.8 Infix Types . . . 22

3.2.9 Function Types . . . .23

3.2.10 Existential Types . . . .23

3.2.11 Primitive Types Defined in Predef . . . 26

3.3 Non-Value Types . . . .26

3.3.1 Method Types . . . . . . 26

3.3.2 PolymorphicMethod Types . . . 27

3.3.3 Type Constructors . . . .27

3.4 Base Types andMember Definitions . . . 28

3.5 Relations between types . . . 29

3.5.1 Type Equivalence . . . 30

3.5.2 Conformance . . . .30

3.6 Type Erasure . . . 33

(4)

4. Basic Declarations and Definitions . . . 35

4.1 Value Declarations and Definitions . . . 35

4.2 Variable Declarations and Definitions . . . .. . . .37

4.3 Type Declarations and Type Aliases . . . . 39

4.4 Type Parameters . . . .. . . .40

4.5 Variance Annotations . . . .. . . 42

4.6 Function Declarations and Definitions . . . . . . 44

4.6.1 By-Name Parameters . . . 45

4.6.2 Repeated Parameters . . . .. . . 45

4.6.3 Procedures . . . .. . . 46

4.6.4 Method Return Type Inference . . . .47

4.7 Import Clauses . . . .. . . . 47

5. Classes and Objects . . . .. . . .49

5.1 Templates . . . . . . 49

5.1.1 Constructor Invocations . . . 51

5.1.2 Class Linearization . . . .. . . .52

5.1.3 ClassMembers . . . . . . . 53

5.1.4 Overriding . . . .. . . . 54

5.1.5 Inheritance Closure . . . .. . . . 55

5.1.6 Early Definitions . . . .. . . 55

5.2 Modifiers . . . . . . . . . 56

5.3 Class Definitions . . . . . . 60

5.3.1 Constructor Definitions . . . . . . .61

5.3.2 Case Classes . . . . . . . 63

5.3.3 Traits . . . .. . . 64

5.4 Object Definitions . . . .. . . .66

6. Expressions. . . 68

6.1 Expression Typing . . . 69

6.2 Literals . . . 69

6.3 The Null Value . . . 69

6.4 Designators . . . 70

6.5 This and Super . . . 71

6.6 Function Applications . . . 72

6.7 Method Values . . . 73

6.8 Type Applications . . . 73

6.9 Tuples . . . 73

6.10 Instance Creation Expressions . . . 74

6.11 Blocks . . . 74

6.12 Prefix, Infix, and Postfix Operations . . . 75

6.12.1 Prefix Operations . . . 75

(5)

6.12.3 Infix Operations . . . 75

6.12.4 Assignment Operators . . . 75

6.13 Typed Expressions . . . 76

6.14 Annotated Expressions . . . 76

6.15 Assignments . . . 76

6.16 Conditional Expressions . . . 78

6.17 While Loop Expressions . . . 78

6.18 Do Loop Expressions . . . 78

6.19 For-Comprehensions . . . 79

6.20 Return Expressions . . . 80

6.21 Throw Expressions . . . 81

6.22 Try Expressions . . . 81

6.23 Anonymous Functions . . . 81

6.24 Statements . . . 83

6.25 Implicit Conversions . . . 83

6.25.1 Value Conversions . . . .83

6.25.2 Method Conversions . . . .. . . 84

6.25.3 Overloading Resolution . . . 84

6.25.4 Local Type Inference . . . . . . 85

6.25.5 Eta Expansion . . . 86

7. Implicit Parameters and Views . . . 88

7.1 The ImplicitModifier . . . 88

7.2 Implicit Parameters . . . 88

7.3 Views . . . .90

7.4 View Bounds . . . 90

8. PatternMatching . . . .91

8.1 Patterns . . . 91

8.1.1 Variable Patterns . . . . . . .92

8.1.2 Typed Patterns . . . .92

8.1.3 Literal Patterns . . . .92

8.1.4 Stable Identifier Patterns . . . 92

8.1.5 Constructor Patterns . . . 93

8.1.6 Tuple Patterns . . . 93

8.1.7 Extractor Patterns . . . .. . . . 93

8.1.8 Pattern Sequences . . . . 94

8.1.9 Infix Operation Patterns . . . . .94

8.1.10 Pattern Alternatives . . . .94

8.1.11 XML Patterns . . . .95

8.1.12 Regular Expression Patterns . . . 95

8.1.13 Irrefutable Patterns . . . 95

8.2 Type Patterns . . . .. . . 95

8.3 Type Parameter Inference in Patterns . . . 96

8.4 PatternMatching Expressions . . . 98

8.5 PatternMatching Anonymous Functions . . . .100

(6)

9. Top-Level Definitions . . . 102

9.1 Compilation Units . . . .102

9.2 Packagings . . . 102

9.3 Package References . . . 102

9.4 Programs . . . 103

10. XML expressions and patterns . . . 104

10.1 XML expressions . . . 104

10.2 XML patterns . . . 105

11. User-Defined Annotations . . . .107

12. The Scala Standard Library . . . 110

12.1 Root Classes . . . .110

12.2 Value Classes . . . .112

12.2.1 Numeric Value Types . . . .112

12.2.2 Class Boolean . . . 115

12.2.3 Class Unit . . . .115

12.3 Standard Reference Classes . . . 115

12.3.1 Class String . . . 115

12.3.2 The Tuple classes . . . .116

12.3.3 The Function Classes . . . .116

12.3.4 Class Array . . . .. . . 116

12.4 Class Node . . . .. . .119

12.5 The Predef Object . . . .. . 120

Βιβλιογραφία . . . 126

Α Περίληψη σύνταξης της Scala . . . .127

B Change Log . . . .133

(7)

Πρόλογος

Η Scala είναι μια γλώσσα προγραμματισμού Java-like, που ενοποιεί τον object- oriented και functional προγραμματισμό. Είναι καθαρά αντικειμενοστρεφής γλώσσα επειδή κάθε value είναι ένα αντικείμενο. Οι τύποι και η συμπεριφορά των αντικειμένων περιγράφονται από τις classes. Οι classes μπορούν να συντεθούν χρησιμοποιώντας τη σύνθεση mixin. Έχει δημιουργηθεί για να λειτουργεί σε συνεργασία με δύο αντικειμενοστρεφείς γλώσσες, τις Java και C#.

Η Scala είναι μια functional γλώσσα υπό την έννοια ότι κάθε function είναι ένα value. Τα function definitions και higher-order functions

υποστηρίζονται φυσικά. Η Scala υποστηρίζει μια γενική έννοια του pattern matching, που μπορούν να μοντελοποιηθούν με αλγεβρικούς τύπους, οι οποίοι έχουν χρησιμοποιηθεί σε πολλές γλώσσες function.

Εχει δημιουργηθεί για να επικοινωνεί, σε συνεργασία με τη Java (μια εναλλακτική εφαρμογή της Scala λειτουργεί επίσης και για .NET). Οι classes της Scala μπορούν να καλέσουν τις μεθόδους της Java, να δημιουργήσουν αντικείμενα της Java, να κληρονομούν από τις classes της Java και να εφαρμόζουν τα Java interfaces. Κανένα από αυτά δεν απαιτεί interface definitions ή τον glue code.

Η Scala αναπτύσσεται από το 2001 στο εργαστήριο μεθόδων προγραμματισμού EPFL. Η έκδοση 1.0 κυκλοφόρησε τον Νοέμβριο του 2003. Το παρόν έγγραφο περιγράφει τη δεύτερη έκδοση της γλώσσας, η οποία κυκλοφόρησε τον Μάρτιο του 2006. Δίνει μια αναφορά για το γλωσσικό καθορισμό της, και μερικές ενότητες βασικών βιβλιοθηκών. Δεν προορίζεται να διδάξει Scala ή τις έννοιές της, για αυτό τον σκοπό υπάρχουν άλλα έγγραφα (Oa04, Ode06, OZ05b, OCRZ03, OZ05a) Η Scala δημιουργήθηκε από την συλλογική προσπάθεια πολλών ανθρώπων. Το σχέδιο και η εφαρμογή της έκδοσης 1.0 ολοκληρώθηκαν από τους Philippe Altherr, Vincent Cremet, Gilles Dubochet, Burak Emir, Stéphane Micheloud, Nikolay Mihaylov, Michel Schinz, Erik Stenman, Matthias Zenger, και ο συντάκτης Iulian Dragos, Gilles Dubochet, Philipp Haller, Sean McDirmid, Lex Spoon, και Geoffrey Washburn ενώθηκαν στην προσπάθεια να αναπτυχθεί η δεύτερη έκδοση της γλώσσας και των εργαλείων της. Οι Gilad Bracha, Craig Chambers, Erik Ernst, Matthias Felleisen, Shriram Krishnamurti, Gary Leavens, Sebastian Maneth, Erik Meijer, Klaus Ostermann, Didier Rémy, Mads Torgersen, και Philip Wadler έχουν διαμορφώσει το σχέδιο της γλώσσας μέσω των ζωηρών και ενθαρρυντικών συζητήσεων και σχολίων, στις προηγούμενες εκδόσεις του παρόντος εγγράφου.

Οι συντελεστές της Scala mailing list μας έχουν δώσει ένα πολύ χρήσιμο feedback, το οποίο μας βοηθάει να βελτιώσουμε τη γλώσσα και τα εργαλεία της.

.

(8)

Κεφάλαιο 1

Λεξικολογική σύνταξη

Τα προγράμματα Scala γράφονται χρησιμοποιώντας τον Unicode Basic Multilingual Plane (BMP) character set. Οι χαρακτήρες Unicode supplementary δεν υποστηρίζονται προς το παρόν. Αυτό το κεφάλαιο καθορίζει τους δύο τρόπους λεξικολογικής σύνταξης της Scala, του Scala mode και του XML mode. Ειδάλλως , οι ακόλουθες περιγραφές της Scala αναφέρονται σε Scalamode, και οι literal characters ‘c’ αναφέρονται στο ASCII fragment \u0000-\u007F.

Στον Scala mode , οι Unicode escapes αντικαθίστανται από τον αντίστοιχο χαρακτήρα Unicode με το δεδομένο δεκαεξαδικό κώδικα.

UnicodeEscape ::= \{\\}u{u} hexDigit hexDigit hexDigit hexDigit hexDigit ::= ‘0’ | ¢¢¢ | ‘9’ | ‘A’ | ¢¢¢ | ‘F’ | ‘a’ | ¢¢¢ | ‘f’ |

Για να κατασκευάσει τα tokens, οι χαρακτήρες διακρίνονται σύμφωνα με τις ακόλουθες classes (γενική κατηγορία Unicode που δίνεται σε παρένθεση):

1. Χαρακτήρες Whitespace. \ u0020 | \ u0009 | \ u000D | \ u000A

2. Letters, τα οποία περιλαμβάνουν τα Lower case letterrs (Ll), upper case letters (LU), title-case letters (Lt), other letters (Lo), letter numerals (NL) και οι δύο χαρακτήρες \u0024 ‘$’ and \u005F ‘_’, οι οποίοι είναι upper case letters 3. Digits ‘0’ | . . . | ‘9’.

4. Παρενθέσεις ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’.

5. Delimiter characters ‘‘’ | ‘’’ | ‘”’ | ‘.’ | ‘;’ | ‘,’.

6. Operator characters

Αυτοί αποτελούνται από όλους τους εκτυπώσιμους ASCII χαρακτήρες \ u0020- \ u007F, οι όποιοι δεν είναι σε κανένα από τα παραπάνω σύνολα, μαθηματικά σύμβολα (Sm) και άλλα σύμβολα (So).

(9)

1.1 Identifiers

Σύνταξη:

Op ::= opchar {opchar}

Varid ::= lower idrest plainid ::= upper idrest | varid

| op id ::= plainid | ‘\‘’ stringLit ‘\‘’

Idrest ::= {letter | digit} [‘_’ op]

Υπάρχουν τρεις τρόποι να διαμορφωθεί ένα identifier. Πρώτον, ένα identifier μπορεί να αρχίσει με ένα letter που μπορεί να ακολουθηθεί από μια αυθαίρετη ακολουθία από letters και digits.

Αυτό μπορεί να είναι ακολουθούμενο από την κάτω παύλα ‘_ ‘, χαρακτήρες και από άλλα string που αποτελούνται είτε από letters και digits είτε από τους operator characters.

Δεύτερον, ένα identifier μπορεί να αρχίσει με έναν operator character που ακολουθείται από μια αυθαίρετη ακολουθία από operator characters. Οι προηγούμενες δύο μορφές ονομάζονται plain identifiers. Τέλος, ένα identifier μπορεί επίσης να διαμορφωθεί από μια αυθαίρετη string μεταξύ των back-quotes (τα host systems μπορούν να επιβάλουν μερικούς περιορισμούς στους οποίους οι strings είναι αποδεκτές για τα identifiers).Δηλαδή το identifier αποτελείται από όλους τους χαρακτήρες αποκλείοντας τα backquotes.

Όπως συνηθίζεται, ισχύει ένας πιο μακροχρόνιος κανόνας αντιστοιχιών. Για παράδειγμα, η string big_bob++=‘def

αποσυνθέτει στα τρία identifiers big_bob, ++=, και def. Οι κανόνες για το pattern matching διακρίνονται περαιτέρω, μεταξύ των variable identifiers, που αρχίζουν με lower case letter, και constnt identifiers, που δεν αρχίζουν με lower case letter.

Ο χαρακτήρας ‘$’ είναι διατηρημένος για τα compiler-synthesized identifiers. Τα προγράμματα χρηστών δεν πρέπει να καθορίσουν τα identifiers που περιέχουν χαρακτήρες ‘$’.Τα ακόλουθα ονόματα είναι διατηρημένες λέξεις αντί να είναι μέλη της syntactic class id των lexical identifiers.

abstract case catch class def do else extends false final finally for forSome if implicit import lazy match new null object override package private protected requires return sealed super this throw trait try true type val var while with yield _ : = =>  <: <% >: # @

(10)

Οι Unicode operators \u21D2 ‘=>’ και \u2190 ‘’, που έχουν το ASCII equivalents

‘=>’και ‘’, είναι επίσης διατηρημένοι.

Παράδειγμα 1.1.1 Εδώ είναι παραδείγματα των identifiers:

x Object maxIndex p2p empty_?

+ ‘yield‘ αρετή _y dot_product_*

__system _MAX_LEN_

Παράδειγμα 1.1.2 Οι backquote-enclosed strings είναι μια λύση, όταν κάποιος πρέπει να έχει πρόσβαση στα identifiers της Java, που είναι διατηρημένες λέξεις σε Scala. Για παράδειγμα, η δήλωση Thread.yield () είναι λάθος, δεδομένου ότι η yield είναι μια διατηρημένη λέξη σε Scala. Εντούτοις, είναι εδώ work-around:

Thread.‘yield‘()

1.2 Newline Characters

Σύνταξη:

semi ::= ‘;’ | nl {nl}

Η Scala είναι μια line-oriented γλώσσα όπου οι δηλώσεις μπορούν να ολοκληρωθούν από τις άνω τελείες ή τις αλλαγές γραμμής. Μια αλλαγή γραμμής σε ένα κείμενο πηγής Scala αντιμετωπίζεται ως το special token “nl”, εάν ικανοποιούνται τα τρία ακόλουθα κριτήρια :

1. Το token που προηγείται της αλλαγής γραμμής μπορεί να ολοκληρώσει μια δήλωση.

2. Το token αμέσως μετά από την αλλαγή γραμμής μπορεί να αρχίσει μια δήλωση.

3. Το token εμφανίζεται σε μια περιοχή όπου οι πολλαπλάσιες δηλώσεις επιτρέπονται.

Τα tokens που μπορούν να ολοκληρώσουν μια δήλωση είναι: literals, προσδιοριστικά και οι ακόλουθοι delimeters και οι διατηρημένες λέξεις:

this null true false return type <xmlstart>

_ ) ] }

Τα tokens που μπορούν να αρχίσουν μια δήλωση είναι όλα τα σύμβολα Scala εκτός από τους ακόλουθους οριοθέτες και τις διατηρημένες λέξεις:

catch else extends finally forSome match requires with yield , . ; : _ = => <- <: <%

>: # [ ) ] }

Ένα case token μπορεί να αρχίσει μια δήλωση μόνο εάν ακολουθείται από ένα class ή object token. Οι πολλαπλές δηλώσεις επιτρέπονται σε:

(11)

1. Όλα τα αρχεία πηγής Scala, εκτός από τις τοποθετημένες περιοχές όπου οι αλλαγές γραμμής καταστέλλονται, και

2. το διάστημα μεταξύ του ταιριάσματος {και} των tokens στηριγμάτων, εκτός από τις τοποθετημένες περιοχές όπου οι αλλαγές γραμμής καταστέλλονται.

Οι πολλαπλάσιες δηλώσεις είναι εκτός λειτουργίας σε:

1. διάστημα μεταξύ του ταιριάσματος (και) των tokens παρενθέσεων, εκτός από τις τοποθετημένες περιοχές όπου οι αλλαγές γραμμής επιτρέπονται, και

2. διάστημα μεταξύ του ταιριάσματος [και] των tokens υποστηριγμάτων, εκτός από τις τοποθετημένες περιοχές όπου οι αλλαγές γραμμής επιτρέπονται.

3. διάστημα μεταξύ ενός token και του ταιριάσματός του => περίπτωσης σημείο, εκτός από τις τοποθετημένες περιοχές όπου οι αλλαγές γραμμής επιτρέπονται.

4. Περιοχές που αναλύονται με οποιοδήποτε τρόπο XML (§1.5).

Σημειώστε ότι οι brace characters {…}των escapes σε XML και τα string literals δεν είναι tokens, και επομένως δεν εσωκλείουν μια περιοχή όπου επιτρέπονται οι αλλαγές γραμμής.

Κανονικά, μόνο ένα token nl παρεμβάλλεται μεταξύ δύο διαδοχικών tokens non- newline, που βρίσκονται σε διαφορετικές γραμμές, ακόμα κι αν υπάρχουν πολλαπλάσιες γραμμές μεταξύ των δύο tokens. Αλλά, εάν δύο tokens χωρίζονται από τουλάχιστον μια κενή γραμμή (δηλ. μια γραμμή που δεν περιέχει κανέναν εκτυπώσιμο χαρακτήρα), παρεμβάλλονται δύο tokens nl.

Η γραμματική της Scala (που δίνεται πλήρως στο παράρτημα Α) περιέχει οδηγίες όπου προαιρετικά τα tokens nl, γίνονται αποδεκτά, αλλά όχι οι άνω τελείες,. Αυτό έχει την επίδραση ότι μια αλλαγή γραμμής σε μια από αυτές τις θέσεις δεν ολοκληρώνει μια έκφραση ή μια δήλωση. Αυτές οι θέσεις μπορούν να συνοψιστούν ως εξής:

Τα πολλαπλάσια tokens αλλαγών γραμμής γίνονται αποδεκτά στις ακόλουθες θέσεις (σημειώστε ότι μια άνω τελεία αντί της αλλαγής γραμμής δεν θα ήταν αποδεκτή στις παρακάτω περιπτώσεις):

 μεταξύ του condition μίας conditional expression (§6.16) ή κατά τη διάρκεια βρόχου (§6.17) και της έκφρασης που ακολουθεί,

 μεταξύ των απαριθμητών μιας for-comprehension (§6.19) και την επόμενη έκφραση, και

 μετά από τον αρχικό τύπο της λέξης κλειδί, σε έναν καθορισμό ή μια δήλωση τύπων (§4.3).

Ένα single σύμβολο αλλαγής γραμμής γίνεται αποδεκτό:

 μπροστά από ένα στήριγμα ανοίγματος «{», εάν αυτό το στήριγμα είναι μια αποδεκτή συνέχεια της τρέχουσας δήλωσης ή έκφρασης,

 μετά από έναν infix operator, εάν το πρώτο token της επόμενης γραμμής μπορεί να αρχίσει μια έκφραση (§6.12),

 μπροστά από μια πρόταση παραμέτρου (§4.6), και

 μετά από έναν σχολιασμό (§11).

(12)

Παράδειγμα 1.2.1 Ο ακόλουθος κώδικας περιέχει τέσσερις καλοσχηματισμένες δηλώσεις, κάθε μια σε δύο γραμμές. Τα tokens αλλαγών γραμμής μεταξύ των δύο γραμμών δεν χρησιμοποιούνται για να διαχωρίσουν τις δηλώσεις.

if (x > 0) x = x 1

while (x > 0) x = x / 2

for (x <1 to 10) println(x)

type

IntList = List[Int]

Παράδειγμα 1.2.2 Ο ακόλουθος κώδικας υποδεικνύει μια ανώνυμη κλάση:

new Iterator[Int]

{

private var x = 0 def hasNext = true def next = { x += 1; x } }

Με έναν πρόσθετο χαρακτήρα αλλαγών γραμμής, ο ίδιος κώδικας ερμηνεύεται ως δημιουργία αντικειμένου που ακολουθείται από έναν local block:

new Iterator[Int]

{

private var x = 0 def hasNext = true def next = { x += 1; x } }

Παράδειγμα 1.2.3 Ο ακόλουθος κώδικας υποδεικνύει μια single expression:

x < 0 ||

x > 10

Με έναν πρόσθετο χαρακτήρα αλλαγών γραμμής, ο ίδιος κώδικας ερμηνεύεται με δύο εκφράσεις:

x < 0 ||

x > 10

(13)

Παράδειγμα 1.2.4 Ο ακόλουθος κώδικας υποδεικνύει έναν ενιαίο, καθορισμό λειτουργίας:

def func(x: Int) (y: Int) = x + y

Με έναν πρόσθετο χαρακτήρα αλλαγών γραμμής, ο ίδιος κώδικας ερμηνεύεται ως αφηρημένος καθορισμός λειτουργίας και συντακτικά η δήλωση δεν είναι σωστη:

def func(x: Int) (y: Int) = x + y

Παράδειγμα 1.2.5 Ο ακόλουθος κώδικας υποδεικνύει έναν καθορισμό:

@serializable

protected class Data { ... }

Με έναν πρόσθετο χαρακτήρα αλλαγών γραμμής, ο ίδιος κώδικας ερμηνεύεται ως ιδιότητα και χωριστή δήλωση (που είναι συντακτικά λάθος).

@serializable

protected class Data { ... }

1.3 Literals

Υπάρχουν literals για τους αριθμούς ακέραιων αριθμών, αριθμούς κινητής υποδιαστολής, χαρακτήρες, booleans, σύμβολα, σειρές. Η σύνταξη αυτών των literals είναι σε κάθε περίπτωση όπως στην Java.

Σύνταξη:

Literal ::= [‘-’] integerLiteral | [‘-’] floatingPointLiteral | booleanLiteral

| characterLiteral | stringLiteral | symbolLiteral | ‘null

(14)

1.3.1 Ακέραια Literals

Σύνταξη:

integerLiteral ::= (decimalNumeral | hexNumeral | octalNumeral) [‘L’ | ‘l’]

decimalNumeral ::= ‘0’ | nonZeroDigit {digit}

hexNumeral ::= ‘0’ ‘x’ hexDigit {hexDigit}

octalNumeral ::= ‘0’ octalDigit {octalDigit}

digit ::= ‘0’ | nonZeroDigit nonZeroDigit ::= ‘1’ | … | ‘9’

octalDigit ::= ‘0’ | … | ‘7’

Τα integer literals είναι συνήθως τύπου int ή τύπου long όταν ακολουθείται από ένα L ή 1 suffix. Οι τιμές του τύπου int, είναι όλοι οι ακέραιoι αριθμοί από -231 έως 231 -1. Οι τιμές του τύπου Long είναι όλοι οι ακέραιοι αριθμοί από -263 έως 263 -1.

Eμφανίζεται compile-time error, εάν ένας integer literal, δείχνει έναν αριθμό έξω από αυτά τα όρια.

Εντούτοις, εάν ο αναμενόμενος τύπος pt (§6.1) ενός literal σε μια έκφραση είναι Byte,

Short, ή Char και ο ακέραιος αριθμός ταιριάζει στην αριθμητική σειρά, στα όρια που καθορίζονται από τον τύπο, κατόπιν ο αριθμός μετατρέπεται στον τύπο pt και ο τύπος του literal είναι pt. Οι αριθμητικές σειρές που δίνονται από αυτούς τους τύπους είναι:

Byte -27 έως 27-1 Short -215 έως 215-1 Char 0 έως 216 -1

Παράδειγμα 1.3.1 Εδώ είναι μερικά integer literals:

0 21 0xFFFFFFFF 0777L

1.3.2 Floating point literals

Σύνταξη:

floatingPointLiteral ::= digit {digit} ‘.’ {digit} [exponentPart] [floatType]

| ‘.’ digit {digit} [exponentPart] [floatType]

| digit {digit} exponentPart [floatType]

| digit {digit} [exponentPart] floatType exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit {digit}

floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’

Τα floating point literals είναι τύπου float όταν ακολουθούντε από ένα floating point τύπου suffix F η f, η αν είναι τύπου Double. Ο τύπος float αποτελείται από όλες τις τιμές IEEE 754 32-bits single-precision binary floating point, ενώ ο τύπος double αποτελείται από όλες τις τιμές IEEE 754 64-bits double-precision binary floating

(15)

Εάν σε ένα πρόγραμμα, ένα floating point literal ακολουθείται με μια συμβολική έναρξη με ένα γράμμα, πρέπει να υπάρξει τουλάχιστον ένας intervening whitespace χαρακτήρας μεταξύ των δύο σημείων.

Παράδειγμα 1.3.2 Εδώ είναι μερικά floating point literals:

0.0 1e30f 3.14159f 1.0e-100 .1

Παράδειγμα 1.3.3 Η φράση ‘1.toString’ αναλύεται σε τρία διαφορετικά σημεία: ‘1΄,

‘. ΄«, και ‘toString’. Αφ’ ενός, εάν ένα διάστημα προστίθεται μετά από την περίοδο, η φράση ‘1. toString΄ αναλύεται ως floating point literal ‘1.΄ ακολουθούμενο από το προσδιοριστικό ‘toString’.

1.3.3 Boolean Literals

Σύνταξη:

booleanLiteral ::= ‘true’ | ‘false

Τα boolean literals true και false είναι μέλη του τύπου Boolean.

1.3.4 Character Literals

Σύνταξη:

characterLiteral ::= ‘\’’ printableChar ‘\’’

| ‘\’’ charEscapeSeq ‘\’’

Ένας χαρακτήρας literal είναι ένας single character που εσωκλείεται στα αποσπάσματα. Ο χαρακτήρας είναι είτε εκτυπώσιμος χαρακτήρας unicode είτε περιγράφεται από μία escape sequence (§1.3.6).

Παράδειγμα 1.3.4 Εδώ είναι μερικά character literals:

’a’ ’\u0041’ ’\n’ ’\t’

Σημειώστε ότι ο ‘’\ u000A’’ δεν είναι έγκυρος character literal ,γιατί η μετατροπή Unicode έχει γίνει πριν από την ανάλυση literal και ο χαρακτήρας Unicode \ u000A (line feed ) δεν είναι εκτυπώσιμος. Κάποιος μπορεί να χρησιμοποιήσει αντ’

αυτού την ακολουθία escape ‘’\ n‘’ ή την octal escape ‘’\ 12 « (§1.3.6).

1.3.5 String Literals

Σύνταξη:

stringLiteral ::= ‘ \” ’ {stringElement} ‘ \” ’

stringElement ::= printableCharNoDoubleQuote | charEscapeSeq

(16)

Ένα string literal είναι μια ακολουθία χαρακτήρων σε διπλά αποσπάσματα. Οι χαρακτήρες είναι είτε εκτυπώσιμοι χαρακτήρες unicode, είτε περιγράφονται από τις ακολουθίες escape (§1.3.6). Εάν η string literal περιέχει έναν διπλό χαρακτήρα αποσπάσματος, πρέπει να γίνει escaped, δηλ. \ «. Η αξία μιας string literal είναι μια περίπτωση class String..

Παράδειγμα 1.3.5 Εδώ είναι μερικά string literals:

“Hello,\nWorld!”

“This string contains a \” character.”

Multi-Line String Literals

Σύνταξη:

stringLiteral ::= ‘ “ “ “ ’ multiLineChars ‘” “ “ ’ multiLineChars ::= {[’”’] [’”’] charNoDoubleQuote}

Μια Multi-Line String Literal είναι μια ακολουθία χαρακτήρων που εσωκλείονται στα τριπλά αποσπάσματα « « « … « « «. Η ακολουθία χαρακτήρων είναι αυθαίρετη, εκτός από το ότι μπορεί να μην περιέχει ένα τριπλό απόσπασμα. Οι χαρακτήρες δεν είναι απαραιτήτως εκτυπώσιμοι.Αλλαγές γραμμής ή άλλοι χαρακτήρες ελέγχου επίσης επιτρέπονται. Οι Unicode escapes λειτουργούν, όπως παντού , αλλά καμία από τις ακολουθίες escape μέσα στο (§1.3.6) δεν ερμηνεύεται.

Παράδειγμα 1.3.6 Εδώ είναι μια multi-line string literal:

“ “ “the present string spans three

lines.” « «

Αυτό θα παράγει το string : the present string spans three lines.

Η βιβλιοθήκη της Scala περιέχει μια χρήσιμη μέθοδο την stripMargin που μπορεί να χρησιμοποιηθεί στις strip leading whitespace από multi-line strings. Η έκφραση

“ “ “ the present string

|spans three

|lines.” “ “.stripMargin Αξιολογείται σε :

the present string spans three lines.

(17)

Η μέθοδος stripMargin καθορίζεται στην class scala.runtime.RichString. Επειδή υπάρχει μια προκαθορισμένη υπονοούμενη μετατροπή (§6.25) από string σε RichString, η μέθοδος ισχύει σε όλες τις string.

1.3.6 Escape Sequences

Οι ακόλουθες escape sequences αναγνωρίζονται στα character και string literals.

\b \u0008: backspace BS

\t \u0009: horizontal tab HT

\n \u000a: linefeed LF

\f \u000c: formfeed FF

\r \u000d: carriage return CR

\” \u0022: double quote “

\’ \u0027: single quote ’

\\ \u0009: backslash \

Ένας χαρακτήρας με Unicode μεταξύ 0 και 255 μπορεί επίσης να αντιπροσωπευθεί από ένα octal escape, δηλ. μια αντίστροφη κάθετος ΄΄ \ ΄΄ που ακολουθείται από μια ακολουθία με περισσότερους από τρεις οκταδικούς χαρακτήρες.

Είναι compile time error εάν ένας χαρακτήρας backslash σε έναν χαρακτήρα ή μια string literal δεν αρχίζει μια έγκυρη ακολουθία escape.

1.3.7 Symbol literals

Σύνταξη:

symbolLiteral ::= ‘ ’ ’ idrest

Ένα Symbol literal ‘Χ’ είναι μια στενογραφία για το scala. Symbol(«x»).intern. Το σύμβολο είναι μια case class (§5.3.2), η οποία καθορίζεται ως εξής:

package scala

final case class Symbol(name: String) { override def toString: String = “’” + name def intern: Symbol = ...

}

Η intern method μετατρέπει τα σύμβολα σε μοναδικές αναφορές: Εάν δύο interned symbols έχουν το ίδιο όνομα, τότε πρέπει να είναι το ίδιο αντικείμενο.

1.4 Whitespace and Comments

Τα tokens μπορούν να χωριστούν από τους χαρακτήρες whitespace και / τα σχόλια. Τα σχόλια έχουν δύο μορφές:

Ένα σχόλιο single-line είναι μια ακολουθία χαρακτήρων που αρχίζει με το // και επεκτείνεται στο τέλος της γραμμής.

(18)

Ένα σχόλιο multi-line είναι μια ακολουθία χαρακτήρων μεταξύ /* και */. Τα σχόλια multi-line μπορούν να τοποθετηθούν.

1.5 XML mode

Προκειμένου να επιτραπεί ο literal συνυπολογισμός των XML τεμαχίων, με λεξικολογική ανάλυση αλλάζουμε από τον Scala mode στον XML mode όταν υπάρχει γωνία ανοίγματος ’ < ‘μετά από την περίσταση: Το ‘ < ‘πρέπει να προηγείται είτε από whitespace, ένα άνοιγμα παρένθεσης ή από ένα opening brace και ακολουθούμενος αμέσως από έναν χαρακτήρα που ξεκινάει με ένα όνομα XML.

Σύνταξη:

( whitespace | ‘(’ | ‘{’ ) ‘<’ (XNameStart | ‘!’ | ‘?’)

XNameStart ::= ‘_’ | BaseChar | Ideographic (όπως στο W3C XML, αλλά χωρίς ‘:’ )

Ο scanner αλλάζει από τον XML mode στον Scala mode εάν είτε :

 η έκφραση XML ή το σχέδιο XML που αρχίζει από το αρχικό ‘ < ‘έχει αναλυθεί επιτυχώς, ή εάν

 ο κατατμητής αντιμετωπίζει μια ενσωματωμένη έκφραση ή ένα σχέδιο Scala και πηγαίνει τον ανιχνευτή πίσω στον κανονικό τρόπο, μέχρι η έκφραση ή το σχέδιο Scala να αναλυθεί επιτυχώς. Σε αυτήν την περίπτωση, δεδομένου ότι ο κώδικας και τα τεμάχια XML μπορούν να τοποθετηθούν, ο κατατμητής πρέπει να διατηρήσει έναν σωρό που απεικονίζει την τοποθέτηση της XML και των εκφράσεων Scala επαρκώς.

Σημειώστε ότι κανένα Scala token δεν κατασκευάζεται με τον XML mode, και ότι τα σχόλια ερμηνεύονται ως κείμενο.

Παράδειγμα 1.5.1 Ο ακόλουθος καθορισμός αξίας χρησιμοποιεί ένα XML literal με δύο ενσωματωμένες εκφράσεις Scala

val b = <book>

<title>The Scala Language Specification</title>

<version>{scalaBook.version}</version>

<authors>{scalaBook.authors.mkList(“ “, “, “, “ “)}</authors>

</book>

(19)
(20)

Κεφάλαιο 2

Identifiers, Names and Scopes

Τα names στη Scala προσδιορίζουν τους τύπους, τις τιμές, τις μεθόδους, και τις κατηγορίες και αποκαλούνται entities. Τα ονόματα εισάγονται από τους τοπικούς ορισμούς και τις δηλώσεις (§4), την κληρονομιά (§5.1.3), τις προτάσεις import (§4.7), ή τις προτάσεις package (§9.2) που καλούνται bindings.

Οι bindings διαφορετικών ειδών καθορίζουν μια προτεραιότητα σε αυτούς: Οι ορισμοί (τοπικοί ή που κληρονομούνται) έχουν την υψηλότερη προτεραιότητα, ακολουθούνται από explicit imports, που ακολουθούνται από wildcard imports, που ακολουθούνται από package members, οι οποίες έχουν τη χαμηλότερη προτεραιότητα.

Υπάρχουν δύο διαφορετικά name spaces, ένα για τους τύπους (§3) και ένα για τους όρους (§6). Το ίδιο όνομα μπορεί να υποδείξει έναν τύπο και έναν όρο, ανάλογα με το πλαίσιο που χρησιμοποιείται το όνομα.

Μια binding έχει ένα πεδίο στο οποίο η οντότητα που καθορίζεται απλά από ένα όνομα, μπορεί να προσεγγιστεί χρησιμοποιώντας ένα απλό όνομα. Τα πεδία τοποθετούνται. Μια σύνδεση σε κάποιο εσωτερικό πεδίο σκιάζει τις bindings χαμηλότερης προτεραιότητας στο ίδιο πεδίο καθώς επίσης και τις bindings της ίδιας ή χαμηλότερης προτεραιότητας στα εξωτερικά πεδία.

Σημειώστε ότι να σκιάσει είναι μόνο μια μερική διαταγή. Σε μια κατάσταση όπως val x = 1;

{ import p.x;

x }

καμία binding του Χ δεν σκιάζει άλλη. Συνεπώς, η αναφορά στο Χ στην τρίτη γραμμή θα ήταν διφορούμενη.

Μια αναφορά σε ένα αναρμόδιο (τύπος ή όρος) identifier Χ δεσμεύεται από τη μοναδική binding, η οποία

 καθορίζει μια οντότητα με το όνομα Χ στο ίδιο namespace με το identifier, και

 σκιάζει όλες τις άλλες bindings που καθορίζουν τις οντότητες με το όνομα Χ σε αυτό namespace.

Εάν καμία τέτοια σύνδεση δεν υπάρχει, είναι λάθος. Εάν το Χ δεσμεύεται από μια πρόταση εισαγωγών, κατόπιν το απλό όνομα Χ λαμβάνεται για να είναι ισοδύναμο με το κατάλληλο όνομα στο οποίο το Χ χαρτογραφείται από την πρόταση εισαγωγών. Εάν το Χ δεσμεύεται από ένα definition ή μια δήλωση, τότε το Χ αναφέρεται στην οντότητα που εισάγεται από εκείνη την binding. Σ’ αυτή την περίπτωση, ο τύπος του Χ είναι ο τύπος της παραπεμφθείσας οντότητας.

(21)

Παράδειγμα 2.0.2 υποθέτουμε τους ακόλουθους δύο definitions αντικειμένων που ονομάζονται Χ στις packages P και Q.

package P {

object X { val x = 1; val y = 2 } }

package Q {

object X { val x = true; val y = “” } }

Το ακόλουθο πρόγραμμα επεξηγεί τα διαφορετικά είδη bindings και προτεραιοτήτων μεταξύ τους.

package P { // ‘X’ bound by package clause import Console._ // ‘println’ bound by wildcard import object A {

println(“L4: “+X) // ‘X’ refers to ‘P.X’ here object B {

import Q._ // ‘X’ bound by wildcard import println(“L7: “+X) // ‘X’ refers to ‘Q.X’ here

import X._ // ‘x’ and ‘y’ bound by wildcard import println(“L8: “+x) // ‘x’ refers to ‘Q.X.x’ here

object C {

val x = 3 // ‘x’ bound by local definition println(“L12: “+x) // ‘x’ refers to constant ‘3’ here

{ import Q.X._ // ‘x’ and ‘y’ bound by wildcard import // println(“L14: “+x) // reference to ‘x’ is ambiguous here import X.y // ‘y’ bound by explicit import

println(“L16: “+y) // ‘y’ refers to ‘Q.X.y’ here { val x = “abc” // ‘x’ bound by local definition

import P.X._ // ‘x’ and ‘y’ bound by wildcard import // println(“L19: “+y) // reference to ‘y’ is ambiguous here

println(“L20: “+x) // ‘x’ refers to string ‘‘abc’’ here }}}}}}

Μια αναφορά σε ένα κατάλληλο (τύπος ή όρος) identifier e.x αναφέρεται στο μέλος του τύπου Τ του e που έχει το όνομα Χ στο ίδιο namespace με το identifier. Είναι ένα λάθος εάν το Τ δεν είναι ένας value type (§3.2). Ο τύπος e.x είναι ο τύπος μελών της παραπεμφθείσας οντότητας στο Τ.

(22)

Κεφάλαιο 3

Τύποι

Σύνταξη:

Type ::= InfixType ‘=>’ Type | ‘(’ [‘=>’ Type] ‘)’ ‘=>’ Type | InfixType [ExistentialClause]

ExistentialClause ::= ‘forSome’ ‘{’ ExistentialDcl {semi ExistentialDcl} ‘}’

ExistentialDcl ::= ‘type’ TypeDcl | ‘val’ ValDcl

InfixType ::= CompoundType {id [nl] CompoundType}

CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement]

| Refinement

AnnotType ::= SimpleType {Annotation}

SimpleType ::= SimpleType TypeArgs | SimpleType ‘#’ id | StableId

| Path ‘.’ ‘type

| ‘(’ Types [‘,’] ’)’

TypeArgs ::= ‘[’ Types ‘]’

Types ::= Type {‘,’ Type}

Διακρίνουμε μεταξύ των first-order types και των type constructors, οι οποίοι παίρνουν τους type parameters και τους yield types. Ένα υποσύνολο των first- order types αποκαλούμενων και ως value types, αντιπροσωπεύει σύνολα (first- class) τιμών. Οι value types είναι είτε concrete είτε abstract.

Κάθε concrete value type μπορεί να αντιπροσωπευθεί ως class type, δηλ. ένας προσδιοριστής τύπων (§3.2.3) που αναφέρεται σε ένα class1 (§5.3), ή ως compound type (§3.2.7) που αντιπροσωπεύει ένα intersection τύπων, ενδεχομένως με έναν καθαρισμό (§3.2.7) που περιορίζει περαιτέρω τους τύπους από τα μέλη τους. Οι abstract value types εισάγονται από τους type parameters (§4.4) και τους abstract type bindings (§4.3). Οι παρενθέσεις στους τύπους χρησιμοποιούνται για την ομαδοποίηση.

1 Yποθέστε ότι τα objects και οι packages σιωπηρά καθορίζουν μια class (του ίδιου ονόματος με το αντικείμενο ή τη package, αλλά είναι απρόσιτος στα προγράμματα χρηστών).

(23)

Οι non-value types βλέπουν τις ιδιότητες των προσδιοριστικών που δεν είναι non- value (§3.3). Για παράδειγμα, ένας type constructor (§3.3.3) δεν διευκρινίζει άμεσα τον τύπο τιμών. Εντούτοις, όταν ένας type constructor απευθύνεται στα σωστά επιχειρήματα τύπων, παράγει το α first-order type, ο οποίος μπορεί να είναι ένας value type.

Οι non-value types εκφράζονται έμμεσα σε Scala. Π.χ., ένας method type περιγράφεται γράφοντας μια υπογραφή μεθόδου, η οποία από μόμη της δεν είναι ένας πραγματικός τύπος, αν και δίνει αφορμή για έναν αντίστοιχο function type (§3.3.1). Οι type constructors είναι ένα άλλο παράδειγμα, μπορούμε να γράψουμε type Swap[m_, _], a, b] = m[b, a], αλλά δεν υπάρχει καμία σύνταξη για να γράψει άμεσα την αντίστοιχη ανώνυμη λειτουργία τύπων.

3.1 Paths

Σύνταξη :

Path ::= StableId | [id ‘.’] this StableId ::= id

| Path ‘.’ id

| [id ’.’] ‘super’ [ClassQualifier] ‘.’ id ClassQualifier ::= ‘[’ id ‘]’

Οι paths δεν είναι τύποι από μόνοι τους, αλλά μπορούν να είναι ένα μέρος των ονομασμένων τύπων και στην fanction form να έχουν πρωταρχικό ρόλο, στο σύστημα τύπων Scala.

Μια path είναι όπως βλέπομε παρακάτω:

 Η empty path (που δεν μπορεί να γραφτεί ρητά στα προγράμματα χρηστών).

 C.this, όπου το C αναφέρεται σε μια class. Η path this λαμβάνεται ως συντομογραφία για το C.this όπου το C είναι το όνομα της class που εσωκλείει άμεσα την αναφορά.

 p.x όπου το p είναι μια path και το Χ είναι ένα σταθερό μέλος του p.Σταθερά μέλη, είναι μέλη που εισάγονται από ορισμούς value ή object , καθώς επίσης και από packages.

 C.super.x ή C.super [Μ] .x όπου το C αναφέρεται σε μία class και το Χ σε ένα σταθερό μέλος της super class ή σε μία οριζόμενη parent classM του C. Το πρόθεμα super λαμβάνεται ως συντομογραφία για το C.super όπου το C είναι το όνομα της class που εσωκλείει άμεσα την αναφορά.

Ένα stable identifier είναι μια path που τελειώνει με ένα identifier.

3.2 VALUE TYPES

Κάθε value type σε Scala έχει έναν τύπο που έχει μια από τις ακόλουθες μορφές.

(24)

3.2.1 Singleton types

Σύνταξη :

SimpleType ::= Path ‘.’ Type

Ένας singleton type είναι του form p.type, όπου το p είναι μια path που δείχνει μια value που αναμένεται για να προσαρμοστεί (§6.1) σε scala.AnyRef. Ο τύπος δείχνει το set των values που αποτελούνται από null και το value δείχνεται από το p.

Όλοι οι singleton types προσαρμόζονται στο τύπο scala.Singleton. Ένας σταθερός τύπος είναι είτε ένας singleton type είτε ένας τύπος ισοδύναμος με το scala.

Singleton.

3.2.2 TYPE PROJECTION

Σύνταξη :

SimpleType ::= SimpleType ‘#’ id

Μία type projection T#x αναφέρει το type member που ονομάζεται x του τύπου T . Εάν το x αναφέρει ένα abstract type member, τότε το Τ πρέπει να είναι ένας stable type (§3.2.1).

3.2.3 TYPE DESIGNATORS Σύνταξη :

SimpleType ::= StableId

Ένας type designator αναφέρεται σε έναν value type. Μπορεί να είναι απλό ή κατάλληλο. Όλοι αυτοί οι type designators είναι shorthands για τα type projections.

Συγκεκριμένα, το αναρμόδιο όνομα τύπων t, όπου το t είναι συνδεδεμένο σε κάποια class, αντικείμενο, ή package C λαμβάνονται ως shorthant για το C.this.type#t. Εάν το t δεν είναι συνδεδεμένο σε μια class, αντικείμενο, ή package, κατόπιν το t λαμβάνεται ως shorthand για το є.type#t.

Ένας κατάλληλος type designator έχει τη μορφή p.t όπου το p είναι μια path (§3.1) και το t είναι ένα type name. Ένας τέτοιος type designator είναι ισοδύναμος με την type projection p.type#t.

Παράδειγμα 3.2.1 μερικοί type designator και οι επεκτάσεις τους παρατίθεται παρακάτω. Υποθέτουμε μια τοπική type parameter t, μια value maintable με ένα type member Node και την standard class, scala. Int,

t €.type#t Int scala.type#Int scala.Int scala.type#Int

data.maintable.Node data.maintable.type#Node

(25)

3.2.4 Parameterized types

Σύνταξη :

SimpleType ::= SimpleType TypeArgs TypeArgs ::= ‘[’ Types ‘]’

Ένας parameterized type Τ [U1,…, Un] αποτελείται από έναν type designator T και τους type parameters U1,… Un, όπου ν ≥ 1. Το Τ πρέπει να αναφερθεί σε έναν type constructor που παίρνει n type parameters a1,…. an.

Ας πούμε ότι οι type parameters έχουν τα κατώτερα όρια L1,…, Ln και ανώτερα όρια U1,… Un. Ο parameterized type είναι καλοσχηματισμένος εάν κάθε πραγματική type parameter προσαρμόζεται σε κάθε όριο, δηλ. σLi<:Ti<:σUi όπου το σ είναι η αντικατάσταση [a1 :=T1,…,an:=Tn].

Παράδειγμα 3.2.2 δεδομένων των partial type definitions : class TreeMap[A <: Comparable[A], B] { . . . }

class List[A] { . . . }

class I extends Comparable[I] { . . . }

οι ακόλουθοι parameterized types διαμορφώνονται σωστά:

TreeMap[I, String]

List[I]

List[List[Boolean]]

Παράδειγμα 3.2.3 δεδομένων των type definitions του παραδείγματος 3.2.2, οι ακόλουθοι τύποι είναι σχηματισμένοι λάθος:

TreeMap[I] // illegal: wrong number of parameters TreeMap[List[I], Boolean] // illegal: type parameter not within bound

3.2.5 Tuple τύποι

Σύνταξη :

SimpleType ::= ‘ ( ’ Types [‘,’] ’ ) ’

Ένας tuple type (T1,…, Tn) είναι ένα ψευδώνυμο για την class scala. Tuplen [T1,…, Tn], όπου n ≥ 2. Ο τύπος μπορεί επίσης να γραφτεί με ένα κόμμα στο τέλος , δηλ.

(T1,…, Tn,).

Οι Tuple classes είναι case classes όπου οι τομείς τους μπορούν να προσεγγιστούν χρησιμοποιώντας τους επιλογείς _1,…, _n. Η λειτουργία τους αφαιρείται σε ένα αντίστοιχο Product trait. Το n-ary tuple class και product trait καθορίζονται όπως θα δούμε στην standard βιβλιοθήκη της Scala (επίσης μπορούν να προστεθούν κι άλλες μεθόδοι και να εφαρμοστούν

άλλα traits).

(26)

case class Tuplen[+T1, ..., +Tn](_1: T1, ..., _n: Tn) extends Productn[T1, ..., Tn] {}

trait Productn[+T1, +T2, +Tn] { override def arity = n

def _1: T1 ...

def _n:Tn }

3.2.6 Annotated Types

Σύνταξη :

AnnotType ::= SimpleType {Annotation}

Ένας Annotated Types Τ a1…an συνδέει τους σχολιασμούς a1…an με τον τύπο Τ (§11).

3.2.7 Compound Types

Σύνταξη :

CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement]

| Refinement

Refinement ::= [nl] ‘{’ RefineStat {semi RefineStat} ‘}’

RefineStat ::= Dcl

| ‘type’ TypeDef

Ένα Compound Type T1 with…with Tn {R} αντιπροσωπεύει τα αντικείμενα με μέλη, όπως δίνονται στα component types T1,…, Tn και στον refinement {R}. Ένας refinement {Ρ} περιέχει τις δηλώσεις και τους type definitions. Εάν μια δήλωση ή ένα definition αγνοεί μια δήλωση ή έναν definition σε ένα από τα component types T1,…, Tn, οι συνηθισμένοι κανόνες για overriding(§5.1.4) ισχύουν, διαφορετικά η δήλωση ή ο definition θα λέγοται «δομικά» 2. Μέσα σε μια δήλωση μεθόδου, με έναν δομικό refinement, ο τύπος οποιασδήποτε value parameter μπορεί μόνο να αναφερθεί στις type parameters ή τους abstract types, που περιλαμβάνονται μέσα στον refinement. Δηλαδή πρέπει να αναφερθεί είτε σε μια type parameter με δική της λειτουργία, είτε σε έναν καθορισμό τύπων μέσα στον refinement. Αυτός ο περιορισμός δεν ισχύει για τον result type της λειτουργίας.

Εάν δεν δίνεται κανένας refinement, ο κενός refinement προστίθεται σιωπηρά, δηλ. T1 with … with Tn είναι συντομογραφία για το T1 with … with Tn {}.

Ένας compound type μπορεί επίσης να αποτελείται από έναν refinement {R}

χωρίς τους προηγούμενους component types. Ένας τέτοιος τύπος είναι ισοδύναμος με AnyRef {R}.

2A η αναφορά σε ένα δομικά καθορισμένο μέλος (κλήση ή πρόσβαση μεθόδου σε μια value ή μια μεταβλητή) μπορεί να παραγάγει το δυαδικό κώδικα που είναι σημαντικά πιό αργός από έναν ισοδύναμο κώδικα σε ένα non-structural member.

(27)

Παράδειγμα 3.2.4 το ακόλουθο παράδειγμα μας δείχνει πώς να δηλώσουμε και να χρησιμοποιήσουμε μια λειτουργία όπου ο τύπος της παραμέτρου περιέχει έναν καθαρισμό με τις δομικές δηλώσεις.

case class Bird (val name: String) extends Object { def fly(height: Int) = ...

...

}

case class Plane (val callsign: String) extends Object { def fly(height: Int) = ...

...

}

def takeoff(

runway: Int,

r: { val callsign: String; def fly(height: Int) }) = {

tower.print(r.callsign + “ requests take-off on runway “ + runway) tower.read(r.callsign + “ is clear for takeoff”)

r.fly(1000) }

val bird = new Bird(“Polly the parrot”){ val callsign = name } val a380 = new Plane(“TZ987”)

takeoff(42, bird) takeoff(89, a380)

Αν και τα Bird και Plane δεν μοιράζονται άλλη parent class εκτός από την Object, η παράμετρος r της function takeoff καθορίζεται χρησιμοποιώντας έναν refinement με τις δομικές δηλώσεις για να δεχτεί οποιοδήποτε αντικείμενο που δηλώνει μία value callsign και μια fly function.

3.2.8 Infix Types

Σύνταξη :

InfixType ::= CompoundType {id [nl] CompoundType}

Ένας Infix Type T1 op T2 αποτελείται από έναν infix operator op που εφαρμόζεται σε δύο type operands T1 και T2. Ο τύπος είναι ισοδύναμος με το type application op[T1, T2]. Ο infix operator op μπορεί να είναι ένα αυθαίρετο identifier, εκτός από *, το οποίο είναι διατηρημένο ως postfix modifier δείχνοντας έναν επαναλαμβανόμενο type parameter (§4.6.2).

Όλοι οι infix operators έχουν την ίδια προτεραιότητα, οι παρενθέσεις πρέπει να χρησιμοποιηθούν για ομαδοποίηση. Το associativity (§6.12) ενός type operator καθορίζεται όπως για τους term operators: type operators που τελειώνουν σε μια άνω και κάτω τελεία ‘’ : ‘’ είναι right-associative: όλοι οι άλλοι χειριστές είναι left- associative.

Σε μια ακολουθία του διαδοχικού type infix, οι διαδικασίες t0 op1 t1 op2...opn tn, όλοι οι χειριστές op1,…, opn πρέπει να έχουν το ίδιο associativity. Εάν είναι όλοι left- associative, η ακολουθία ερμηνεύεται ως (… (t0 op1 t1) op2…) opn tn, διαφορετικά ερμηνεύεται ως t0 op1 (t1 op2 (...opn tn)…).

(28)

3.2.9 Function Types

Σύνταξη :

Type ::= InfixType ‘=>’ Type | ‘(’ [‘=>’ Type] ‘)’ ‘=>’ Type

Ο τύπος (T1,…, Tn) => U αντιπροσωπεύει το σύνολο των τιμών λειτουργίας, που παίρνουν τα επιχειρήματα των τύπων T1,…,Tn και τα παραγωγικά αποτελέσματα του τύπου U. Στην περίπτωση που έχουμε ακριβώς ένα επιχείρημα τύπου Τα =>

U είναι μια συντομογραφία για το (T) => U. Ο τύπος (=> Τ) => U αντιπροσωπεύει τις λειτουργίες με παραμέτρους call-by-name (§4.6.1) του τύπου Τα, που παράγουν τα αποτελέσματα του τύπου U. Οι τύποι function που είναι μάζι δεξιά, π.χ. S => Τ => U είναι το ίδιο με το S => (Τ => U).

Οι τύποι function είναι shorthands, για τύπους των class που καθορίζουν apply functions. Συγκεκριμένα, ο τύπος function n-ary , (T1,…, Tn) => U είναι μια συντομογραφία για τον class type Functionn[T1,…, Tn, U]. Τέτοιοι class types καθορίζονται στη βιβλιοθήκη της Scala, για n μεταξύ 0 και 9 ως εξής:

package scala

trait Functionn[-T1,. . . , Tn,+R] { def apply(x1: T1, . . . , xn: Tn): R override def toString = “<function>”

}

Ως εκ τούτου, οι τύποι function είναι covariant (§4.5) στον τύπο αποτελέσματός τους και contravariant στους argument τύπους τους.

Ένας τύπος function call-by-name (=> Τ) => U είναι μια συντομογραφία για τον class type ByNameFunction [Τ, U], ο οποίος καθορίζεται ως εξής:

package scala

trait ByNameFunction[-T,+R] { def apply(x: => T): R

override def toString = “<function>”

}

3.2.10 Τύποι existential

Σύνταξη :

Type ::= InfixType ExistentialClauses ExistentialClauses ::= ‘forSome’ ‘{’ ExistentialDcl {semi ExistentialDcl} ‘}’

ExistentialDcl ::= ‘type’ TypeDcl | ‘val’ ValDcl

(29)

Ένας τύπος existential έχει το form Τ forSome {Q} όπου το Q είναι μια ακολουθία από type declarations §4.3. Αφήνουμε το t1 [tps1] >: L1 <: U1,…, tn [tpsn] >: Ln <: Un

να είναι οι τύποι που δηλώνονται στο Q (οποιαδήποτε από τα τμήματα παραμέτρου τύπων [tpsi] μπορεί να λείπουν). Το πεδίο από κάθε τύπο Tj

περιλαμβάνει τον τύπο Τ και την existential clause Q. Οι μεταβλητές τύπων Ti, θεωρούνται συνδεδεμένες στον τύπο Τ forSome {Q}. Μεταβλητές τύπων που εμφανίζονται μέσα σε ένα τύπο Τ αλλά δεν είναι συνδεδεμένες στο Τ, θεωρούνται ελεύθερες στο Τ.

Μια περίπτωση τύπων του Τ forSome {Q} είναι ένας τύπος σΤ όπου το σ αντικαθιστά το t1,…, tn έτσι ώστε, για κάθε i, σLi<:σti<:σUi. Το σύνολο τιμών που δείχνονται από τον τύπο existential Τ forSome {Q} είναι η ένωση του συνόλου τιμών όλων των περιπτώσεων τύπων του.

Μία skolemization του Τ forSome {Q} είναι μια περίπτωση τύπων σΤ, όπου σ είναι η αντικατάσταση [t΄1 /t1,…, t΄n /tn] και κάθε t΄i είναι ένας τύπος fresh abstract με lower bound sLi και upper bound σUi.

Κανόνες απλοποίησης

Οι existential types υπακούνε τις ακόλουθες τέσσερις ισοδυναμίες:

1. Οι multiple for-clauses σε έναν existential type, μπορούν να συγχωνευθούν.

Π.χ., Τ forSome {Q} forSome {Q’} είναι ισοδύναμο με το Τ forSome {Q ; Q’}.

2. Τα αχρησιμοποίητα quantifications μπορούν να πέσουν. Π.χ., Τ forSome {Q

; Q’} όπου κανένας από τους τύπους που καθορίζονται Q’ δεν αναφέρεται από το Τ ή το Q, είναι ισοδύναμο με το Τ forSome {Q}.

3. Έναν κενό προσδιορισμό της ποσότητας μπορεί να πέσουν. Π.χ., το Τ forSome {} είναι ισοδύναμο με το Τ.

4. Ένας υπαρξιακός τύπος Τ forSome {Q} όπου το Q περιέχει μια πρόταση type t [tps] > : L< : U είναι ισοδύναμο με το forSome τύπων Τ’ {Q} όπου το Τ’

προκύπτει από το Τ με την αντικατάσταση κάθε covariant περιστατικού (§4.5) του t στο Τ από το U και με την αντικατάσταση κάθε contravariant περιστατικού του t στο Τ από το L.

Υπαρξιακός προσδιορισμός της ποσότητας πέρα από τις τιμές

Σαν συντακτική ευκολία, η πρόταση συνδέσεων σε έναν υπαρξιακό τύπο μπορεί επίσης να περιέχει τις δηλώσεις αξίας val x: Τ. Ένας υπαρξιακός τύπος Τ forSome {Q val x: S; Q’} αντιμετωπίζεται ως συντομογραφία για τον τύπο forSome {Q type t <: S with Singleton; Q’ }, όπου το t είναι ένα όνομα fresh type και το T’ προκύπτει από το T αντικαθιστόντας κάθε x.type με t.

(30)

Placeholder σύνταξη για τους υπαρξιακούς τύπους

Σύνταξη :

WildcardType ::= ‘_’ TypeBounds

H Scala υποστηρίζει μια placeholder σύνταξη για τους υπαρξιακούς τύπους. Ένας τύπος μπαλαντέρ είναι του εντύπου _ >: L < : U. Και οι δύο συνδεδεμένες προτάσεις μπορούν να παραλειφθούν. Εάν μια χαμ

Referências

Documentos relacionados