Constrained genericity: Difference between revisions

→‎{{header|TXR}}: New section.
m (syntax highlighting fixup automation)
(→‎{{header|TXR}}: New section.)
Line 1,595:
<syntaxhighlight lang="swift">func foo<T: Eatable>(x: T) { }
// although in this case this is no more useful than just "func foo(x: Eatable)"</syntaxhighlight>
 
=={{header|TXR}}==
 
We implement a food-box-defining macro, which checks at macro expansion time that the contained type is <code>edible</code>. The macro generates a structure of the specified name, which has a <code>set-food</code> method that additionally performs a run-time check against the exact variant of the <code>edible</code> type that was given to the macro.
 
<syntaxhighlight lang="txrlisp">(defmacro define-food-box (name food-type : supers . clauses)
(unless (subtypep food-type 'edible)
(error "~s requires edible type, not ~s" %fun% food-type))
^(defstruct ,name ,supers
food
(:method set-food (me food)
(unless (typep food ',food-type)
(error "~s: requires ~s object, not ~s" %fun% ',food-type food))
(set me.food food))
,*clauses))</syntaxhighlight>
 
Instead of the type-based <code>subtypep</code> check, we could easily check for the existence of methods; e.g. test for the presence of a static slot using <code>(static-slot-p food-type 'eat)</code>, or more specifically that it's a function: <code>(functionp (static-slot food-type 'eat))</code>. These tests will blow up if the macro's <code>food-type</code> argument isn't a struct type.
 
{{out}}
 
In the interactive session, we:
 
* verify that <code>define-food-box</code> cannot be used with a type argument that isn't derived from <code>edible<code>
* define the struct type <code>edible</code> and then one derived from it called <code>perishable</code>.
* use <code>define-food-box</code> to define a box called <code>fridge</code> which holds <code>perishable</code>. This works because <code>perishable</code> is <code>edible</code>
* create an instance of <code>fridge</code> and show that its <code>set-food</code> method doesn't take an integer, or an <code>edible<code>; only a <code>perishable</code>.
 
<pre>$ txr -i generic.tl
This area is under 24 hour TTY surveillance.
1> (define-food-box fridge string)
** define-food-box requires edible type, not string
** during evaluation of form (error "~s requires edible type, not ~s"
'define-food-box
food-type)
** ... an expansion of (error "~s requires edible type, not ~s"
%fun% food-type)
** which is located at generic.tl:3
1> (defstruct edible ())
#<struct-type edible>
2> (defstruct perishable (edible))
#<struct-type perishable>
3> (define-food-box fridge perishable)
#<struct-type fridge>
4> (new fridge)
#S(fridge food nil)
5> *4.(set-food 42)
** (set-food fridge): requires perishable object, not 42
** during evaluation of form (error "~s: requires ~s object, not ~s"
'(set-food fridge)
'perishable
food)
** ... an expansion of (error "~s: requires ~s object, not ~s"
%fun% 'perishable
food)
** which is located at expr-3:1
5> *4.(set-food (new edible))
** (set-food fridge): requires perishable object, not #S(edible)
** during evaluation of form (error "~s: requires ~s object, not ~s"
'(set-food fridge)
'perishable
food)
** ... an expansion of (error "~s: requires ~s object, not ~s"
%fun% 'perishable
food)
** which is located at expr-3:1
5> *4.(set-food (new perishable))
#S(perishable)
6> *4
#S(fridge food #S(perishable))</pre>
 
=={{header|Wren}}==
543

edits