How to resolve the algorithm Constrained genericity step by step in the Racket programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Constrained genericity step by step in the Racket programming language

Table of Contents

Problem Statement

Constrained genericity or bounded quantification means that a parametrized type or function (see parametric polymorphism) can only be instantiated on types fulfilling some conditions, even if those conditions are not used in that function. Say a type is called "eatable" if you can call the function eat on it. Write a generic type FoodBox which contains a collection of objects of a type given as parameter, but can only be instantiated on eatable types. The FoodBox shall not use the function eat in any way (i.e. without the explicit restriction, it could be instantiated on any type). The specification of a type being eatable should be as generic as possible in your language (i.e. the restrictions on the implementation of eatable types should be as minimal as possible). Also explain the restrictions, if any, on the implementation of eatable types, and show at least one example of an eatable type.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Constrained genericity step by step in the Racket programming language

Source code in the racket programming language

#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?))


  

You may also check:How to resolve the algorithm Abbreviations, automatic step by step in the BQN programming language
You may also check:How to resolve the algorithm Modular exponentiation step by step in the Seed7 programming language
You may also check:How to resolve the algorithm HTTPS step by step in the Nim programming language
You may also check:How to resolve the algorithm Cheryl's birthday step by step in the VBA programming language
You may also check:How to resolve the algorithm Lucas-Lehmer test step by step in the ERRE programming language