5 βήματα για τη δημιουργία της πρώτης σας κατηγορίας κατηγορίας στη Scala

Σε αυτή την ανάρτηση ιστολογίου θα μάθετε πώς να εφαρμόσετε την πρώτη σας τάξη τύπου, η οποία είναι θεμελιώδης γλώσσα στο εικονίδιο των λειτουργικών γλωσσών προγραμματισμού - Haskell.

Φωτογραφία από τον Stanley Dai στο Unsplash

Η Τάξη τύπου είναι ένα μοτίβο που προέρχεται από το Haskell και είναι ο τυπικός τρόπος εφαρμογής του πολυμορφισμού. Αυτός ο τύπος πολυμορφισμού ονομάζεται ad-hoc πολυμορφισμός. Το όνομά του προέρχεται από το γεγονός ότι σε αντίθεση με τον πολύ γνωστό πολυμορφισμό υποτύπων πληκτρολόγησης μπορούμε να επεκτείνουμε κάποια λειτουργικότητα της βιβλιοθήκης ακόμη και χωρίς να έχει πρόσβαση στον πηγαίο κώδικα της βιβλιοθήκης και της κλάσης σε ποια λειτουργικότητα θέλουμε να επεκταθεί.

Σε αυτή τη θέση θα δείτε ότι η χρήση των τάξεων τύπου μπορεί να είναι τόσο βολική όσο και η χρήση του κανονικού πολυμορφισμού OOP. Το παρακάτω περιεχόμενο θα σας οδηγήσει σε όλες τις φάσεις της εφαρμογής του μοντέλου Κλάσης Τύπου για να σας βοηθήσει να κατανοήσετε καλύτερα τις εσωτερικές λειτουργικές βιβλιοθήκες προγραμματισμού.

Δημιουργία της πρώτης σας κατηγορίας τύπου

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

Ως παράδειγμα σε αυτό το άρθρο θα χρησιμοποιήσω την Eq Type Class από τη βιβλιοθήκη Cats.

χαρακτηριστικό Eq [A] {
  def είναι ισοδύναμα (α: Α, β: Α): Boolean
}}

Τύπος Η τάξη Eq [A] είναι μια σύμβαση ικανή να ελέγξει εάν δύο αντικείμενα τύπου Α είναι ίσα με βάση κάποια κριτήρια που εφαρμόζονται στην μέθοδο isEquals.

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

def modoEq (διαιρέτης: Int): Eq [Int] = νέο Eq [Int] {
 (a: Int, b: Int) = α διαιρέτης == b% διαιρέτης
}}
implicτική τιμή val modulo5Eq: Eq [Int] = moduloEq (5)

Το παραπάνω κομμάτι κώδικα μπορεί να συμπιεστεί λίγο σε μια ακόλουθη φόρμα.

def modoEq: Eq [Int] = (a: Int, b: Int) => α% 5 == β% 5

Αλλά περιμένετε, πώς μπορείτε να εκχωρήσετε τη συνάρτηση (Int, Int) => Boolean για αναφορά με τον τύπο Eq [Int] ;! Αυτό το πράγμα είναι δυνατό χάρη στη λειτουργία Java 8 που ονομάζεται διεπαφή τύπου αφηρημένης μεθόδου. Μπορούμε να κάνουμε κάτι τέτοιο όταν έχουμε μόνο μία αφηρημένη μέθοδο στο χαρακτηριστικό μας.

Πληκτρολογήστε ανάλυση κλάσης

Σε αυτή την παράγραφο θα σας δείξω πώς να χρησιμοποιείτε τις παρουσίες κλάσης τύπου και πώς να συνδέετε μαγικά μεταξύ των κατηγοριών τύπου Eq [A] με το αντίστοιχο αντικείμενο του τύπου Α όταν αυτό είναι απαραίτητο.

Εδώ εφαρμόσαμε τη λειτουργία σύγκρισης δύο τιμών Int ελέγχοντας αν οι τιμές διαίρεσης modulo τους είναι ίσες. Με όλη αυτή τη δουλειά, μπορούμε να χρησιμοποιήσουμε την Τάξη Τύπου για την εκτέλεση κάποιας επιχειρησιακής λογικής, π.χ. θέλουμε να αντιστοιχίσουμε δύο αξίες που είναι μονόδρομες.

(A: A, b: A) (implicit eq: Eq [A]): ​​Η επιλογή [(A, A)] =
 αν (eq.areEquals (a, b)) Ορισμένες ((a, b)) αλλιώς Καμία
}}

Έχουμε παραμετροποιήσει τη συνάρτηση pairEquals για να δουλέψουμε με όλους τους τύπους παρέχοντας στιγμιότυπο της κλάσης Eq [A] διαθέσιμο στο σιωπηρό πεδίο εφαρμογής.

Όταν ο μεταγλωττιστής δεν θα βρει καμία εμφάνιση που να ταιριάζει με την παραπάνω δήλωση, θα καταλήξει σε προειδοποίηση σφάλματος σύνταξης σχετικά με την έλλειψη κατάλληλης παρουσίας σε παρεχόμενο σιωπηρό πεδίο.
  1. Ο μεταγλωττιστής θα συμπεράνει τον τύπο παρεχόμενων παραμέτρων εφαρμόζοντας επιχειρήματα στη συνάρτηση μας και την εκχωρεί στο ψευδώνυμο Α
  2. Προηγούμενο όρισμα eq: Το Eq [A] με τη σιωπηρή λέξη-κλειδί θα ενεργοποιήσει την πρόταση να αναζητήσουμε αντικείμενα τύπου Eq [A] σε σιωπηρά πλαίσια.

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

Έχουν καθοριστεί όλες οι παρουσίες και λειτουργίες, ας ελέγξουμε εάν ο κωδικός μας δίνει έγκυρα αποτελέσματα

pairEquals (2,7)
res0: Επιλογή [(Int, Int)] = Μερικοί ((2,7))
pairEquals (2,3)
res0: Επιλογή [(Int, Int)] = Καμία

Όπως βλέπετε, λάβαμε τα αναμενόμενα αποτελέσματα, ώστε η τάξη τύπου μας να έχει καλές επιδόσεις. Αλλά αυτό φαίνεται λίγο γεμάτο, με το σωστό ποσό boilerplate. Χάρη στη μαγεία της σύνταξης της Σκάλας μπορούμε να βγάλουμε πολλά λεωφορεία.

Συνδέσεις πλαισίου

Το πρώτο πράγμα που θέλω να βελτιώσω στον κώδικα μας είναι να απαλλαγούμε από τον κατάλογο δεύτερων παραδειγμάτων (με τη χρήση σιωπηρής λέξης-κλειδιού). Δεν μεταφέρουμε απευθείας αυτό το ένα κατά την κλήση της συνάρτησης, οπότε αφήστε τις σιωπηρές να είναι σιωπηρές ξανά. Στα σιωπηρά επιχειρήματα Scala με παραμέτρους τύπου μπορούν να αντικατασταθούν από τη γλωσσική κατασκευή που ονομάζεται Context Bound.

Το Context Bound είναι δήλωση στην λίστα παραμέτρων τύπου που η σύνταξη Α: Eq λέει ότι κάθε τύπος που χρησιμοποιείται ως όρισμα της συνάρτησης pairEquals πρέπει να έχει την implicit τιμή του τύπου Eq [A] στο σιωπηλό πεδίο.

(A: Eq) (a: A, b: A): Η επιλογή [(A, A)] = {
 αν (έμμεσα [Eq [A]] areEquals (a, b)) Κάποια ((a, b)
}}

Όπως παρατηρήσατε, δεν καταλήξαμε σε καμία αναφορά στην έμμεση αξία. Για να ξεπεραστεί αυτό το πρόβλημα χρησιμοποιούμε τη συνάρτηση implicitly [F [_]] η οποία τραβά την εξαγώμενη τιμή που έχει βρεθεί καθορίζοντας τον τύπο στον οποίο αναφερόμαστε.

Αυτό μας προσφέρει η Scala γλώσσα για να το κάνουμε πιο συνοπτικό. Δεν μου φαίνεται αρκετά καλό για μένα. Το Context Bound είναι μια πραγματικά δροσερή συντακτική ζάχαρη, αλλά αυτό σιωπηρά μοιάζει να μολύνει τον κώδικα μας. Θα κάνω ένα ωραίο τέχνασμα πώς να ξεπεράσω αυτό το πρόβλημα και να μειώσω την ερμηνεία της εφαρμογής μας.

Αυτό που μπορούμε να κάνουμε είναι να παρέχουμε παραμετροποιημένη λειτουργία εφαρμογής σε συνοδευτικό αντικείμενο της τάξης τύπου μας.

αντικείμενο Eq {
 def [A] (implicit eq: Eq [A]): ​​Eq [A] = eq
}}

Αυτό το πολύ απλό πράγμα μας επιτρέπει να ξεφορτωθούμε σιωπηρά και να τραβήξουμε το παράδειγμα μας από το κενό για να το χρησιμοποιήσουμε στη λογική του τομέα χωρίς boilerplate.

(A: Eq) (a: A, b: A): Η επιλογή [(A, A)] = {
 αν (Eq [A] .areEquals (a, b)) Κάποια ((a, b)) αλλιώς Κανένα
}}

Ενεργοποιημένες μετατροπές - γνωστός και ως. Μονάδα σύνταξης

Το επόμενο πράγμα που θέλω να φτάσω στον πάγκο εργασίας μου είναι το Eq [A] .areEquals (a, b). Αυτή η σύνταξη φαίνεται πολύ λεπτομερή επειδή αναφέρουμε ρητά την εμφάνιση κλάσης τύπου που πρέπει να είναι σιωπηρή, σωστά; Δεύτερο πράγμα είναι ότι εδώ το παράδειγμα κλάσης τύπου μας δρα σαν Υπηρεσία (σε DDD έννοια) αντί για πραγματική επέκταση κλάσης Α. Ευτυχώς, αυτό μπορεί επίσης να διορθωθεί με τη βοήθεια μιας άλλης χρήσιμης χρήσης σιωπηρής λέξης-κλειδιού.

Αυτό που θα κάνουμε εδώ είναι η παροχή της λεγόμενης σύνταξης ή (ops όπως σε μερικές βιβλιοθήκες FP) με τη χρήση σιωπηρών μετατροπών που μας επιτρέπουν να επεκτείνουμε το API κάποιου κλάδου χωρίς τροποποίηση του πηγαίου κώδικα.

η έμμεση κλάση EqSyntax [A: Eq] (a: A) {
 def === (b: A): Boolean = Eq [Α] .Αριθμός (a, b)
}}

Αυτός ο κώδικας λέει στον μεταγλωττιστή να μετατρέψει την κλάση Α στην περίπτωση της κλάσης τύπου Eq [A] στην κλάση EqSyntax που έχει μία συνάρτηση ===. Όλα αυτά τα πράγματα δημιουργούν την εντύπωση ότι προσθέσαμε λειτουργία === στην κλάση Α χωρίς τροποποίηση του πηγαίου κώδικα.

Έχουμε όχι μόνο την κρυφή αναφορά κλασικού τύπου, αλλά και την σύνταξη πιο κατηγορίας που κάνει την εντύπωση της μεθόδου === να εφαρμοστεί στην τάξη Α ακόμα κι αν δεν γνωρίζουμε τίποτα για αυτή την τάξη. Δύο πουλιά σκοτώθηκαν με μια πέτρα.

