racket-algebraic
racket-algebraic copied to clipboard
add newtype declarations
A special notation for representing ordinary Racket data as a single-field product type.
newtype
instances are indistinguishable from their field at run time. Their main purpose is to provide a basis for code generation and future development into modular newtype
-aware type system components.
In the following example,
(1newtype List list? list (>> $ id))
a List
is any value recognized by the List?
predicate, which in this case
is bound to list?
. At run time, instances of the type can be created with
the List
constructor, here bound to list
. Instances can also be matched
against the List
pattern and then de-constructed further.
And finally, a List
can be unList
ed, which in this case returns the
elements of the list as multiple distinct values. If unList
does not receive
exactly one argument, or if its sole argument is not a List?
, a run-time
error is raised.
The Short Form
Takes 3 or 4 non-keyword arguments.
Arguments:
- type name
<T>
- predicate value
- constructor value
- optional de-constructor value
Defines:
- type descriptor
type:<T>
- match pattern
<T>
- predicate function
<T>?
- constructor function
<T>
- optional de-constructor function
un<T>
The Long Form
Takes 3 to 5 keyword arguments.
Required Keyword Arguments:
- type name
<T>
- predicate name
<T?>
and value - constructor name
<TC>
and value
Optional Keyword Argument:
- optional match pattern name
<TM>
- optional de-constructor name
<unT>
and value
Defines:
- type description
type:<T>
- match pattern
<TM>
or<T>
- predicate function
<T?>
- constructor function
<TC>
- optional de-constructor function
<unT>
More Examples
;;; short form
(newtype List list? list)
;;; defaults:
;;; match pattern name: List
;;; predicate name: List?
;;; constructor name: List
;;;
;;; no de-constructor
(newtype Pair pair? :: (φ (a . b) (id a b)))
;;; defaults:
;;; match pattern name: Pair
;;; predicate name: Pair?
;;; constructor name: Pair
;;; de-constructor name: unPair
;;; Arguments in a box!
(newtype Args ;type name
(&& box? (.. list? unbox)) ;predicate value
(.. box list) ;constructor value
(.. id unbox)) ;de-constructor value
;;; defaults:
;;; match pattern name: Args
;;; predicate name: Args?
;;; constructor name: Args
;;; de-constructor name: unArgs
;;; Long form
(newtype State
#:match state
#:predicate [state? number?]
#:constructor [state id]
#:de-constructor [get-state id])
newtype
has three main use cases:
- hide implementation details through indirection
- create many instances of a class with the same underlying type
- create class instances for Racket data types
Indirection
(class WidgetImpl s
(: make-widget (-> s (WidgetImpl s)))
(: widget-state (-> (WidgetImpl s) s))
(: widget? (-> a Bool)))
(newtype Widget ;The Widget (sum) type
[Widget make-widget] ;is implemented by make-widget,
[Widget? widget?] ;can be recognized by its implementation,
[Widget-state (φ (Foo s) s)]) ;and carries a "state" value
Now I can export Widget
as an API without leaking the details of how it's implemented:
(provide (newtype-out Widget))
;;; or, equivalently:
(provide (sum Widget) Widget Widget? Widget-state)
Many Instances
This appears to be its main use in Haskell. The class system is evolving to accommodate automatic code generation, initially for run-time type checks and dynamic dispatch. The changes will effectively turn classes into an opt-in mechanism for type-directed code generation and lay the groundwork for compile-time optimizations by class
-aware type checker modules.
Before any code can be generated, the types must be analyzed. This means the types must be specified in the code or inferred automatically (see #86). With respect to code generation, each newtype
alias has its own set of instances distinct from all the others.
Racket data
newtype
allows any function to be used as a constructor or de-constructor, enabling class instances for built-in data types, such as boxes:
(newtype Box
[Box box]
[Box? box?]
[unBox unbox])
(instance WidgetImpl Box
(define make-widget Box)
(define widget? Box?)
(define widget-state unBox))
or lists:
(newtype List
[List list]
[List? list?]
[unList id])
(instance WidgetImpl List
(define make-widget List)
(define widget? List?)
(define widget-state unList))