Den tor. 21. jan. 2021 kl. 05.06 skrev Stuart Hungerford <
[email protected]>:

> My project is really aimed at supporting self-directed learning of
> concepts from abstract algebra.
>
I was taught many years ago that to really understand something to try
> implementing it in a high level language.
>
That will soon expose any hidden assumptions or misunderstandings.
>
> An early attempt (in Rust) is at:
> https://gitlab.com/ornamentist/un-algebra
>
> By using the Rust trait system (and later Haskell typeclasses) I could
> create structure traits/typeclasses that don't clash with the builtin
> numeric types or with the larger more production oriented libraries in
> those languages in the same general area of math.
>
> Once I added generative testing of the structure axioms I could experiment
> with, e.g. finite fields and ensure all the relevant axioms and laws were
> (at least probabilistically) met.
>

Not knowing Rust nor traits, I have amused myself writing a very simple
version of traits.


#lang racket
(require (for-syntax syntax/parse racket/syntax))

;;;
;;; TRAITS
;;;

; This file contains a minimal implementation of traits.
; Overview:

;    (define-trait trait (method ...)
;        A trait is defined as list of methods names.

;    (implementation trait structure body ...)
;        An implementation of a trait for a given structure types,
;        associates functions to each of the methods suitable
;        for that structure type.
;        Within body, the method names can be used.

;    (with ([id trait structure expression] ...) . body)
;        Similar to (let ([id expression] ...) . body),
;        but within the body, one can use id.method
;        to call a method.

; Expansion time helpers
(begin-for-syntax
  ; These functions produce new identifiers. The context is taken from stx.
  (define (identifier:structure-method stx s m)
    (format-id stx "~a-~a" s m))
  (define (identifier:id.method stx id m)
    (format-id #'stx "~a.~a" id m))

    ; get-methods : identifier -> list-of-identifiers
  (define (get-methods trait)
    (syntax-local-value (format-id trait "~a-methods" trait))))


; SYNTAX  (define-trait Name (name ...))
;   This declares a new trait with the name Name having the methods name
....

(define-syntax (define-trait stx)
  (syntax-parse stx
    [(_define-trait Name (id ...))
       (with-syntax ([trait-methods (format-id #'Name "~a-methods" #'Name)])
         (syntax/loc stx
           (define-syntax trait-methods (list #'id ...))))]

    [(_define-trait Name (super-trait ...) (id ...))
     (displayln (list 'dt stx))
     (define ids    (syntax->list #'(id ...)))
     (define supers (syntax->list #'(super-trait ...)))
     (with-syntax ([trait-methods            (format-id #'Name "~a-methods"
#'Name)]
                   [((super-method ...) ...) (map get-methods supers)])
       (syntax/loc stx
         (define-syntax trait-methods
           (list #'id ...
                 #'super-method ... ...))))]))



; SYNTAX  (implementation trait structure . body)
;   Defines structure-method for each method of the trait.
;   The structure-method  is bound to  method.
;   If method is defined in body, then that binding is used.
;   If method is not bound in body, but bound outside, the outside binding
is used.
;   If method is not bound at all, an error is signaled.
(define-syntax (implementation stx)
  (syntax-parse stx
    [(_implementation trait structure body ...)
     (define methods (get-methods #'trait))
     (with-syntax*
       ([(method ...)                        ; These short names are used
by the user
         (for/list ([method methods])
           (syntax-local-introduce
            (format-id #'stx "~a" method)))]

        [(structure-method ...)               ; Used in the output of the
`with` form.
         (for/list ([method methods])
           (identifier:structure-method #'trait #'structure method))])

       (syntax/loc stx
         (define-values (structure-method ...)
           (let ()
             body ...
             (values method ...)))))]))

(define-syntax (with stx)
  (syntax-parse stx
    [(_with ([id trait structure expression] ...) . body)
     (define traits         (syntax->list #'(trait ...)))
     (define ids            (syntax->list #'(id ...)))
     (define structures     (syntax->list #'(structure ...)))
     (define methodss       (map get-methods traits))

     (define (map-methods f id t s ms)
       (for/list ([m ms])
         (f id t s m)))

     (define (map-clauses f)
       (for/list ([id ids] [t traits] [s structures] [ms methodss])
         (map-methods f id t s ms)))

     (with-syntax
       ([((id.method ...) ...)        ; names used inside `with`
         (map-clauses (λ (id t s m)
                        (syntax-local-introduce
                         (identifier:id.method #'stx id m))))]

        [((structure-method ...) ...) ; names used outside `with`
         (map-clauses (λ (id t s m)
                        (syntax-local-introduce
                         (identifier:structure-method t s m))))]

        [((it ...) ...)               ; all id (in the right shape)
         (map-clauses (λ (id t s m) id))])
     (syntax/loc stx
       (let* ([id expression] ...)
         (let-syntaxes
             ([(id.method ...) ; we need a macro in other to pass id
               (values         ; to the associated structure-method
                (λ (call-stx)
                  (syntax-parse call-stx
                    [(_ . args)
                     (syntax/loc call-stx
                       (structure-method it . args))]))
                ...)]
              ...)
           . body))))]))

;;;
;;; Test
;;;

; Let's test the traits with a silly fish example.

(struct herring (size color) #:transparent)

(define-trait Fish (grow swim shrink))

(define (shrink f)
  (displayln (~a "This type of fish can't swim: " f)))

(implementation Fish herring
  ; swim : herring -> void
  (define (swim h)
     (match h
       [(herring s c) (displayln (~a "A " c " herring swims."))]))

  ; grow : fish integer -> fist
  ; Add the amount a to the size of of the fish.
  (define (grow h a)
    (match h
      [(herring s c) (herring (+ s a) c)])))


(define a-herring (herring 2 "gray"))

(with ([h Fish herring a-herring])
  (h.shrink)  ; picks up default definition
  (h.grow 3)) ; uses the herring implementation of Fish

;; ; =>
(let ([h a-herring])
  (herring-shrink h)
  (herring-grow h 3))

;;;
;;; A simple implementation of rings using traits.
;;;

(define-trait Set             (member? size)) ; a Set has the methods
 member? and size
(define-trait Monoid (Set)    ($))            ; a Monoid is a Set    with
an operation $
(define-trait Group  (Monoid) (inv))          ; a Group  is a Monoid with
an operation inv
(define-trait Ring   (Group)  (+ |0| * |1|))  ; a Ring is an additive Group
with an multiplicative monoid


(require (prefix-in + (only-in racket/base +))  ; ++ is now standard +
         (prefix-in * (only-in racket/base *))) ; ** is now standard *

(struct Z ())

(implementation Ring Z
   (define (member? R x) (integer? x))
   (define (size R)      +inf.0)
   ; Ring
   (define (+ R a b)     (++ a b))
   (define (* R a b)     (** a b))
   (define |0|            0)
   (define |1|            1)
   ; Group
   (define (inv R a)     (- a))
   ; Additive Monoid
   (define $ +))

(struct Zn (n))

(implementation Ring Zn
   (define (modulus R)    (Zn-n R))
   ; Set
   (define (member? R x)  (and (integer? x) (<= x (modulus R))))
   (define (size R)       (modulus R))
   ; Ring
   (define (+ R a b)      (modulo (++ a b) (modulus R)))
   (define (* R a b)      (modulo (** a b) (modulus R)))
   (define |0|            0)
   (define |1|            1)
   ; Group
   (define (inv R a)      (modulo (- a) (modulus R)))
   ; Additive Monoid
   (define $ +))


(struct Zx (x p))   ; p(x) belongs to Z[x], the ring of polynomials over Z

(require (prefix-in cas: racket-cas))

(implementation Ring Zx
   (define (var R)        (Zx-x R))
   ; Set
   (define (member? R f)  (cas:polynomial? f (var R))) ; approximate
   (define (size R)       +inf.0)
   ; Ring
   ;   Note: We are assuming a and b are normalized.
   ;         If a and b are normalized, the return values from + and * will
   ;         automatically be normalized.
   (define (+ R a b)      (cas:plus a b))
   (define (* R a b)      (cas:expand (cas:times a b)))
   (define |0|            0)
   (define |1|            1)
   ; Group
   (define (inv R a)      (cas:times -1 a))
   ; Additive Monoid
   (define $ +))


(struct Zx/I (x I)) ; the ideal I is represented by a polynomial p
; The ring of single variable polynomials over Z modulo an ideal I.
; Note: We are using racket-cas to compute, and it supports polynomials
;       over Z, Q and floating points. It does support polynomials
;       in several variables, so Zxy/I can be implemented in the same
;       manner.
(implementation Ring Zx/I
   (define (var R)        (Zx/I-x R))
   (define (I R)          (Zx/I-I R)) ; a polynomial
   (define (inject R p)   (cas:polynomial-remainder p (I R) (var R)))
   ; Set
   (define (member? R f)  (cas:polynomial? f (var R))) ; approximate
   (define (size R)       +inf.0)
   ; Ring
   (define (+ R a b)      (inject R (cas:plus a b)))
   (define (* R a b)      (inject R (cas:expand (cas:times a b))))
   (define |0|            0)
   (define |1|            1)
   ; Group
   (define (inv R a)      (cas:times -1 a))
   ; Additive Monoid
   (define $ +))



(with ([R Ring Z (Z)])
 (R.* (R.+ 1 2) 3))     ; => 9

(with ([R Ring Zn (Zn 5)])
 (R.* (R.+ 1 2) 3))    ; => 4

(with ([R Ring Zx Zx])
  (R.* (R.+ 'x 1) (R.+ 'x 1)))

(with ([R Ring Zx/I (Zx/I 'x (cas:normalize '(+ (* x x) 1)))])
  (R.* 'x 'x))  ; => -1

(with ([R Ring Zx/I (Zx/I 'x (cas:normalize '(+ (* x x) 1)))])
  (R.* (R.+ 'x 1) (R.+ 'x 1))) ; => 2x

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CABefVgwQWiZ6JOHFuoJpWozECSNDyat_J6b38tYOsdZWMs9uHQ%40mail.gmail.com.

Reply via email to