Τώρα επιτρέπεται η εφαρμογή της μεθόδου === στον τύπο Α κάθε φορά που έχουμε την κλάση EqSyntax στο πεδίο εφαρμογής. Τώρα η εφαρμογή του pairEquals θα αλλάξει λίγο, και θα είναι η εξής.

(A: Eq) (a: A, b: A): Η επιλογή [(A, A)] = {
 αν (a === β) Κάποιες ((a, b)) αλλιώς Κανένα
}}

Όπως υποσχέθηκα, καταλήξαμε στην εφαρμογή, όπου η μόνη ορατή διαφορά σε σχέση με την εφαρμογή OOP είναι η παραπομπή πλαισίου πλαισίου μετά από μια παράμετρο τύπου. Όλες οι τεχνικές πτυχές της κατηγορίας κατηγορίας διαχωρίζονται από τη λογική του τομέα μας. Αυτό σημαίνει ότι μπορείτε να επιτύχετε τρόπο πιο δροσερό υλικό (το οποίο θα αναφερθώ στο ξεχωριστό άρθρο που θα δημοσιευθεί σύντομα) χωρίς να σας βλάψει τον κώδικα.

Τυπικό πεδίο εφαρμογής

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

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

1. Τοπικές και κληρονομούμενες περιπτώσεις
2. Εισαγόμενα στιγμιότυπα
3. Ορισμοί από το συνοδευτικό αντικείμενο της κατηγορίας τύπου ή τις παραμέτρους

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

Έτσι, μπορείτε να συζητήσετε το σημείο 3 χρησιμοποιώντας παράδειγμα γνωστής λειτουργίας από τη συνηθισμένη βιβλιοθήκη της Scala που ταξινομεί ποιες λειτουργίες βασίζονται σε έμμεσα παρασχεθέντες συγκριτές.

ταξινομημένο [B>: A] (σφάλμα: math.Ordering [B]): Λίστα [Α]

Η εμφάνιση κλάσης τύπου θα αναζητηθεί σε:
 * Παραγγελία αντικειμένου σύντροφο
 * Λίστα σύντροφος αντικείμενο
 * B συνοδευτικό αντικείμενο (το οποίο μπορεί επίσης να είναι ένα σύντροφο αντικείμενο λόγω της ύπαρξης ορισμού κατώτερων ορίων)

Ομοίωμα

Αυτά τα πράγματα βοηθούν πολύ όταν χρησιμοποιείτε μοτίβο τύπου, αλλά αυτά είναι επαναλαμβανόμενη εργασία που πρέπει να γίνει σε κάθε έργο. Αυτές οι ενδείξεις είναι ένα προφανές σημάδι ότι η διαδικασία μπορεί να εξαχθεί στη βιβλιοθήκη. Υπάρχει μια εξαιρετική μακρο-βασισμένη βιβλιοθήκη που ονομάζεται Simulacrum, η οποία χειρίζεται όλα τα πράγματα που απαιτούνται για τη δημιουργία module syntax (που ονομάζεται ops στο Simulacrum) κλπ. Με το χέρι.

Η μόνη αλλαγή που πρέπει να εισαγάγουμε είναι ο σχολιασμός @typelass που είναι το σήμα για τις μακροεντολές για την επέκταση της ενότητας σύνταξης.

εισαγωγή simulacrum._
Χαρακτηριστικό @typelass Eq [A] {
 Το @op ("===") def είναιEquals (a: A, b: A): Boolean
}}

Τα υπόλοιπα μέρη της εφαρμογής μας δεν απαιτούν αλλαγές. Αυτό είναι όλο. Τώρα ξέρετε πώς να εφαρμόσετε μοτίβο τάξης τύπου στη Scala από τη δική σας και ελπίζω να έχετε αποκτήσει συνειδητοποίηση για το πώς οι βιβλιοθήκες όπως το Simulacrum δουλεύουν.

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