Newtypes, and giving one the traits of the type it wraps
A newtype beside aliases in "Your own types", and #:via and type/derive for one in the traits chapter. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-5-5) Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
parent
6d9c9bf8a0
commit
bd71102c19
1 changed files with 44 additions and 0 deletions
|
|
@ -354,6 +354,25 @@ A type alias is a different name for a type that already exists. It is declared
|
|||
(type (: Stringalias string))
|
||||
}
|
||||
|
||||
@subsubsection{Newtypes}
|
||||
|
||||
A newtype is the opposite of an alias: a type of its own, which the compiler will not mix up with the type it wraps, but which is that type when the program runs, so it costs nothing.
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(type (: UserId (Newtype int)))
|
||||
(type (: OrderId (Newtype int)))
|
||||
|
||||
(def u (UserId 7)) ;; built by its name
|
||||
(def (UserId n) u) ;; taken apart by a pattern: n is 7
|
||||
|
||||
(: (-> UserId string))
|
||||
(defun (lookup-user id) ...)
|
||||
|
||||
(lookup-user (OrderId 7)) ;; a compile error: an OrderId is not a UserId
|
||||
}|
|
||||
|
||||
A newtype does not get the traits of the type it wraps: @code{(+ u u)} is an error, though @code{(+ 7 7)} is not. It gets those you give it, as the section on traits shows.
|
||||
|
||||
@subsubsection{Records and structs}
|
||||
Records and structs are direct mappings to c# records and immutable structs. The constructor is the type name, together with parenthesised fields. As follows.
|
||||
|
||||
|
|
@ -2172,6 +2191,31 @@ A trait may say that every type implementing it implements another trait too, wi
|
|||
|
||||
The prelude's @code{Ord} requires @code{Eq} this way, so a function with @code{(where (Ord %a))} can use @code{=} as well as @code{compare}.
|
||||
|
||||
@subsubsection{Giving a newtype the traits of the type it wraps}
|
||||
|
||||
A newtype is the type it wraps when the program runs, so it can borrow that type's implementation of a trait as it is, with @code{#:via}:
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(type (: Meters (Newtype double)))
|
||||
|
||||
(impl (Eq Meters) #:via double)
|
||||
(impl (Ord Meters) #:via double)
|
||||
(impl (Num Meters) #:via double) ;; + - * /
|
||||
(impl (Ordered Meters) #:via double) ;; < > <= >=
|
||||
|
||||
(+ (Meters 1.5) (Meters 2.0)) ;; (Meters 3.5)
|
||||
(+ (Meters 1.5) 2) ;; a number literal is a Meters too
|
||||
}|
|
||||
|
||||
@code{type/derive} writes these for you, for any trait, when the type is a newtype:
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(type/derive (Eq Ord Num Ordered)
|
||||
(: Meters (Newtype double)))
|
||||
}|
|
||||
|
||||
A trait that should behave differently is written by hand instead. That is the other use of a newtype: the same data with a different behaviour, such as an @code{Eq} that ignores case on a @code{(Newtype string)}. Sets and maps of the newtype use that implementation, not the wrapped type's.
|
||||
|
||||
@subsubsection{Eq, Ord and ->str}
|
||||
|
||||
Three traits from the prelude are worth knowing by heart, since the rest of the library leans on them.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue