Constrained genericity: Difference between revisions

m
{{out}} / moved omit down
m ({{out}} / moved omit down)
Line 1:
{{task|Object oriented}} [[Category:Type System]]{{omit from|BBC BASIC}}
'''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.
 
'''Constrained genericity''' or '''bounded quantification''' means
Say a type is called "eatable" if you can call the function <tt>eat</tt> on it. Write a generic type <tt>FoodBox</tt> 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.
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 <tt>eat</tt> on it.
Write a generic type <tt>FoodBox</tt> 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.
 
=={{header|Ada}}==
Ada allows various constraints to be specified in parameters of generics. A formal type constrained to be derived from certain base is one of them:
A formal type constrained to be derived from certain base is one of them:
<lang ada>with Ada.Containers.Indefinite_Vectors;
 
Line 42 ⟶ 55:
 
=={{header|C sharp|C#}}==
In C#, type constraints are made on the type hierarchy, so here we make <code>IEatable</code> an interface, with an <code>Eat</code> method. Types which are eatable would have to implement the <code>IEatable</code> interface and provide an <code>Eat</code> method.
Types which are eatable would have to implement the
<code>IEatable</code> interface and provide an <code>Eat</code> method.
<lang csharp>interface IEatable
{
void Eat();
}</lang>
Type constraints in type parameters can be made via the <code>where</code> keyword, which allows us to qualify T. In this case, we indicate that the type argument must be a type that is a subtype of <code>IEatable</code>.
In this case, we indicate that the type argument must be a type
that is a subtype of <code>IEatable</code>.
<lang csharp>using System.Collections.Generic;
 
Line 82 ⟶ 99:
The technique used here is like that in the [[Abstract type]] task.
 
The task says that this task is only for statically typed languages,
The task says that this task is only for statically typed languages, and Common Lisp is dynamically typed. However, there are many places where type declarations can be provided to the compiler, and there is user access to the type system (e.g., a user can ask whether an object is of a particular type). Via the latter mechanism, one could write a class containing a collection such that the insert method checked that the object to be inserted is of an appropriate type.
and Common Lisp is dynamically typed.
However, there are many places where type declarations can be provided
to the compiler, and there is user access to the type system
(e.g., a user can ask whether an object is of a particular type).
Via the latter mechanism, one could write a class containing a collection
such that the insert method checked that the object to be inserted
is of an appropriate type.
 
In this example, we define a class <code>food</code>, and two subclasses, <code>inedible-food</code> and <code>edible-food</code>.
In this example, we define a class <code>food</code>, and two subclasses, <code>inedible-food</code> and <code>edible-food</code>. We define a generic function <code>eat</code>, and specialize it only for <code>edible-food</code>. We then define a predicate <code>eatable-p</code> which returns true only on objects for which <code>eat</code> methods have been defined. Then, using <code>[http://www.lispworks.com/documentation/HyperSpec/Body/m_deftp.htm deftype]</code> with a <code>[http://www.lispworks.com/documentation/HyperSpec/Body/t_satisf.htm satisfies]</code> type specifier, we define a type <code>eatable</code> to which only objects satisfying <code>eatable-p</code> belong. Finally, we define a function <code>make-food-box</code> which takes, in addition to typical array creation arguments, a type specifier. The array is declared to have elements of the type that is the intersection of <code>food</code> and the provided type. <code>make-eatable-food-box</code> simply calls <code>make-food-box</code> with the type <code>eatable</code>.
We define a generic function <code>eat</code>,
and specialize it only for <code>edible-food</code>.
We then define a predicate <code>eatable-p</code> which returns true only on objects for which <code>eat</code> methods have been defined.
In this example, we define a class <code>food</code>, and two subclasses, <code>inedible-food</code> and <code>edible-food</code>. We define a generic function <code>eat</code>, and specialize it only for <code>edible-food</code>. We then define a predicate <code>eatable-p</code> which returns true only on objects for which <code>eat</code> methods have been defined. Then, using <code>[http://www.lispworks.com/documentation/HyperSpec/Body/m_deftp.htm deftype]</code> with a <code>[http://www.lispworks.com/documentation/HyperSpec/Body/t_satisf.htm satisfies]</code> type specifier, we define a type <code>eatable</code> to which only objects satisfying <code>eatable-p</code> belong. Finally, we define a function <code>make-food-box</code> which takes, in addition to typical array creation arguments, a type specifier. The array is declared to have elements of the type that is the intersection of <code>food</code> and the provided type. <code>make-eatable-food-box</code> simply calls <code>make-food-box</code> with the type <code>eatable</code>.
The array is declared to have elements of the type that is the intersection of <code>food</code> and the provided type.
<code>make-eatable-food-box</code> simply calls <code>make-food-box</code>
with the type <code>eatable</code>.
 
The only shortcoming here is that the compiler isn't required to enforce the type specifications for the arrays. A custom insert function, however, could remember the specified type for the collection, and assert that inserted elements are of that type.
Line 164 ⟶ 195:
 
=={{header|E}}==
It is surely arguable whether this constitutes an implementation of the above task:
of the above task:
<lang e>/** Guard accepting only objects with an 'eat' method */
def Eatable {
Line 313 ⟶ 345:
 
=={{header|F_Sharp|F#}}==
It is possible to constrain type parameters in a number of ways, including inheritance relationships and interface implementation. But for this task, the natural choice is an explicit member constraint.
including inheritance relationships and interface implementation.
But for this task, the natural choice is an explicit member constraint.
<lang fsharp>type ^a FoodBox // a generic type FoodBox
when ^a: (member eat: unit -> string) // with an explicit member constraint on ^a,
Line 327 ⟶ 361:
 
=={{header|Go}}==
Go's interfaces do exactly what this task wants. Eatable looks like this,
Eatable looks like this:
<lang go>type eatable interface {
eat()
}</lang>
And the following is all it takes to define foodbox as a slice of eatables.
And the following is all it takes to define foodbox as a slice of eatables. The result is that an object of type foodbox can hold objects of any type that implements the eat method (with the function signature specified in eatable.) The definition of foodbox though, doesn't even need to enumerate the functions of eatable, much less call them. Whatever is in the interface is okay.
The definition of foodbox though, doesn't even need to enumerate the functions of eatable, much less call them. Whatever is in the interface is okay.
<lang go>type foodbox []eatable</lang>
Here is an example of an eatable type.
Line 432 ⟶ 469:
 
=={{header|J}}==
J is not a statically typed language, but I do not see why we should not implement this in J:
but I do not see why we should not implement this in J:
<lang j>coclass'Connoisseur'
isEdible=:3 :0
Line 465 ⟶ 503:
=={{header|Java}}==
{{works with|Java|5}}
In Java type constraints are made on the type hierarchy, so here we make <code>Eatable</code> an interface, with an <code>eat</code> method. Types which are Eatable would have to implement the <code>Eatable</code> interface and provide an <code>eat</code> method.
Types which are Eatable would have to implement the
<code>Eatable</code> interface and provide an <code>eat</code> method.
<lang java5>interface Eatable
{
Line 535 ⟶ 575:
 
foreach (apple in appleBox) apple.Eat();</lang>
{{out}}
Output:
<pre>nom..nom..nom
nom..nom..nom
Line 573 ⟶ 613:
We just require that module instances of this module type describe a type <tt>t</tt> and implement a function <tt>eat</tt> which takes in the type and returns nothing.
 
The <tt>FoodBox</tt> generic type could be implemented as a ''functor'' (something which takes a module as an argument and returns another module):
(something which takes a module as an argument and returns another module):
<lang ocaml>module MakeFoodBox(A : Eatable) = struct
type elt = A.t
Line 599 ⟶ 640:
let my_box = BananaBox.make_box_from_list [Foo]
let your_box = FloatBox.make_box_from_list [2.3; 4.5]</lang>
Unfortunately, it is kind of cumbersome in that, for every type parameter we want to use for this generic type, we will have to explicitly create a module for the resulting type (i.e. <tt>BananaBox</tt>, <tt>FloatBox</tt>). And the operations on that resulting type (i.e. <tt>make_box_from_list</tt>) are tied to each specific module.
we want to use for this generic type, we will have to explicitly create a module
for the resulting type (i.e. <tt>BananaBox</tt>, <tt>FloatBox</tt>).
And the operations on that resulting type (i.e. <tt>make_box_from_list</tt>)
are tied to each specific module.
 
=={{header|ooRexx}}==
ooRexx methods, routines, and collections are all untyped, so there are no language-level checks for type matches. Tests for identity need to be performed at runtime using mechanisms such as the object isA method.
so there are no language-level checks for type matches.
Tests for identity need to be performed at runtime using mechanisms
such as the object isA method.
<lang ooRexx>
call dinnerTime "yogurt"
Line 778 ⟶ 826:
=={{header|Racket}}==
 
<code>edible&lt;%></code> objects simply need to state that they implement the
the interface in the second argument to <code>class*</code>. By doing so they will
By doing so they will be forced to implement <code>eat</code>.
 
<lang racket>#lang racket
Line 927 ⟶ 975:
 
=={{header|Scala}}==
Scala can constrain types in many different ways. This specific constrain, for the type to contain a particular method, can be written as this:
This specific constrain, for the type to contain a particular method,
can be written as this:
<lang scala>type Eatable = { def eat: Unit }
 
Line 939 ⟶ 989:
 
val foodBox = new FoodBox(List(new Fish("salmon")))</lang>
A more extensive discussion on genericity in Scala and some of the constrains that can be imposed can be found on [[Parametric Polymorphism]].
that can be imposed can be found on [[Parametric Polymorphism]].
 
=={{header|Swift}}==
Here we make <code>Eatable</code> a protocol, with an <code>eat</code> method. Types which are Eatable would have to conform to the <code>Eatable</code> protocol and provide an <code>eat</code> method.
Types which are Eatable would have to conform to the <code>Eatable</code> protocol
and provide an <code>eat</code> method.
<lang swift>protocol Eatable {
func eat()
}</lang>
Type constraints in type parameters can be made via the <code>:</code> syntax, indicating in this case that the type argument must be a type that is a subtype of <code>Eatable</code>.
a subtype of <code>Eatable</code>.
<lang swift>struct FoodBox<T: Eatable> {
var food: [T]
Line 956 ⟶ 1,010:
<!-- Place omit from templates below here -->
{{omit from|ALGOL 68|it isn't immediately obvious that ALGOL 68 is object oriented}}
{{omit from|BBC BASIC}}
{{omit from|C|type system doesn't support this}}
{{omit from|J|not statically typed}}
Anonymous user