Constrained genericity: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
(→{{header|TXR}}: New section.) |
||
Line 1,595: | Line 1,595: | ||
<syntaxhighlight lang="swift">func foo<T: Eatable>(x: T) { } |
<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> |
// 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}}== |
=={{header|Wren}}== |