{"size": 6691, "ext": "scrbl", "max_stars_repo_path": "algebraic/scribblings/algebraic-class-core.scrbl", "max_stars_repo_name": "aqui18/racket-algebraic", "max_stars_repo_stars_event_min_datetime": "2019-03-01T17:40:44.000Z", "max_stars_repo_stars_event_max_datetime": "2022-03-18T10:19:13.000Z", "max_issues_repo_path": "algebraic/scribblings/algebraic-class-core.scrbl", "max_issues_repo_name": "aqui18/racket-algebraic", "max_issues_repo_issues_event_min_datetime": "2018-08-07T18:39:35.000Z", "max_issues_repo_issues_event_max_datetime": "2022-02-20T00:37:39.000Z", "max_forks_repo_path": "algebraic/scribblings/algebraic-class-core.scrbl", "max_forks_repo_name": "aqui18/racket-algebraic", "max_forks_repo_forks_event_min_datetime": "2019-03-31T22:50:06.000Z", "max_forks_repo_forks_event_max_datetime": "2022-02-24T00:27:29.000Z"}
{"max_stars_count": 75, "max_issues_count": 91, "max_forks_count": 6, "avg_line_length": 26.764, "max_line_length": 91, "alphanum_fraction": 0.6159019579}
#lang scribble/manual

