Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add newtype declarations #85

Open
dedbox opened this issue Aug 12, 2019 · 1 comment
Open

add newtype declarations #85

dedbox opened this issue Aug 12, 2019 · 1 comment

Comments

@dedbox
Copy link
Owner

dedbox commented Aug 12, 2019

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 unListed, 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])
@dedbox
Copy link
Owner Author

dedbox commented Aug 15, 2019

newtype has three main use cases:

  1. hide implementation details through indirection
  2. create many instances of a class with the same underlying type
  3. 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))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant