Προβολές σύνδεσης και αποσύνδεσης. Μοντέλο-Ενημέρωση-Προβολή μοτίβου και εξαρτημένων τύπων Σύνολο προβολής html αποσύνδεσης

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

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

  • Σύνδεση
  • Αποσύνδεση
  • Εγγραφείτε
  • ΕΠΑΝΑΦΟΡΑ ΚΩΔΙΚΟΥ

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

Ξεκινώντας

Πριν ξεκινήσουμε, βεβαιωθείτε ότι έχετε το django.contrib.auth στις INSTALLED_APPS σας και ότι το ενδιάμεσο λογισμικό ελέγχου ταυτότητας έχει διαμορφωθεί σωστά στις ρυθμίσεις MIDDLEWARE_CLASSES.

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

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

$ python manager.py createsuperuser

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

Διαμορφώστε τις διαδρομές URL

Πρώτα εισάγετε τη λειτουργική μονάδα django.contrib.auth.views και προσθέστε μια διαδρομή URL για τις προβολές σύνδεσης και αποσύνδεσης:

από django.conf.urls εισαγωγή url από django.contrib εισαγωγή διαχειριστή από django.contrib.auth προβολές εισαγωγής ως auth_views urlpatterns = [ url (r"^login/$" , auth_views . login , name = "login" ), url ( r"^logout/$" , auth_views , όνομα = "logout" ), url (r"^admin/" , admin. site . urls ), ]

Δημιουργήστε ένα πρότυπο σύνδεσης

Από προεπιλογή, η προβολή django.contrib.auth.views.login θα προσπαθήσει να αποδώσει το πρότυπο register/login.html. Έτσι, η βασική διαμόρφωση θα ήταν η δημιουργία ενός φακέλου με το όνομα εγγραφή και η τοποθέτηση ενός προτύπου login.html μέσα.

Ακολουθώντας ένα ελάχιστο πρότυπο σύνδεσης:

(% επεκτείνει το "base.html" %) (% block title )Είσοδος(% endblock %) (% block περιεχόμενο %)

Σύνδεση

(% csrf_token %) (( form.as_p ))
(%endblock%)

Αυτό το απλό παράδειγμα επικυρώνει ήδη το όνομα χρήστη και τον κωδικό πρόσβασης και τον σωστό έλεγχο ταυτότητας του χρήστη.

Προσαρμογή της προβολής σύνδεσης

Υπάρχουν μερικές παράμετροι που μπορείτε να μεταβιβάσετε στην προβολή σύνδεσης για να ταιριάζει στο έργο σας. Για παράδειγμα, εάν θέλετε να αποθηκεύσετε το πρότυπο σύνδεσής σας κάπου αλλού εκτός από το register/login.html, μπορείτε να μεταβιβάσετε το όνομα του προτύπου ως παράμετρο:

url (r"^login/$" , auth_views . login , ( "template_name" : "core/login.html" ), name = "login" ),

Μπορείτε επίσης να περάσετε μια προσαρμοσμένη φόρμα ελέγχου ταυτότητας χρησιμοποιώντας την παράμετρο authentication_form , σε περίπτωση που έχετε εφαρμόσει ένα προσαρμοσμένο μοντέλο χρήστη.

Τώρα, γίνεται μια πολύ σημαντική διαμόρφωση στο αρχείο settings.py, το οποίο είναι η διεύθυνση URL που θα ανακατευθύνει ο χρήστης από το Django μετά από έναν επιτυχημένο έλεγχο ταυτότητας.

Μέσα στο αρχείο settings.py προσθέστε:

LOGIN_REDIRECT_URL = "σπίτι"

Η τιμή μπορεί να είναι μια διεύθυνση URL με σκληρό κώδικα ή ένα όνομα διεύθυνσης URL. Η προεπιλεγμένη τιμή για το LOGIN_REDIRECT_URL είναι /accounts/profile/.

Είναι επίσης σημαντικό να σημειωθεί ότι το Django θα προσπαθήσει να ανακατευθύνει τον χρήστη στην επόμενη παράμετρο GET.

Ρύθμιση προβολής αποσύνδεσης

Μετά την πρόσβαση στην προβολή django.contrib.auth.views.logout, το Django θα αποδώσει το πρότυπο register/logged_out.html. Με παρόμοιο τρόπο όπως κάναμε στην προβολή σύνδεσης, μπορείτε να περάσετε ένα διαφορετικό πρότυπο όπως:

url (r"^logout/$" , auth_views . logout , ( "template_name" : "logged_out.html" ), name = "logout" ),

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

Αυτό το παράδειγμα δείχνει πώς να αποσυνδεθείτε αυτόματα με την προεπιλεγμένη διαμόρφωση ασφαλείας Spring.

Για να αποσυνδεθούμε, χρειάζεται απλώς πρόσβαση στη διεύθυνση URL "/logout" με αίτημα POST.

Στη φόρμα POST /logout, πρέπει επίσης να συμπεριλάβουμε το διακριτικό CSRF, το οποίο αποτελεί προστασία έναντι της επίθεσης CSRF .

Ας δούμε το παράδειγμα πώς να το κάνουμε αυτό.

Κλάση Java Config

@Configuration @EnableWebSecurity @EnableWebMvc @ComponentScan η δημόσια κλάση AppConfig επεκτείνει το WebSecurityConfigurerAdapter ( προστατευμένο void configure(HttpSecurity http) ρίχνει Εξαίρεση ( http.authorizeRequests() .anyRequests() .anyRequest()(O) δημόσιο void configure(AuthenticationManagerBuilder builder) ρίχνει Exception ( builder.inMemoryAuthentication() .withUser("joe") .password("123") .roles("ADMIN"); ) @Bean public ViewResolver viewResolver =ResolverResourceViewResolver() (NewResolverResourceViewResolverView (") viewResolver.setPrefix("/WEB-INF/views/");

Σημειώστε ότι, στην παραπάνω διαμόρφωση, παρακάμπτουμε επίσης τη ρύθμιση παραμέτρων (HttpSecurity http) για να παραλείψουμε τον προεπιλεγμένο Βασικό έλεγχο ταυτότητας (δείτε την αρχική μέθοδο στον πηγαίο κώδικα WebSecurityConfigurerAdapter) και χρησιμοποιούμε Έλεγχο ταυτότητας βάσει φόρμας. Το κάνουμε επειδή τα προγράμματα περιήγησης αποθηκεύουν τις πληροφορίες Βασικού ελέγχου ταυτότητας επιθετικά (μετά την πρώτη επιτυχημένη σύνδεση) και δεν υπάρχει τρόπος να αποσυνδεθείτε από τον χρήστη στην τρέχουσα περίοδο λειτουργίας. Στα περισσότερα από τα παραδείγματα, δεν θα χρησιμοποιήσουμε τον βασικό μηχανισμό ελέγχου ταυτότητας.

Ένας ελεγκτής

Δημόσια κλάση @Controller ExampleController ( @RequestMapping("/") δημόσια συμβολοσειρά handleRequest2 (χάρτης ModelMap) ( map.addAttribute("time", LocalDateTime.now().toString()); επιστροφή "my-page"; ) )

Η σελίδα JSP

src/main/webapp/WEB-INF/views/my-page.jsp

Παράδειγμα ελατηρίου ασφάλειας

Χρόνος: $ (χρόνος)

Για να δοκιμάσετε παραδείγματα, εκτελέστε το embedded tomcat (που έχει διαμορφωθεί στο pom.xml του παραδείγματος έργου παρακάτω):

Mvn tomcat7: run-war

Παραγωγή

Η αρχική πρόσβαση στο URI "/" θα ανακατευθύνει στο "/login":

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

Κάνοντας κλικ στο κουμπί "Αποσύνδεση":


Παράδειγμα έργου

Εξαρτήσεις και τεχνολογίες που χρησιμοποιούνται:

  • spring-security-web 4.2.3.RELEASE: spring-security-web.
  • spring-security-config 4.2.3.RELEASE: spring-security-config.
  • Spring-webmvc 4.3.9.RELEASE: Spring Web MVC.
  • javax.servlet-api 3.1.0 Java Servlet API
  • JDK 1.8
  • Maven 3.3.9

Επεξεργαστείτε το αρχείο urls.pyεφαρμογές λογαριασμός:

από το django.conf.urls εισαγωγή url από . εισαγωγή προβολών urlpatterns = [ # προηγούμενη προβολή σύνδεσης # url(r"^login/$", views.user_login, name="login"),# url url σύνδεσης / αποσύνδεσης(r"^login/$" , "django.contrib.auth.views.login", name="login" ), url(r"^logout/$" , "django.contrib.auth.views.logout", name="logout" ), url(r"^logout-then-login/$" , "django.contrib.auth.views.logout_then_login", name="logout_then_login" ), ]

Έχουμε σχολιάσει το πρότυπο URL για την προβολή σύνδεση χρήστη, δημιουργήθηκε νωρίτερα για χρήση της προβολής Σύνδεση Django.

Δημιουργήστε έναν νέο κατάλογο στον κατάλογο προτύπων εφαρμογών λογαριασμόςκαι ονομάστε το εγγραφή.Δημιουργήστε ένα νέο αρχείο σε έναν νέο κατάλογο, ονομάστε το login.html

(% επεκτείνει το "base.html" %) (% τίτλος αποκλεισμού )Σύνδεση(% endblock %) (% περιεχόμενο αποκλεισμού %)

Σύνδεση

(% εάν μορφή.λάθη %)

Το όνομα χρήστη και ο κωδικός πρόσβασής σας δεν ταιριάζουν. Δοκιμάστε ξανά.

(%αλλού%)

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

(%τέλος εαν%) (%endblock%)

Αυτό το πρότυπο σύνδεσης είναι πολύ παρόμοιο με αυτό που δημιουργήθηκε νωρίτερα. Το Django χρησιμοποιεί Φόρμα ελέγχου ταυτότητας, που βρίσκεται στην django.contrib.auth.forms. Αυτή η φόρμα επιχειρεί να ελέγξει την ταυτότητα του χρήστη και δημιουργεί ένα σφάλμα επικύρωσης εάν το όνομα χρήστη ήταν εσφαλμένο. Σε αυτήν την περίπτωση, μπορούμε να αναζητήσουμε σφάλματα χρησιμοποιώντας την εντολή (% if form.errors %) . Λάβετε υπόψη ότι έχουμε προσθέσει ένα κρυφό στοιχείο για να στείλετε την τιμή μιας μεταβλητής με όνομα Επόμενο.

Παράμετρος Επόμενοπρέπει να είναι URL. Εάν έχει καθοριστεί αυτή η παράμετρος, τότε αφού συνδεθεί ο χρήστης, ανακατευθύνεται στην καθορισμένη διεύθυνση URL.

Τώρα δημιουργήστε ένα πρότυπο logged_out.htmlμέσα στον κατάλογο προτύπων εγγραφήκαι επικολλήστε τον ακόλουθο κώδικα σε αυτό:

(% επεκτείνει το "base.html" %) (% block title )Έχει αποσυνδεθεί (% endblock %) (% block περιεχόμενο %)

Αποσυνδέθηκα

Έχετε αποσυνδεθεί επιτυχώς. Μπορείτε να συνδεθείτε ξανά.

(%endblock%)

Αυτό είναι το πρότυπο που θα εμφανιστεί μετά τη σύνδεση του χρήστη.

Αφού προσθέσετε τα πρότυπα URL και τα πρότυπα για τις προβολές εισόδου και εξόδου, ο ιστότοπος είναι έτοιμος για συνδέσεις χρησιμοποιώντας τις προβολές ελέγχου ταυτότητας του Django.

Σημειώστε ότι η παρουσίαση logout_then_login, περιλαμβάνονται στο δικό μας urlconf, δεν χρειάζεται πρότυπο αφού ανακατευθύνει σε συνδεθείτε στην προβολή.

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

από το django.contrib.auth.decorators εισαγωγή login_required @login_required Def ταμπλό(αίτημα): return render( request, "account/dashboard.html" , ("section" : "dashboard" ))

Προσθέτουμε έναν διακοσμητή στην άποψή μας Απαιτείται σύνδεσηπλαίσιο ελέγχου ταυτότητας. Διακοσμητής Απαιτείται σύνδεσηελέγχει εάν ο τρέχων χρήστης έχει πιστοποιηθεί. Εάν ο χρήστης έχει πιστοποιηθεί, η υποβολή θα εκτελεστεί. Εάν ο χρήστης δεν έχει πιστοποιηθεί, θα ανακατευθυνθεί στη σελίδα σύνδεσης.

Ορίσαμε επίσης μια μεταβλητή Ενότητα. Θα χρησιμοποιήσουμε αυτήν τη μεταβλητή για να παρακολουθήσουμε ποια ενότητα του ιστότοπου παρακολουθεί ο χρήστης.

Τώρα πρέπει να δημιουργήσετε ένα πρότυπο για την προβολή πίνακα εργαλείων. Δημιουργήστε ένα νέο αρχείο μέσα σε πρότυπα/λογαριασμό πρότυπα/λογαριασμός/και ονομάστε το dashboard.html :

(% επεκτείνει το "base.html" %) (% block title )Πίνακας ελέγχου(% endblock %) (% περιεχόμενο μπλοκ %)

Ταμπλό

Καλώς ορίσατε στον πίνακα ελέγχου σας.

(%endblock%)

Στη συνέχεια, προσθέστε το ακόλουθο μοτίβο διεύθυνσης URL για αυτό το αρχείο αλλαγής urls.pyεφαρμογές λογαριασμός:

Urlpatterns = [ # ... url(r"^$" , views.dashboard, name="dashboard" ), ]

Τώρα επεξεργαστείτε το αρχείο settings.py:

από το django.core.urlresolvers εισαγωγή reverse_lazy LOGIN_REDIRECT_URL = reverse_lazy("dashboard" ) LOGIN_URL = reverse_lazy("login") LOGOUT_URL = reverse_lazy("logout" )
  • LOGIN_REDIRECT_URL: Υποδεικνύει σε ποια διεύθυνση URL να ανακατευθύνει τον χρήστη μετά τη σύνδεση.
  • LOGIN_URL: URL για ανακατεύθυνση του χρήστη για σύνδεση (για παράδειγμα, χρησιμοποιώντας διακοσμητή Απαιτείται σύνδεση)
  • LOGOUT_URL: URL για ανακατεύθυνση του χρήστη για έξοδο

Τώρα θα προσθέσουμε συνδέσμους σύνδεσης και αποσύνδεσης στο βασικό μας πρότυπο.

Για να γίνει αυτό, είναι απαραίτητο να προσδιοριστεί εάν ο τρέχων χρήστης είναι συνδεδεμένος ή όχι, προκειμένου να εμφανιστεί ένας σύνδεσμος που αντιστοιχεί στην τρέχουσα κατάσταση χρήστη. Ο τρέχων χρήστης καθορίζεται στο HttpRequestαντικείμενο της ενδιάμεσης κλάσης ελέγχου ταυτότητας. Μπορεί να προσπελαστεί χρησιμοποιώντας αίτημα.χρήστης. Το αίτημα θα βρει ένα αντικείμενο χρήστη, ακόμα κι αν ο χρήστης δεν έχει πιστοποιηθεί. Χρήστης χωρίς έλεγχο ταυτότητας, που προσδιορίζεται στο αίτημα ως παράδειγμα Ανώνυμος Χρήστης. Ο καλύτερος τρόπος για να ελέγξετε την κατάσταση ελέγχου ταυτότητας του τρέχοντος χρήστη είναι να καλέσετε request.user.is_authenticated()

Επεξεργαστείτε στο template base.html

με κεφαλίδα αναγνωριστικού:

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

  • για να επισημάνετε την τρέχουσα ενότητα στο μενού χρησιμοποιώντας CSS. Εμφανίζει επίσης το όνομα χρήστη και έναν σύνδεσμο για να αποσυνδεθείτε εάν ο χρήστης έχει πιστοποιηθεί ή έναν σύνδεσμο για να συνδεθείτε.

    Ανοίξτε το http://127.0.0.1:8000/account/login/ στο πρόγραμμα περιήγησής σας. Θα πρέπει να δείτε μια σελίδα σύνδεσης. Εισαγάγετε ένα έγκυρο login και κωδικό πρόσβασης. Θα δείτε τα εξής:

    Μπορείτε να δείτε ότι η ενότητα Ο πίνακας ελέγχου μου επισημαίνεται με CSS καθώς έχει μια κλάση επιλεγμένο. Εφόσον ο χρήστης έχει πιστοποιηθεί, το όνομα χρήστη εμφανίζεται στη δεξιά πλευρά της κεφαλίδας. Κάντε κλικ στον σύνδεσμο Αποσύνδεση. Θα δείτε την παρακάτω σελίδα:

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

    Εάν βλέπετε τη σελίδα αποσύνδεσης από τον ιστότοπο διαχειριστή του Django και όχι τη δική σας σελίδα αποσύνδεσης, ελέγξτε τις ρυθμίσεις INSTALLED_APPS και βεβαιωθείτε ότι django.contrib.adminείναι μετά λογαριασμός. Και τα δύο πρότυπα βρίσκονται στην ίδια σχετική διαδρομή και ο φορτωτής προτύπων Django θα χρησιμοποιήσει το πρώτο που θα βρει.

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

    Ένα παρόμοιο πρόβλημα προκύπτει (και επιλύεται) όταν χρησιμοποιείται το μοτίβο State OO.

    Η γλώσσα Elm είναι απλή, αλλά πολύ αυστηρή - ελέγχει ότι η λειτουργία ενημέρωσης χειρίζεται με κάποιο τρόπο όλους τους πιθανούς συνδυασμούς κατάστασης μοντέλου και συμβάντων μηνύματος. Επομένως, πρέπει να γράψετε επιπλέον, αν και ασήμαντο, κώδικα που συνήθως αφήνει το μοντέλο αμετάβλητο. Θέλω να δείξω πώς μπορεί να αποφευχθεί αυτό σε πιο σύνθετες γλώσσες - Idris, Scala, C++ και Haskell.

    Όλος ο κώδικας που εμφανίζεται εδώ είναι διαθέσιμος στο GitHub για πειραματισμό. Ας δούμε τα πιο ενδιαφέροντα μέρη.


    Η συνάρτηση msg είναι ασυνήθιστη - επιστρέφει έναν τύπο, όχι μια τιμή. Κατά την εκτέλεση, τίποτα δεν είναι γνωστό για τους τύπους τιμών - ο μεταγλωττιστής διαγράφει όλες τις περιττές πληροφορίες. Δηλαδή, μια τέτοια συνάρτηση μπορεί να κληθεί μόνο στο στάδιο της μεταγλώττισης.

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

    Τώρα ας δούμε πώς να εκκινήσετε αυτήν την εφαρμογή

    MuvRun: (Application modelType msgType IO) -> IO a muvRun (MUV model updater view) = do msg<- view model muvRun (MUV (updater model msg) updater view)
    Επιλέξαμε μια λειτουργία εισόδου/εξόδου ως εξωτερική αναπαράσταση (προβολή) (στο Idris, όπως και στο Haskell, οι πράξεις εισόδου/εξόδου είναι τιμές πρώτης κατηγορίας· για να εκτελεστούν, πρέπει να γίνουν πρόσθετες ενέργειες, συνήθως επιστρέφοντας μια τέτοια πράξη από την κύρια λειτουργία).

    Εν συντομία για το IO

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


    Εφόσον η συνάρτηση muvRun δημιουργεί I/O, θα πρέπει να επιστρέψει IO, αλλά επειδή δεν θα ολοκληρωθεί ποτέ, ο τύπος λειτουργίας μπορεί να είναι οτιδήποτε - IO a.

    Τώρα ας περιγράψουμε τους τύπους οντοτήτων με τους οποίους πρόκειται να εργαστούμε

    Μοντέλο δεδομένων = Αποσύνδεση | Δεδομένα συμβολοσειράς σύνδεσης MsgOuted = Δεδομένα συμβολοσειράς σύνδεσης MsgIned = Αποσύνδεση | Χαιρετίστε το συνολικό msgType: Model -> Πληκτρολογήστε msgType Logouted = MsgOuted msgType (Logined _) = MsgIned
    Εδώ περιγράφουμε έναν τύπο μοντέλου που αντικατοπτρίζει την παρουσία δύο καταστάσεων διεπαφής - ο χρήστης δεν είναι συνδεδεμένος και ο χρήστης με όνομα τύπου String είναι συνδεδεμένος.

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

    Και τέλος, μια συνάρτηση που ορίζει την αντιστοιχία της τιμής του μοντέλου στον τύπο μηνύματος.

    Η συνάρτηση δηλώνεται ολική - δηλαδή, δεν πρέπει να κολλήσει ή να παγώσει, ο μεταγλωττιστής θα προσπαθήσει να το παρακολουθήσει. Το msgType καλείται τη στιγμή της μεταγλώττισης και η ολότητά του σημαίνει ότι η μεταγλώττιση δεν θα παγώσει λόγω του σφάλματος μας, αν και δεν μπορεί να εγγυηθεί ότι η εκτέλεση αυτής της συνάρτησης θα εξαντλήσει τους πόρους του συστήματος.
    Είναι επίσης εγγυημένο ότι δεν θα εκτελέσει το "rm -rf /" επειδή δεν υπάρχει IO στην υπογραφή του.

    Ας περιγράψουμε το updater:

    Σύνολο ενημέρωσης: (m:Model) -> (msgType m) -> Μοντέλο ενημέρωσης Logouted (Όνομα σύνδεσης) = Logined name updater (Logined name) Logout = Logouted updater (Logined name) Greet = Logined name
    Νομίζω ότι η λογική αυτής της λειτουργίας είναι ξεκάθαρη. Θα ήθελα να σημειώσω για άλλη μια φορά το σύνολο - σημαίνει ότι ο μεταγλωττιστής Idris θα ελέγξει ότι έχουμε εξετάσει όλες τις εναλλακτικές που επιτρέπονται από το σύστημα τύπων. Η Elm εκτελεί επίσης αυτόν τον έλεγχο, αλλά δεν μπορεί να γνωρίζει ότι δεν μπορούμε να αποσυνδεθούμε εάν δεν είμαστε ακόμη συνδεδεμένοι και θα απαιτήσει ρητή επεξεργασία της συνθήκης

    Updater Logouted Logout = ???
    Ο Idris θα βρει αναντιστοιχίες τύπου σε έναν περιττό έλεγχο.

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

    Σύνολο loginΣελίδα: IO MsgOuted loginPage = do putStr "Είσοδος: " χάρτης Σύνδεση getLine σύνολο genMsg: String -> MsgIned genMsg "" = Αποσύνδεση genMsg _ = Χαιρετισμός συνόλου workPage: String -> IO MsgIned workPage name = do putStr ("Γεια σας, ++ όνομα ++ "\n") putStr "Εισαγωγή κενή συμβολοσειρά για αποσύνδεση ή μη κενή για χαιρετισμό\n" χάρτης genMsg getLine Συνολική προβολή: (m: Μοντέλο) -> IO (msgType m) Προβολή Logouted = login Προβολή σελίδας (Συνδεδεμένο όνομα ) = Όνομα σελίδας εργασίας
    Η προβολή πρέπει να δημιουργήσει μια λειτουργία I/O που επιστρέφει μηνύματα, ο τύπος της οποίας εξαρτάται και πάλι από την τιμή του μοντέλου. Έχουμε δύο επιλογές: loginPage, που εκτυπώνει ένα μήνυμα "Login:", διαβάζει μια συμβολοσειρά από το πληκτρολόγιο και την τυλίγει σε ένα μήνυμα σύνδεσης και workPage με μια παράμετρο ονόματος χρήστη, η οποία εκτυπώνει έναν χαιρετισμό και επιστρέφει διαφορετικά μηνύματα (αλλά του ίδιου τύπου - MsgIned) ανάλογα με το αν ο χρήστης εισάγει μια κενή ή μη κενή συμβολοσειρά. Η προβολή επιστρέφει μία από αυτές τις λειτουργίες ανάλογα με την τιμή του μοντέλου και ο μεταγλωττιστής ελέγχει τον τύπο τους, παρόλο που είναι διαφορετικός.

    Τώρα μπορούμε να δημιουργήσουμε και να εκτελέσουμε την εφαρμογή μας

    App: Application Model Main.msgΤύπος IO app = MUV Κύρια προβολή προγράμματος ενημέρωσης: IO () main = εφαρμογή muvRun
    Εδώ πρέπει να σημειωθεί ένα λεπτό σημείο - η συνάρτηση muvRun επιστρέφει ΙΟ α, όπου το a δεν καθορίστηκε και η τιμή main είναι τύπου IO(), Οπου () είναι το όνομα ενός τύπου που συνήθως αποκαλείται Μονάδα, το οποίο έχει μια ενιαία τιμή, γραμμένη επίσης ως κενή πλειάδα () . Αλλά ο μεταγλωττιστής μπορεί να το χειριστεί εύκολα. αντικαθιστώντας το a().

    Τύποι Scala και μονοπατιών

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

    Σφραγισμένη αφηρημένη κλάση MsgLogouted κλάση υπόθεσης Σύνδεση(όνομα: Συμβολοσειρά) επεκτείνεται MsgLogouted σφραγισμένη κλάση αφηρημένης MsgLogined κλάση υπόθεσης Logout() επεκτείνει MsgLogined κατηγορία υπόθεσης Greet() επεκτείνει MsgLogined αφηρημένη κλάση Προβολή ( def run() : Msg ) κλάση σφραγισμένου τύπου def view() : View ) case class Logouted() extensions Model ( type Message = MsgLogined override def view() : View .... ) case class Logined(name: String) extends Model (type Message = MsgLogined override def view( ) : Προβολή .... )
    Οι αλγεβρικοί τύποι στη Scala μοντελοποιούνται μέσω κληρονομικότητας. Ο τύπος αντιστοιχεί σε κάποιους σφραγισμένη αφηρημένη τάξη, και κάθε κατασκευαστής κληρονόμησε από αυτό κατηγορία περίπτωσης. Θα προσπαθήσουμε να τις χρησιμοποιήσουμε ακριβώς ως αλγεβρικούς τύπους, περιγράφοντας όλες τις μεταβλητές ότι ανήκουν στον γονέα σφραγισμένη αφηρημένη τάξη.

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

    Τώρα ας εφαρμόσουμε το updater

    Ενημέρωση αντικειμένου ( def update(model: Model)(msg: model.Message) : Model = ( match model ( case Logouted() => msg match ( case Login(name) => Logined(name) ) case Logined(name) => αντιστοίχιση μηνυμάτων ( case Logout() => Logouted() case Greet() => μοντέλο ) ) )
    Εδώ χρησιμοποιούμε τύπους που εξαρτώνται από τη διαδρομή για να περιγράψουμε τον τύπο του δεύτερου ορίσματος από την τιμή του πρώτου. Για να αποδεχτεί το Scala τέτοιες εξαρτήσεις, οι συναρτήσεις πρέπει να περιγραφούν σε μορφή curried, δηλαδή ως συνάρτηση από το πρώτο όρισμα, το οποίο επιστρέφει μια συνάρτηση από το δεύτερο όρισμα. Δυστυχώς, το Scala δεν κάνει πολλούς ελέγχους τύπου σε αυτό το σημείο για το οποίο ο μεταγλωττιστής έχει αρκετές πληροφορίες.

    Τώρα ας δώσουμε μια πλήρη υλοποίηση του μοντέλου και της προβολής

    Η κλάση περίπτωσης Logouted() επεκτείνει το μοντέλο ( τύπος Message = MsgLogouted override def view() : View = new View ( override def run() = ( println("Enter name ") val name = scala.io.StdIn.readLine() Είσοδος (όνομα) ) ) case class Logined(name: String) extensions Model ( type Message = MsgLogined override def view() : View = new View ( override def run() = ( println(s"Hello, $name") println ( "Κενή συμβολοσειρά για αποσύνδεση, nonempy για χαιρετισμό.") scala.io.StdIn.readLine() match ( case "" => Logout() case _ => Greet() ) ) ) abstract class View ( def run( ) : Μήνυμα ) Προβολή αντικειμένου ( προβολή def(μοντέλο: Μοντέλο): Προβολή = ( model.view() ) )
    Ο τύπος επιστροφής μιας συνάρτησης προβολής εξαρτάται από την παρουσία του ορίσματός της. Αλλά για την υλοποίηση στρέφεται στο μοντέλο.

    Η εφαρμογή που δημιουργήθηκε με αυτόν τον τρόπο εκκινείται ως εξής:

    Κύριο αντικείμενο ( εισαγωγή scala.annotation.tailrec @tailrec def process(m: Model) ( val msg = Viewer.view(m).run() process(Updater.update(m)(msg)) ) def main(args: Πίνακας) = ( διεργασία (Logouted()) ) )
    Επομένως, ο κώδικας συστήματος χρόνου εκτέλεσης δεν γνωρίζει τίποτα για την εσωτερική δομή των μοντέλων και των τύπων μηνυμάτων, αλλά ο μεταγλωττιστής μπορεί να ελέγξει ότι το μήνυμα ταιριάζει με το τρέχον μοντέλο.

    Εδώ δεν χρειαζόμασταν όλες τις δυνατότητες που παρέχουν οι τύποι που εξαρτώνται από τη διαδρομή. Θα εμφανιστούν ενδιαφέρουσες ιδιότητες εάν εργαζόμαστε παράλληλα με πολλές περιπτώσεις συστημάτων Model-Updater-View, για παράδειγμα, κατά την προσομοίωση ενός κόσμου πολλών πρακτόρων (η προβολή θα αντιπροσωπεύει τότε την επιρροή του πράκτορα στον κόσμο και τη λήψη σχολίων). Σε αυτήν την περίπτωση, ο μεταγλωττιστής έλεγξε ότι το μήνυμα επεξεργάστηκε ακριβώς τον πράκτορα για τον οποίο προοριζόταν, παρά το γεγονός ότι όλοι οι πράκτορες έχουν τον ίδιο τύπο.

    C++

    Η C++ εξακολουθεί να είναι ευαίσθητη στη σειρά των ορισμών, ακόμα κι αν έχουν γίνει όλοι στο ίδιο αρχείο. Αυτό δημιουργεί κάποια ταλαιπωρία. Θα παρουσιάσω τον κώδικα με μια σειρά κατάλληλη για την επίδειξη ιδεών. Μια μεταγλωττιζόμενη έκδοση μπορεί να βρεθεί στο GitHub.

    Οι αλγεβρικοί τύποι μπορούν να υλοποιηθούν με τον ίδιο τρόπο όπως στη Scala - μια αφηρημένη κλάση αντιστοιχεί σε έναν τύπο και οι συγκεκριμένοι απόγονοι αντιστοιχούν σε κατασκευαστές (ας τους ονομάσουμε "κατηγορίες κατασκευαστών", για να μην συγχέονται με τους συνηθισμένους κατασκευαστές C++) του αλγεβρικού τύπος.

    Η C++ υποστηρίζει τύπους που εξαρτώνται από τη διαδρομή, αλλά ο μεταγλωττιστής δεν μπορεί να χρησιμοποιήσει τον τύπο αφηρημένο χωρίς να γνωρίζει τον πραγματικό τύπο με τον οποίο σχετίζεται. Επομένως, είναι αδύνατο να υλοποιηθεί το Model-Updater-View με τη βοήθειά τους.

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

    Struct Processor ( virtual const Processor *next() const = 0; ); πρότυπο struct ProcessorImpl: δημόσιος επεξεργαστής (const CurModel * μοντέλο, ProcessorImpl (const CurModel* m) : model(m) ( ); const Επεξεργαστής *next() const ( Const Προβολή * view = model->view(); const typename CurModel::Message * msg = view->run(); Διαγραφή προβολής. const Μοντέλο * newModel = msg->process(model); διαγραφή μηνυμάτων. return newModel->processor(); ) );
    Περιγράφουμε ένα αφηρημένο σύστημα εκτέλεσης, με μία μόνο μέθοδο για να κάνουμε ό,τι απαιτείται και να επιστρέφουμε ένα νέο σύστημα εκτέλεσης κατάλληλο για την επόμενη επανάληψη. Η συγκεκριμένη έκδοση έχει μια παράμετρο template και θα είναι εξειδικευμένη για κάθε «κατηγορία κατασκευαστή» του μοντέλου. Είναι σημαντικό εδώ ότι όλες οι ιδιότητες του τύπου CurModel θα ελέγχονται κατά την εξειδίκευση του προτύπου με μια συγκεκριμένη παράμετρο τύπου και κατά τη στιγμή της σύνταξης του ίδιου του προτύπου, δεν απαιτείται να περιγραφούν (αν και είναι δυνατή η χρήση έννοιες ή άλλους τρόπους υλοποίησης κλάσεων τύπου). Το Scala διαθέτει επίσης ένα αρκετά ισχυρό σύστημα παραμετροποιημένων τύπων, αλλά ελέγχει τις ιδιότητες των τύπων παραμέτρων κατά τη σύνταξη του παραμετροποιημένου τύπου. Εκεί, η εφαρμογή ενός τέτοιου μοτίβου είναι δύσκολη, αλλά δυνατή, χάρη στην υποστήριξη κλάσεων τύπου.

    Ας περιγράψουμε το μοντέλο.

    Struct Model ( virtual ~Model() (); virtual const Επεξεργαστής *processor() const = 0; ); struct Logined: public Model ( struct Μήνυμα ( const virtual Model * process(const Logined * m) const = 0; virtual ~Message() (); struct Logout: public Message ( const Model * process(const Logined * m) const; struct Greet: public Message ( const Model * process(const Logined * m) const std::string name; (...); Const Προβολή * view() const ( return new LoginedView(name); ); const Επεξεργαστής *processor() const ( επιστροφή νέου ProcessorImpl (Αυτό); (...); Const Προβολή ) ) struct Logouted: public Model ( struct Μήνυμα ( const virtual Model * process(const Logouted * m) const = 0; virtual ~Message() (); struct Login: δημόσιο Μήνυμα ( const std::όνομα συμβολοσειράς; Login(std :: string lname) : name(lname) ( ) const Model * process(const Logouted * m) const ; * view() const ( return new LogoutedView(); ); const Επεξεργαστής *processor() const ( επιστρέφει νέο ProcessorImpl
    (Αυτό);

    ) )

    Const Model * Logouted::Login::process(const Logouted * m) const ( διαγραφή m; επιστροφή νέου Logined(name); ); const Model * Logined::Logout::process(const Logined * m) const ( διαγραφή m; επιστροφή νέου Logouted(); ); const Model * Logined::Greet::process(const Logined * m) const ( return m; );
    Τώρα ας συγκεντρώσουμε όλα όσα σχετίζονται με την προβολή, συμπεριλαμβανομένων των εσωτερικών οντοτήτων των μοντέλων

    Πρότυπο struct Προβολή ( virtual const Μήνυμα * run() const = 0; virtual ~View () (); ) struct Logined: public Model ( struct LoginedView: public View ( const std:: string name; LoginedView(std::string lname) : name(lname) (); virtual const Μήνυμα * run() const ( char buf; printf("Hello %s", name.c_str()) fgets(buf, 15, stdin return (*buf == 0 || *buf == "\n" || *buf == "\r") ? (new Logout()) : static_cast (νέος Χαιρετισμός) ) ) Const Προβολή * view() const ( return new LoginedView(name); ); ) struct Logouted: public Model ( struct LogoutedView: public View ( virtual const Μήνυμα * run() const ( char buf; printf("Login: "); fgets(buf, 15, stdin); return new Login(buf); ); ); Const Προβολή * view() const ( return new LogoutedView(); ); )
    Και τέλος, ας γράψουμε κυρίως

    Int main(int argc, char ** argv) ( const Processor * p = new ProcessorImpl (νέο Logouted()); while(true) ( const Processor * pnew = p->next(); delete p; p = pnew; ) return 0; )

    Και πάλι Scala, αυτή τη φορά με τάξεις τύπου

    Στη δομή, αυτή η υλοποίηση αντιγράφει σχεδόν πλήρως την έκδοση C++.

    Παρόμοιο μέρος του κώδικα

    abstract class View ( def run(): Message ) abstract class Processor ( def next(): Processor; ) sealed abstract class Model ( def processor(): Processor ) sealed abstract class LoginedMessage class case Logout() επεκτείνει LoginedMessage κλάση περίπτωσης Greet( ) επεκτείνει την κλάση περίπτωσης LoginedMessage Logined(val name: String) επεκτείνει Μοντέλο ( override def processor(): Processor = new ProcessorImpl(this) ) σφραγισμένη αφηρημένη κλάση LogoutedMessage κλάση περίπτωσης Login(όνομα: String) επεκτείνει LogoutedMessage κλάση περίπτωσης Logouted() επέκταση ( παράκαμψη def processor(): Processor = new ProcessorImpl(this) ) αντικείμενο Κύριο ( import scala.annotation.tailrec @tailrec def process(p: Processor) ( process(p.next()) ) def main(args: Array) = ( process(new ProcessorImpl(Logouted())) ) )


    Αλλά στην υλοποίηση του περιβάλλοντος χρόνου εκτέλεσης, προκύπτουν λεπτές αποχρώσεις.

    Κλάση ProcessorImpl(μοντέλο: M)(σιωπηρή ενημέρωση: (M, Μήνυμα) => Μοντέλο, προβολή: M => Προβολή) επεκτείνει τον επεξεργαστή ( def next(): Επεξεργαστής = ( val v = προβολή(μοντέλο) val msg = v. run() val newModel = updater(model,msg) newModel.processor() ) )
    Εδώ βλέπουμε νέες μυστηριώδεις παραμέτρους (σιωπηρή ενημέρωση: (M, Μήνυμα) => Μοντέλο, προβολή: M => Προβολή). Η σιωπηρή λέξη-κλειδί σημαίνει ότι όταν καλείται αυτή η συνάρτηση (ακριβέστερα, ο κατασκευαστής κλάσης), ο μεταγλωττιστής θα αναζητήσει αντικείμενα κατάλληλων τύπων που επισημαίνονται ως σιωπηρά στο περιβάλλον και θα τα μεταβιβάσει ως κατάλληλες παραμέτρους. Αυτή είναι μια αρκετά περίπλοκη έννοια, μια από τις εφαρμογές της οποίας είναι η υλοποίηση κλάσεων τύπου. Εδώ υπόσχονται στον μεταγλωττιστή ότι για συγκεκριμένες υλοποιήσεις του μοντέλου και του μηνύματος, όλες οι απαραίτητες λειτουργίες θα παρέχονται από εμάς. Τώρα ας εκπληρώσουμε αυτή την υπόσχεση.

    Ενημερωτές αντικειμένων ( σιωπηρό def logoutedUpdater(μοντέλο: Logouted, msg: LogoutedMessage): Μοντέλο = ( (μοντέλο, μηνύματα) αντιστοίχιση (περίπτωση (Logouted(), Login(όνομα)) => Logined(όνομα) ) σιωπηρή def viewLogouted(model : Logouted) = new View ( override def run() : LogoutedMessage = ( println("Enter name") val name = scala.io.StdIn.readLine() Login(name) ) σιωπηρή def loginedUpdater(model: Logined, msg : LoginedMessage): Μοντέλο = ( (μοντέλο, msg) αντιστοίχιση ( case (Logined(name), Logout()) => Logouted() case (Logined(name), Greet()) => model ) ) implicit def viewLogined( model: Logined) = new View ( val name = model.name override def run() : LoginedMessage = ( println(s"Hello, $name") println("Κενή συμβολοσειρά για αποσύνδεση, nonempy για χαιρετισμό.") scala.io Ταίριασμα .StdIn.readLine() ( case "" => Logout() case _ => Greet() ) ) ) εισαγωγή ενημερώσεων._

    Haskell

    Δεν υπάρχουν εξαρτημένοι τύποι στο mainstream Haskell. Δεν έχει επίσης κληρονομικότητα, την οποία χρησιμοποιήσαμε σημαντικά κατά την υλοποίηση του μοτίβου σε Scala και C++. Αλλά η κληρονομικότητα ενός επιπέδου (με στοιχεία εξαρτημένων τύπων) μπορεί να μοντελοποιηθεί χρησιμοποιώντας περισσότερο ή λιγότερο τυπικές επεκτάσεις γλώσσας - TypeFamilies και ExistentialQuantification. Για την κοινή διεπαφή των θυγατρικών κλάσεων OOP, δημιουργείται μια κλάση τύπου, στην οποία υπάρχει ένας εξαρτημένος τύπος "οικογένειας", οι ίδιες οι θυγατρικές κλάσεις αντιπροσωπεύονται από έναν ξεχωριστό τύπο και στη συνέχεια τυλίγονται σε έναν "υπαρξιακό" τύπο με έναν μοναδικό κατασκευαστή. .

    Μοντέλο δεδομένων = για όλα τα m. (Με δυνατότητα ενημέρωσης m, με δυνατότητα προβολής m) => Κατηγορία μοντέλου m με δυνατότητα ενημέρωσης m όπου δεδομένα Μήνυμα m:: * ενημέρωση:: m -> (Μήνυμα m) -> Κατηγορία μοντέλου (Με δυνατότητα ενημέρωσης m) => Με δυνατότητα προβολής m όπου προβολή:: m -> (Προβολή (Μήνυμα m)) δεδομένα Logouted = Logouted data Logined = Logined String
    Προσπάθησα να διαχωρίσω την ενημέρωση και την προβολή όσο το δυνατόν περισσότερο, έτσι δημιούργησα δύο διαφορετικές κατηγορίες τύπων, αλλά μέχρι στιγμής δεν έχει λειτουργήσει καλά.

    Η υλοποίηση του updater είναι απλή

    Παρουσίαση με δυνατότητα ενημέρωσης Αποσύνδεση όπου τα δεδομένα έχουν αποσυνδεθεί Μήνυμα = Ενημέρωση συμβολοσειράς σύνδεσης Αποσύνδεση (Όνομα σύνδεσης) = Μοντέλο (Συνδεδεμένο όνομα) Παρουσίαση με δυνατότητα ενημέρωσης Συνδεδεμένο όπου τα δεδομένα Μήνυμα έχουν συνδεθεί = Αποσύνδεση | Ενημέρωση χαιρετισμού m Αποσύνδεση = Μοντέλο Αποσυνδεθείσα ενημέρωση m Χαιρετισμός = Μοντέλο m
    Έπρεπε να διορθώσω το IO ως View. Οι προσπάθειες να γίνει πιο αφηρημένο περιέπλεξαν πολύ τα πάντα και αύξησαν τη σύζευξη του κώδικα - ο τύπος μοντέλου πρέπει να γνωρίζει ποια Προβολή θα χρησιμοποιήσουμε.

    Τύπος Εισαγωγής System.IO Προβολή a = IO μια παρουσία Δυνατότητα προβολής Αποσύνδεση όπου προβολή Αποσύνδεση = do putStr "Είσοδος: " hFlush stdout fmap Σύνδεση στιγμιότυπο getLine Προβολή Είσοδος όπου προβολή (Συνδεδεμένο όνομα) = do putStr $ "Γεια " ++ όνομα ++ " !\n" hFlush stdout l<- getLine pure $ if l == "" then Logout else Greeting
    Λοιπόν, το εκτελέσιμο περιβάλλον διαφέρει ελάχιστα από το παρόμοιο στο Idris

    RunMUV::Model -> IO a runMUV (Μοντέλο m) = κάντε μήνυμα<- view m runMUV $ update m msg main:: IO () main = runMUV (Model Logouted)

  • mob_info