@title[#:tag "class:core"]{Defining Classes and Instances}

@require{./algebraic-includes.rkt}

@require[
  @for-label[
    (except-in algebraic/racket/base #%module-begin)
    racket/contract/base
  ]
]

@define[class-eval (algebraic-evaluator)]
@define-syntax[example (algebraic-example/locations class-eval)]

@example[#:hidden
  (require algebraic/class)
]

@; #############################################################################

A @deftech{class} is an abstract collection of names with optional default
values.

Algebraic Racket's @tech{class}es are a lot like Haskell's type classes
without the typing constraints. They allow abstract design patterns like
monads with do-notation to be expressed simply and in a generic way that makes
sense for Racket.

The @racket[class] form declares an abstract @tech{class} and its members.

@example[
  (class Eq
    [== (.. not /=)]
    [/= (.. not ==)]
    minimal ([==] [/=]))
]

Using an uninstantiated member is a syntax error.

@example[
  (eval:error ==)
  (eval:error (/= 1 0))
]

The @racket[instance] form produces an object that holds @tech{class} member
definitions.

@example[
  (define-syntax StringEq (instance Eq [== string=?]))
  (define-syntax NumberEq (instance Eq [/= (|| < >)]))
]

Attempting to define an incomplete instance is a syntax error.

@example[
  (eval:error (define-syntax BadEq (instance Eq)))
]

The @racket[with-instance] form temporarily instantiates the members of an
instance.

@example[
  (with-instance StringEq
    (and (== "abcde" "abcde")
         (/= "hello" "world")))
]

@example[
  (with-instance NumberEq
    (and (== 1 1)
         (/= 2 3)))
]

The @racket[with-instance] form can prefix the names it binds so multiple
instances of the same class can be used together.

@example[
  (with-instance [S: StringEq]
    (with-instance [N: NumberEq]
      (and (S:== "abc" "abc")
           (N:== 123 123))))
]

The @racket[with-instances] form offers a cleaner syntax for working with
multiple instances.

@example[
  (define-syntax EqEq (instance Eq [== eq?]))
  (with-instances (EqEq
                   [S: StringEq]
                   [N: NumberEq])
    (and (== + +) (S:== "?" "?") (N:== 0 0)))
]

@; #############################################################################

@defform[
  (class class-id member-decl-or-def ...+ maybe-minimal)
  #:grammar [(member-decl-or-def [member-id]
                                 [member-id def-expr])
             (maybe-minimal (code:line)
                            (code:line #,(racketidfont "minimal") ([member-id ...+] ...)))]
]{

  Creates a new @tech{class} and @gtech{transformer bindings} related to it.

  A @racket[class] form with @var[n] members defines @var[n+1] names:

  @itemlist[

    @item{@var[class-id], a @deftech{class descriptor} that represents the
      @tech{class}.}

    @item{for each @var[member-id], a @gtech{transformer binding} that raises
      a syntax error.}

  ]

  Example:
  @example[
    (class Continuation
      [call]
      [abort (φ x (error (format "abort: ~a" x)))]
      minimal ([call]))
    (eval:error (call))
    (eval:error (abort 'no-op))
  ]

  The @racketid[minimal] directive, when present, constrains valid instances
  to only those that provide at least one of the minimal @var[member-id] sets.

  Example:
  @example[
    (eval:error
     (define-syntax BadContinuation
       (instance Continuation [abort void])))
  ]
}

@defform[
  (instance class-id maybe-extends [member-id def-expr] ...+)
  #:grammar [(maybe-extends (code:line)
                            (code:line #,(racketidfont "extends") (instance-id ...)))]
]{

  Produces an @deftech{instance descriptor} that represents a run-time
  implementation of the @tech{class} bound to @var[class-id] with its
  @var[member-id]s extended or overridden by @var[def-expr]s.

  The @racketid[extends] directive, when present, recursively imports the
  members of the @var[instance-id]s and the instances they extend.

  Examples:
  @example[
    (define current-esc (make-parameter #f))
    (define current-con (make-parameter #f))
    (define ((current-abort param msg) . xs)
      (if (param) ($ (param) xs) (error msg)))
  ]

  @example[
    (define-syntax EscapeContinuation
      (instance Continuation
        [call (φ f (call/ec (φ esc (parameterize ([current-esc esc]) (f)))))]
        [abort (current-abort current-esc "no escape continuation")]))
  ]

  @example[
    (define-syntax CurrentContinuation
      (instance Continuation
        [call (φ f (call/cc (φ con (parameterize ([current-con con]) (f)))))]
        [abort (current-abort current-con "no current continuation")]))
  ]
}

@deftogether[(
@defform[(with-instance instance-id/optional-prefix expr ...+)]
@defform[(with-instances (instance-id/optional-prefix ...) expr ...+)
         #:grammar [(instance-id/optional-prefix instance-id
                                                 [prefix-id instance-id])]]
)]{

  Evaluates the @var[exprs] with the members of the @var[class-id]s
  instantiated according to the instances bound to the @var[instance-id]s.

  If any @var[prefix-id]s are given, they are prepended to the names of the
  members defined by the corresponding @var[instance-id]s.

  Examples:
  @example[
    (with-instance EscapeContinuation
      (call (λ () (println 'START) (abort 'ESCAPE) (println 'DONE))))
  ]

  @example[
    (with-instance CurrentContinuation
      (call (λ () (abort 1 2) 3)))
  ]
}

@deftogether[(
@defidform[splicing-with-instance]
@defidform[splicing-with-instances]
)]{

  Like @racket[with-instance] and @racket[with-instances], except that in a
  definition context, the body forms are spliced into the enclosing definition
  context (in the same way as for @racket[begin]).

  Examples:
  @example[
    (splicing-with-instance EscapeContinuation
      (define (f x)
        (call (λ () (abort `(ESC ,x))))))
    (f 1)
  ]
  
  @example[
    (splicing-with-instance [C: CurrentContinuation]
      (define (g x)
        (C:call (λ () (C:abort `(ABORT ,x))))))
    (g 2)
  ]
}

@defform*[((instantiate instance-id)
           (instantiate prefix instance-id))]{

  Defines the members of @var[instance-id] with optional @var[prefix] for the
  remainder of the containing module or top level.

  Example:
  @example[
    (module inst algebraic/racket/base
      (class Eq
        [== (.. not /=)]
        [/= (.. not ==)]
        minimal ([==] [/=]))
      (define-syntax EqEq (instance Eq [== eq?]))
      (define-syntax StringEq (instance Eq [== string=?]))
      (instantiate EqEq)
      (instantiate S: StringEq)
      (== 'A 'A)
      (S:/= "a" "b"))
    (require 'inst)
  ]
}
