Erfahrungen mit Racket

Ich dachte bislang, da sei ein Leerzeichen nötig.
Leerzeichen dienen ja als Trenner zwischen Werten/Ausdrücken. Offenbar braucht man keinen expliziten Trenner wenn eindeutig ist, dass dann der nächste Ausdruck anfängt.
So ein bisschen vergleichbar mit Turbo Pascal, wo man ja beim nem end worauf ein end folgt das Semikolon weglassen kann.
 
Weiterhin Spaß mit Racket-GUIs:
Ich würde gern mathematische Operatoren abfangen, also die "Return"-Methode erweitern.

Code:
(new (class text-field% (super-new)
         (define/override (on-subwindow-char receiver event)
           (case (send event get-key-code)
             ((#\return)
              (do-something)) ;; funktioniert
             (('add 'separator 'subtract 'divide)
              (do-something-else)) ;; hier geht er nie rein...?!
             (else
              ;; Hier dann schon wieder. Glaube ich (ich sehe die Auswirkungen ja nicht).
              (super on-subwindow-char receiver event)))
           #f))
  ;; ...

Habe ich die Syntax versiebt? :confused:
Nachtrag: oder funktioniert das nur im "nummerischen" Block? Wenn ja: wie fange ich ein "normales Plus" ab?
 
Ich würde gern mathematische Operatoren abfangen, also die "Return"-Methode erweitern.
Es macht sich bei solchen Sachen immer gut, mit displayln ein paar Debug-Ausgaben zu machen. Dann sieht man nämlich, was da wirklich ankommt.
Also z.B. nach
(define/override (on-subwindow-char receiver event)
ein
(displayln (~a "Key-Code: " (send event get-key-code)))
einfügen oder besser noch:
(display "Key-Code: ") (println (send event get-key-code))

Weil println die Werte vorher nicht in Strings umwandelt, wie display und displayln.

Dann ergibt sich auch schnell, wie Dein Quelltext aussehen muss:
Code:
(new (class text-field% (super-new)
         (define/override (on-subwindow-char receiver event)
           ; DEBUG-Code
           (display "Key-Code: ")
           (println (send event get-key-code))
           ; DEBUG-Code Ende
           (case (send event get-key-code)
             ((#\return)
              (do-something)) ;; funktioniert
             ((#\+ #\-)
              (do-something-else)) ;; hier geht er nie rein...?!
             (else
              ;; Hier dann schon wieder. Glaube ich (ich sehe die Auswirkungen ja nicht).
              (super on-subwindow-char receiver event)))
           #f))
  ;; ...
 
Ah, verdammt. Danke. Ich merke schon: einfach herleiten ohne nachgucken ist unter Racket wenig Erfolg versprechend... :)
 
Oh, ich glaube der Bengel hat Ahnung ..... :D

Nee. Ich tu nur so. Alles Fassade. :-)

Generell ist es aber ne oft gute Möglichkeit quasi mit Ausgaben auf stdout zu debuggen. Das mache ich auch gern in anderen Sprachen, weils halt unkompliziert ist.

Übrigens. Auch solche Sachen kann man wieder schön in ein Makro verpacken:

Code:
(define-syntax-rule (debug msg expression)
  (when DEBUG
    (begin
      (display (format "~a: " msg))
      (println expression)
      )))

Dann kann man nämlich einfach schreiben:

(debug "Key-Code" (send event get-key-code))

Und in Abhängigkeit davon, ob die Variable DEBUG #t oder #f ist die Debug-Ausgabe aktiv oder nicht.
Wenn man also Debug-Ausgaben haben will macht man am Anfang des Programms irgendwo ein:
(define DEBUG #t)

Jetzt können wir das Makro noch etwas verfeinern. Wir möchten z.B. auch darauf reagieren, wenn die Variable DEBUG gar nicht existiert.
Das können wir prüfen mit (identifier-binding #'DEBUG)

Tricky wird es jetzt, wenn wir auch noch angeben wollen, welcher Ausdruck überhaupt ausgewertet wird. Oder auch in welche Zeilennummer die debug-Ausgabe steht.
Fürs erste können wir syntax->datum nehmen. Dann ergibt sich
Code:
(define-syntax-rule (debug msg expression)
  (when (and (identifier-binding #'DEBUG) DEBUG)
        (begin
          (display (format "~a - ~a:"
                           msg
                           (syntax->datum #'expression)))
          (println expression))))

Wenn wir das dann in CrimsonKings einbauen sieht die Debug-Ausgabe durch:
(debug "Key-Code" (send event get-key-code))
z.B. so aus:
Key-Code - (send event get-key-code): #\f

Man kann auch andere Scherze machen. Das debug beispielsweise direkt den Ausdruck zurück gibt, den es anzeigt.
Dann braucht man keine extra Zeile mehr dafür:
Code:
(define DEBUG #t)

(define-syntax-rule (debug msg expression)
  (begin
    (when (and (identifier-binding #'DEBUG) DEBUG)
               (begin
                 (display (format "~a - ~a: "
                                  msg
                                  (syntax->datum #'expression)))
                 (println expression)))
    expression))

Und das kann man dann direkt einbauen und es funktioniert immer, egal ob DEBUG #t ist oder #f. Aber nur bei #t gibts eben zusätzlich Debug-Ausgaben:
Code:
  (new (class text-field% (super-new)
         (define/override (on-subwindow-char receiver event)
           (case (debug "Key-Code" (send event get-key-code))
             ((#\return)
              (displayln "ENTER"))
             ((#\+ #\-)
              (displayln "+"))
             (else
              (displayln "ELSE-Zweig")
              (super on-subwindow-char receiver event)))
           #f))

Soviel erst mal für jetzt.
 
Ab und an braucht man manchmal eine Endlosschleife.
Normalerweise nimmt man dafür ein Konstrukt wie
C-ähnlich:
while (true)
{
   ...
}

In Racket gibt es allerdings ein Schleifentyp wie "while" nicht. Wie viele funktionale Sprachen setzt Racket sehr auf Rekursion. Und so kann man das auch hier handhaben. Zum Beispiel mit Hilfe eines Named-Let
http://docs.racket-lang.org/guide/let.html#(part._.Named_let)

Code:
(let my-endless-loop ()
  (displayln "Hello ")
  (displayln "  World!")
  (my-endless-loop))

Nun sieht das nicht wirklich schick aus. Deshalb formulieren wir ein Makro für Endlosschleifen:
Code:
(define-syntax-rule (infinite-loop body ...)
  (let my-endless-loop ()
    body
    ...
    (my-endless-loop)))

Nun können wir unser Eingangsbeispiel so schreiben:
Code:
(infinite-loop
 (displayln "Hello")
 (displayln "  World!"))

Klar, sauber, unmissverständlich.
 
In Racket kann man natürlich auch auf Signale reagieren. Zum Beispiel möchte man ja so eine Entdlosschleife der Art , wie im letzten Beitrag beschrieben, abbrechen können.
Damit sollen vielleicht noch die ein oder anderen "Aufräumarbeiten" gemacht werden.
Es ist also gut, wenn man solche Sachen abfangen kann.

In Racket sind solche Signale als Exceptions realisiert. Und die kann man abfangen.

Code:
(with-handlers ([exn:break? (lambda (x) (displayln "\nTime to say goodbye!" x))])
  (infinite-loop
   (displayln "Hello")
   (displayln "  World!"))
  )

Wenn wir beim Ablauf des Programms jetzt Strg-C drücken, kriegen wir zum Abschluss noch "Time to say goodbye!" ausgegeben.
 
Ich hatte hier noch ein Windows 8.1 rumliegen, habe es installiert und erfolgreich auf Windows 10 upgegradet, um mal den von @CrimsonKing beschriebenen Fehler mit dem Leerraum nachzuvollziehen. Und jetzt mal eine Frage an Windows Nutzer:

Wenn ein GUI Script gestartet wird, dann taucht im Hintergrund die cmd.exe auf. Das finde ich extrem hinderlich. Kann man das abstellen?
 
Wenn du unter OpenBSD ein Script startest, dann läuft das doch auch als Unterprozess der ksh...

Aber ja, das geht natürlich.
https://superuser.com/a/62646

Oder du kompilierst es zu einem Programm, dann taucht (jedenfalls bei mir) auch kein Interpreter auf. (Wofür auch?)
 
Geht bei mir. Allerdings könnte es durchaus sein, dass Racket sich nicht in deinen %PATH% einträgt, dann musst du das nachholen. :)
(Oder jedes Mal "C:\Racket-Ordner\..." davorschreiben.)
 
So, ich habe den Path gesetzt und er findet raco auch in der Konsole cmd.exe. Wenn ich jetzt

Code:
raco exe dm.rkt

ausführe oder

Code:
raco exe --gui dm.rkt

ändert sich nichts, wenn ich die compilierte ausführbare Datei im Explorer anklicke, startet imHintergrund die cmd.exe. Was mache ich noch falsch oder wie machst Du @CrimsonKing es richtig. weil es nach Deiner Aussage ja bei Dir funktioniert?
 
Hat sich erledigt, Ausflug zu Windows ist vorbei. Hab ja seit vielen Jahren kein Windows mehr angepackt, wollte mir einfach mal einen Überblick verschaffen, wo Windows heute steht.
 
Bei mir startet eine handelsübliche .exe, erzeugt mit besagtem Befehl (und natürlich "--gui"), ohne jedes cmd-Fenster... entschuldige.
 
Bei mir startet eine handelsübliche .exe, erzeugt mit besagtem Befehl (und natürlich "--gui"), ohne jedes cmd-Fenster... entschuldige.
Das zweifel ich in keinster Weise an, aber seltsam ist es schon, oder? Ich habe die aktuelle Racket Version 7.x für Windows runtergeladen und benutzte Windows 10 Home, was ich vorher von Windows 8.1 geupgradet habe. Den Path hinzuzufügen war ja auch ganz einfach und hat auch funktioniert. Um was in Teufels Namen ist dann bei mir anders? Hast Du cmd.exe noch extra konfiguriert? Bei mir poppt auch bei einem compilierten Script mit --gui tatsächlich jedesmal die Konsole auf und das finde ich ganz einfach blöd und nicht praktikabel. Trotzdem danke für die Rückmeldung. Ich meine mich zu erinnern, das @rubricanis das auch mal beanstandet hatte mit der cmd.exe.
 
Ich habe bei mir die %COMSPEC% sogar umkonfiguriert. Bei mir ist das ConEmu, nicht cmd. Das ist enorm praktisch für manche Konsolenanwendungen und enorm nervig für Programme, die nicht damit rechnen. emacsclient zum Beispiel macht jedes Mal ein Fenster auf (vielleicht sollte ich mal gucken, ob ich da einen Patch einreichen kann, für mein ymarks habe ich das ja schon mal gemacht). Eigentlich sollte ich also noch wahrscheinlicher als du ein Extrafenster kriegen. Krieg ich aber nicht. :confused:
 
NB: Wenn man unter windows öfters env variable ändern muss (was schon mal notwendig ist) ist der EvEditor eine gute Sache. Das über die Systemeinstellungen zu machen ist doch reichlich umständlich.

Yep, in mancher Hinsicht ist win schon reichlich zickig, es sei den man macht alles genau so wie vorgesehen. Zu dem obigen Problem git es sicherlich eine Lösung, man muss sie nur kennen... ;-) Ich würde bei den Racket-Leuten mal nachfragen, die wissen das vermutlich.
 
Zuletzt bearbeitet:
Danke für den Hinweis auf den EvEditor. Bisher nutzte ich dafür RapidEE, das zwar vieles kann, aber anscheinend unpraktischer zu bedienen ist. Verdammt...

Aber ich lass das jetzt so. :)
 
Racket 7.2 steht vor der Tür (die nächsten Tage dürfte es erscheinen). Ein ganz wichtiges Projekt der letzten anderthalb Jahre war die Umstellung des Backends auf Chez-Scheme.
Chez ist ein R6RS kompatibler Scheme-Interpreter, der als die Schnellste Scheme-Implementation gilt.
Das war natürlich mit ein Grund für die Umstellung. Warum soll man alles selber machen, wenn es andere Projekte gibt, von denen man profitieren kann. Das ist ja einer der großen Vorteile von Open-Source.

Ein anderer (mindestens genauso wichtiger Aspekt; wenn nicht noch Wichtiger) ist die Möglichkeit, den relativ großen Anteil von C-Code in Racket zu verringern (insbesondere im Macro-Expander). Das hätte man zwar auch so gekonnt, aber wenn man mal schon ne große Umstellung macht, dann kann man das gleich mitmachen.
Ziel davon ist es den Anteil an C-Code zu verkleinern und entsprechend mit Racket-Code zu ersetzen.
Das erleichtert die Wartung, die Erweiterung und die Portierung auf die diversen Plattformen.

Mit Racket 7.2 wird der Großteil der Racket-on-Chez-Implementation abgeschlossen sein. Dennoch fehlt hier und da noch etwas. Und der Code bietet auch noch viel Spielraum für Optimierungen. Daher wird das neue Backend noch nicht per default eingesetzt.
Allerdings kann man jetzt schon mal mehr als nur ein Blick drauf werfen.

Ein vollständigen Statusbericht inkl. einiger Benchmarks findet man im Racket-Blog:
http://blog.racket-lang.org/2019/01/racket-on-chez-status.html
 
Zurück
Oben