Sawfish
Register
Advertisement

This page tries to estimate the amount of work required to port Sawfish from rep/rep-gtk to Racket. There is nothing official about it, and it can also be useful for anyone interested in porting Sawfish to another language, like Chicken.

(So this page is not to discuss the benefits of Racket for Sawfish, but only to consider what should be done to port it.)

Note: A project has been started here. It is not a direct port of Sawfish, but a rewrite from scratch. Its aim is to be very similar to Sawfish (extensibility, scripting in Scheme, console, etc.), but there may be differences.

Links:

Discussions:

Core differences[]

In variable binding, namespaces, module loading, etc.

  • Namespaces
    • Rep:
    • Racket:
  • Modules and features
    • Rep: loading
    • Racket:
    • Racket's module should be sufficient though Racket also provides other loading means (like Units and 'load')
  • Exceptions and continuations
    • Rep:
    • Racket:
    • Rep's catch/throw, exceptions and continuations should be easy to implement in Racket
  • Threads (shouldn't be too problematic)
  • defvar
    • Rep: dynamic scoping, used like normal variables
    • Racket: parameters, used like fluid-variables (?) with a bit less "dynamic scope" (for safety reasons). Like Rep, Racket can define global variables with 'define', which are used (almost) like if defined with a global surrounding 'let'.
  • Void
    • Rep: unbounded variable
    • Racket: unbounded = #<undefined>, 'void' is a function that consumes all its arguments and returns #<void>, which serves as the output of functions that return no value
  • lists
    • Rep: mutable cons cells
    • Racket: immutable cons cells by default (mutable cells exist too)
  • booleans
    • Rep: 'nil' or '() is false, any other value is like true, 't'.
    • Racket: false is 'false' or #f, any other value (including '()) is like true ('true', #t). 'nil' does not exist, and is 'null', which is '().
  • if
    • Rep: (if test expr-true expr-false ...)
    • Racket: (if test expr-true expr-false)
  • let
  • Macros
    • Rep: lisp-like, using defmacro
    • Racket: hygienic macros (define-syntax)
    • Eli Barzilay says it should be fairly easy to port them and may help with it
    • Racket also has a legacy defmacro wich may be useful in a first step, but hygienic macros are much safer and less error-prone, especially for 3rd-party scripts. Most simple macros are very easy to write in the hygienic system too. For example, the 'when' macro could be (re)defined by:
(define-syntax-rule (when test body ...)
  (cond (test body ...)))
  • Doc-strings
    • Rep: in define, defun, lambda, defvar, etc.
    • Racket: does not have doc-strings by default, but it should be easy to add support for them when defining the Racket/Sawfish interface
  • Hooks
    • Rep:
    • Racket: Nothing that I know of, but should be easy to implement

...

Library differences[]

Xlib[]

Mainly bindings to Xlib through the FFI (?), or maybe better to XCB (though that would probably require additional work).

There is already a Xlib bindings package for Racket.

...

GTK[]

Racket already uses GTK for its GUI.


...

Simple rewritings and conventions[]

  • Predicates
    • Rep: predicates end in '-p' or 'p'.
    • Racket: predicates end in '?'
  • define
    • Rep: define, defun with doc-string
    • Racket: define (without doc-strings, but should not be difficult to add)
  • Fluid variables
    • Rep: make-fluid, let-fluid
    • Racket: make-parameter, parameterize
  • Arrays
    • Rep: []
    • Racket: vector ([] is like () in Racket)
  • setq --> set! ('!' means mutation)
    • set --> nothing yet, would require a macro if needed (I think)
  • prog1, progn --> begin0, begin
  • Keywords arguments
    • Rep: ':the-keyword'
    • Racket: '#:the-keyword'
  • Special arguments
    • (lambda (#!optional a (b 5)) ...) --> (lambda ((a #f) (b 5)) ...)
    • !rest --> .
    • !key: ((lambda (a #!key b c) (list a b c)) 1 #:b 2 3) --> ((lambda (a #:b b #:c c) (list a b c)) 1 #:b 2 3)
    • In Racket, keyword arguments cannot be used as non-key arguments
  • 1+, 1- --> add1, sub1
  • mapcar --> map
  • mapc --> for-each (but returns #<void> instead of the last value)
  • ...


...

How to port Sawfish to Racket[]

It may be simpler in a first step to write a compatibility interface in Racket to make sure that Sawfish runs over Racket, and in a second step slowly change Sawfish code to Racket's philosophy. This would also ensure that nothing/little breaks in 3rd party scripts.

Note: It would probably be safer to initially make no binding visible to Sawfish-racket, so that every rep binding has to be defined explicitly or raise an error if not defined (i.e., don't fall in the simplicity solution to not redefine the binding that seem to have the same meaning in rep and Racket).

Other possibilities: Do not try to make a Rep substitute but use the opportunity to adapt to Racket's way? For some issues it may require less work, but maybe not for most.

It would be useful to know what part of Rep does not need to be ported to Racket (e.g., are remote files needed?)

Foreign Function Interface[]

Racket provides a good foreign function interface (FFI) which should make it easy to bind the current sawfish/rep C code, although the project should become entirely independent of rep.

Advertisement