Constrained genericity: Difference between revisions
Content added Content deleted
m (=={{header|Racket}}== stub added... code comes in a moment.) |
|||
Line 777: | Line 777: | ||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
<code>edible<%></code> objects simply need to state that they implement the |
|||
interface in the second argument to <code>class*</code>. By doing so they will |
|||
be forced to implement <code>eat</code>. |
|||
<lang racket>#lang racket |
|||
(module+ test (require tests/eli-tester)) |
|||
;; This is all that an object should need to properly implement. |
|||
(define edible<%> |
|||
(interface () [eat (->m void?)])) |
|||
(define (generic-container<%> containee/c) |
|||
(interface () |
|||
[contents (->m (listof containee/c))] |
|||
[insert (->m containee/c void?)] |
|||
[remove-at (->m exact-nonnegative-integer? containee/c)] |
|||
[count (->m exact-nonnegative-integer?)])) |
|||
(define ((generic-box-mixin containee/c) %) |
|||
(->i ([containee/c contract?]) |
|||
(rv (containee/c) (implementation?/c (generic-container<%> containee/c)))) |
|||
(class* % ((generic-container<%> containee/c)) |
|||
(super-new) |
|||
(define l empty) |
|||
(define/public (contents) l) |
|||
(define/public (insert o) (set! l (cons o l))) |
|||
(define/public (remove-at i) |
|||
(begin0 (list-ref l i) |
|||
(append (take l i) (drop l (add1 i))))) |
|||
(define/public (count) (length l)))) |
|||
;; As I understand it, a "Food Box" from the task is still a generic... i.e. |
|||
;; you will specify it down ;; to an "apple-box%" so: food-box%-generic is still |
|||
;; generic. food-box% will take any kind of food. |
|||
(define/contract (food-box-mixin T%) |
|||
(-> (or/c (λ (i) (eq? edible<%> i)) (implementation?/c edible<%>)) |
|||
(make-mixin-contract)) |
|||
(generic-box-mixin (and/c (is-a?/c edible<%>) (is-a?/c T%)))) |
|||
(module+ test |
|||
(define integer-box% ((generic-box-mixin integer?) object%)) |
|||
(define integer-box (new integer-box%)) |
|||
(define apple% |
|||
(class* object% (edible<%>) |
|||
(super-new) |
|||
(define/public (eat) |
|||
(displayln "nom!")))) |
|||
(define banana% |
|||
(class* object% (edible<%>) |
|||
(super-new) |
|||
(define/public (eat) |
|||
(displayln "peel.. peel... nom... nom!")))) |
|||
(define semolina% |
|||
(class* object% () ; <-- empty interfaces clause |
|||
(super-new) |
|||
;; you can call eat on it... but it's not explicitly (or even vaguely) |
|||
;; edible<%> |
|||
(define/public (eat) (displayln "blech!")))) |
|||
;; this will take any object that is edible<%> and edible<%> (therefore all |
|||
;; edible<%> objects) |
|||
(define any-food-box (new ((food-box-mixin edible<%>) object%))) |
|||
;; this will take any object that is edible and an apple<%> |
|||
;; (therefore only apple<%>s) |
|||
(define apple-food-box (new ((food-box-mixin apple%) object%))) |
|||
(test |
|||
;; Test generic boxes |
|||
(send integer-box insert 22) |
|||
(send integer-box insert "a string") =error> exn:fail:contract? |
|||
;; Test the food box that takes any edible<%> |
|||
(send any-food-box insert (new apple%)) |
|||
(send any-food-box insert (new banana%)) |
|||
(send any-food-box insert (new semolina%)) =error> exn:fail:contract? |
|||
;; Test the food box that takes any apple% |
|||
(send apple-food-box insert (new apple%)) |
|||
(send apple-food-box insert (new banana%)) =error> exn:fail:contract? |
|||
(send apple-food-box insert (new semolina%)) =error> exn:fail:contract? |
|||
(send apple-food-box count) => 1 |
|||
;; Show that you cannot make a food-box from the non-edible<%> semolina cannot |
|||
(implementation? semolina% edible<%>) => #f |
|||
(new ((food-box-mixin semolina%) object%)) =error> exn:fail:contract?))</lang> |
|||
All the tests pass. Look at the tests to see what generates an exception (i.e. |
|||
not allowed at runtime) and what does not. |
|||
=={{header|Sather}}== |
=={{header|Sather}}== |