* Design by contract and control inversion
@ 2012-10-31 19:28 Yannick Duchêne (Hibou57)
2012-11-01 17:13 ` Yannick Duchêne (Hibou57)
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-10-31 19:28 UTC (permalink / raw)
Hi all,
I wondered if there are known idioms to express predicates for callbacks,
which may be via access to subprogram or interface/tagged types. Control
inversion seems to not be easily mixable with design by contract. An
example to make it clearer.
Say a type `T` has a method with expect either an access to subprogram or
an interface:
type T is private;
procedure Expect_Handler
(Me : T;
Handler : not null access
procedure (Context : T));
type H is interface;
procedure Handle
(Me : H; Context : T)
is abstract;
procedure Expect_Handler
(Me : T;
Handler : H'Class);
Now let say the type `T` has different properties, and one is always
`True` when no handler in currently invoked, and always `False` when an
handler is currently running (which may be nesting), so that from inside
of any `Handle` interface method of subprogram, then that property will
always be `False`, and that I want it to be part of the specification.
I can't make it a precondition for the `Handle` method of type `H`, that
would not be clean and not what's really intended. Worst, I can't express
anything at all with the access to subprogram case.
The only thing I could imagine, is to create a second type, `U`,
re‑interfacing `T`, and passed to the handlers instead of `T`:
type T is private;
type U (<>) is limited private;
procedure Expect_Handler
(Me : T;
Handler : not null access
procedure (Context : U));
type H is interface;
procedure Handle
(Me : H; Context : U)
is abstract;
procedure Expect_Handler
(Me : T;
Handler : H'Class);
And in the private part, one of `U` or `T` wraps the other (within a
record). Not that clean, but at least, `T` and `U` can expose different
properties, and `U` is only used to be passed as parameters to handlers.
This construct is not only interesting for design by contract, as it also
allows to expose a different subprogram set for both type; as an example,
in the real thing I did, `U` only gets a subset of the subprograms
applicable to `T`.
Do you believe that's the best way to do? Or is this too much tricky and
you know another idiom?
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-10-31 19:28 Design by contract and control inversion Yannick Duchêne (Hibou57)
@ 2012-11-01 17:13 ` Yannick Duchêne (Hibou57)
2012-11-01 20:29 ` Adam Beneschan
2012-11-02 16:45 ` Shark8
2 siblings, 0 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-01 17:13 UTC (permalink / raw)
Le Wed, 31 Oct 2012 20:28:30 +0100, Yannick Duchêne (Hibou57)
<yannick_duchene@yahoo.fr> a écrit:
> The only thing I could imagine, is to create a second type, `U`,
> re‑interfacing `T`, and passed to the handlers instead of `T`:
>
>
> type T is private;
>
> type U (<>) is limited private;
>
> […]
>
> […] in the real thing I did, `U` only gets a subset of the subprograms
> applicable to `T`.
May make one feels it would be better to expression the subset relation
with a type relation, i.e. type `T` would be a derived type of `U` (hence,
`U`'s properties, is a subset of that of `T`).
Unfortunately, doing this:
type U (<>) is tagged limited private;
type T is new U with private;
… leads into an issue: you want to disallow any instance of `U` to be
created (the reason of the unknown discriminant), but this also disallow
instantiation of `T` as‑is, as `T` inherits the unknown discriminant. Can
have a `Create` or `Instance` function, but would be cleaner to be able to
get ride of this unknown discriminant. If `T` adds capabilities to `U`, it
should be able to get ride of it, isn't it?
That make me feel again that use of the unknown discriminant is too much
like a hack, when you just want to disallow instantiation from the client
side. Would be nice to have something to directly express it, not
involving any discriminants, which is a separate concept (indeed, the full
view of the type, does not have, and does not need any).
There use to be thread on that topic, but can't remember which one it was.
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-10-31 19:28 Design by contract and control inversion Yannick Duchêne (Hibou57)
2012-11-01 17:13 ` Yannick Duchêne (Hibou57)
@ 2012-11-01 20:29 ` Adam Beneschan
2012-11-02 3:40 ` Yannick Duchêne (Hibou57)
2012-11-02 16:45 ` Shark8
2 siblings, 1 reply; 9+ messages in thread
From: Adam Beneschan @ 2012-11-01 20:29 UTC (permalink / raw)
On Wednesday, October 31, 2012 12:28:33 PM UTC-7, Hibou57 (Yannick Duchêne) wrote:
> Hi all,
>
> I wondered if there are known idioms to express predicates for callbacks,
> which may be via access to subprogram or interface/tagged types. Control
> inversion seems to not be easily mixable with design by contract. An
> example to make it clearer.
>
> Say a type `T` has a method with expect either an access to subprogram or
> an interface:
>
>
> type T is private;
>
> procedure Expect_Handler
> (Me : T;
> Handler : not null access
> procedure (Context : T));
>
> type H is interface;
>
> procedure Handle
> (Me : H; Context : T)
> is abstract;
>
> procedure Expect_Handler
> (Me : T;
> Handler : H'Class);
>
>
> Now let say the type `T` has different properties, and one is always
> `True` when no handler in currently invoked, and always `False` when an
> handler is currently running (which may be nesting), so that from inside
> of any `Handle` interface method of subprogram, then that property will
> always be `False`, and that I want it to be part of the specification.
Let me see if I understand correctly. You have a package that defines
T and some operations on T, including Expect_Handler (which in real
life would probably be some useful operation on T that could call the
callback). (1) You want human users of the package to know that, when
they write the procedure My_Handler that will be passed to
Expect_Handler, that My_Handler can count on a particular property of
T being false. (2) You also want users to know that they can count on
that property of T being true at other times.
Well, the first thing to do is to write a comment in your package
specification saying so.
It seems that you want something more formal than just a comment,
though. While I can understand why you'd want to do this, I don't
think it's feasible in Ada. As far as #1 is concerned, Ada doesn't
have a syntax for adding preconditions or postconditions to an
access-procedure parameter. I can see how this feature might be
useful, so that when Expect_Handler calls Handler.all, the compiler
would generate the checks before and/or after the call. But it
doesn't exist right now. As for #2, I can't imagine any language ever
supporting anything like this, since I can't even imagine how one
would express the concept of "other times" in a computer language.
My concern is that you're trying to come up with a "solution", but
you're thinking of solutions that, in my opinion, will make the
specification more obscure to a reader. What's the gain in that? To
me, "design by contract" is more of an approach to software design,
rather than a language feature; language features can help support
this design approach, but the language features are not themselves
"design by contract". (Thus, I think your earlier statement that
"control inversion seems not be easily mixable with design by
contract" is a fallacy; I think they go together just fine, it's just
that we don't yet know how to add language support for it yet.)
To me, the important thing is that you have the contract in mind when
you design the package, and you express it in a way so that other
programmers who are using this package will know what conditions are
expected of their code, and what conditions they have a right to
expect from yours. If the only way to do that is with comments, then
do it that way. But since that's the important thing, trying to come
up with a tricky or idiomatic "solution" to your problem would tend to
defeat your purpose more than to serve it. Unless, that is, there's
some other purpose you really, really need to accomplish. If so, then
you'll probably have to provide more specific details about the code
you're trying to design.
Just my opinion..... I'm sure others will differ.
-- Adam
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-11-01 20:29 ` Adam Beneschan
@ 2012-11-02 3:40 ` Yannick Duchêne (Hibou57)
2012-11-02 8:59 ` Yannick Duchêne (Hibou57)
0 siblings, 1 reply; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-02 3:40 UTC (permalink / raw)
Le Thu, 01 Nov 2012 21:29:49 +0100, Adam Beneschan <adam@irvine.com> a
écrit:
> Let me see if I understand correctly. You have a package that defines
> T and some operations on T, including Expect_Handler (which in real
> life would probably be some useful operation on T that could call the
> callback). (1) You want human users of the package to know that, when
> they write the procedure My_Handler that will be passed to
> Expect_Handler, that My_Handler can count on a particular property of
> T being false. (2) You also want users to know that they can count on
> that property of T being true at other times.
Yes, that's it.
> It seems that you want something more formal than just a comment,
> though. While I can understand why you'd want to do this, I don't
> think it's feasible in Ada. As far as #1 is concerned, Ada doesn't
> have a syntax for adding preconditions or postconditions to an
> access-procedure parameter. I can see how this feature might be
> useful, so that when Expect_Handler calls Handler.all, the compiler
> would generate the checks before and/or after the call. But it
> doesn't exist right now.
I more hardly see the same with the interface type (instead of an access
to sub-program).
> My concern is that you're trying to come up with a "solution", but
> you're thinking of solutions that, in my opinion, will make the
> specification more obscure to a reader. What's the gain in that?
I tried, and when I saw this was too difficult, I gave up. I ended with
the solution posted elsewhere in this thread, which is to use another type
for the parameter passed to the handler. But that created some other
issues worth to be discussed (will be back with it later).
> To me, "design by contract" is more of an approach to software design,
> rather than a language feature; language features can help support
> this design approach, but the language features are not themselves
> "design by contract".
Not sure I've understood, but I will try. The comment seems interesting.
> To me, the important thing is that you have the contract in mind when
> you design the package, and you express it in a way so that other
> programmers who are using this package will know what conditions are
> expected of their code, and what conditions they have a right to
> expect from yours. If the only way to do that is with comments, then
> do it that way.
I did it that way. For the time, “other programmers” is just me (some
others may have opportunities to see it, but that's for a far later time).
> But since that's the important thing, trying to come
> up with a tricky or idiomatic "solution" to your problem would tend to
> defeat your purpose more than to serve it.
Sometime it's not easy to see if you just fail to properly setup a design
which may be good, or if it's just the design which is wrong. At least,
that's good experiments to remember about. I'm currently facing a similar
question, trying to mimic SML's signatures using Ada generics and
interface types.
I may post part of the specification in this thread, to request people for
comments. Will be useful to know if whether or not it's clear enough and
understandable (just don't expect anything wonderful, that's a stupidly
simple thing apart of the question raised here).
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-11-02 3:40 ` Yannick Duchêne (Hibou57)
@ 2012-11-02 8:59 ` Yannick Duchêne (Hibou57)
2012-11-02 12:32 ` Yannick Duchêne (Hibou57)
2012-11-07 1:34 ` Yannick Duchêne (Hibou57)
0 siblings, 2 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-02 8:59 UTC (permalink / raw)
Le Fri, 02 Nov 2012 04:40:44 +0100, Yannick Duchêne (Hibou57)
<yannick_duchene@yahoo.fr> a écrit:
> I may post part of the specification in this thread, to request people
> for comments. Will be useful to know if whether or not it's clear enough
> and understandable (just don't expect anything wonderful, that's a
> stupidly simple thing apart of the question raised here).
Here is… reading Adam, I get the idea to post an excerpt to ensure it's
intelligible enough. That does not really stand for the real thing, I'm
just posting an intermediate thing, which is enough to looks useful, but
lacks many things. Hope that's not too long for a Usenet post.
Although I decided to use a façade type, I still could not avoid to put
some preconditions at multiple place. Comment explains why (in short,
that's due to the remaining ability to access the object via other paths
than the one provided to the handler sub-program).
-- abc.ads
package ABC is
pragma Pure;
end ABC;
-- abc-sets.ads
package ABC.Sets is
pragma Pure;
end ABC.Sets;
-- abc-sets-base.ads
generic
type Element_Type is private;
type Cardinal_Type is (<>);
package ABC.Sets.Base is
type Instance_Type is limited interface;
Read_Unassigned : exception;
Update_Unassigned : exception;
-- An implementation may raise the above exceptions, even
-- when pre/post assertions are not enabled.
--
------------------------------------------------------------------------
function Is_Assigned
(Instance : Instance_Type)
return Boolean
is abstract;
-- For pre/post assertions and defensive design.
--
------------------------------------------------------------------------
procedure Add
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Pre'Class =>
Is_Assigned (Instance);
procedure Add
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with Pre'Class =>
Is_Assigned (Instance) and
Is_Assigned (Other);
procedure Assign
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Post'Class =>
Is_Assigned (Instance);
procedure Assign
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with
Pre'Class => Is_Assigned (Other),
Post'Class => Is_Assigned (Instance);
function Cardinal
(Instance : in Instance_Type)
return Cardinal_Type
is abstract
with Pre'Class =>
Is_Assigned (Instance);
function Create
return Instance_Type
is abstract;
-- `Is_Assigned` may or may not be `True`, depending
-- on concrete implementation's choice.
function Equal
(Instance : in Instance_Type;
Other : in Instance_Type)
return Boolean
is abstract
with Pre'Class =>
Is_Assigned (Instance) and
Is_Assigned (Other);
function Has
(Instance : in Instance_Type;
Element : in Element_Type)
return Boolean
is abstract
with Pre'Class =>
Is_Assigned (Instance);
function Has
(Instance : in Instance_Type;
Other : in Instance_Type)
return Boolean
is abstract
with Pre'Class =>
Is_Assigned (Instance) and
Is_Assigned (Other);
procedure Remove
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Pre'Class =>
Is_Assigned (Instance);
procedure Remove
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with Pre'Class =>
Is_Assigned (Instance) and
Is_Assigned (Other);
-- Concrete implementations must consider the case
-- where `Instance` and `Other` refers (directly or indirectly)
-- to the same object, whether what "object" is for the
-- implementation.
end ABC.Sets.Base;
-- abc-sets-functional.ads
-- Functional set.
--
-- The functional behaviour, its absence of dependencies to side
-- effects during evaluation, provides optimization opportunities
-- for concrete implementations.
--
-- A classic interface would leave, to create complex expressions,
-- no other choices than using regular functions each returning
-- a sub-expression. An alternative is to directly write to
-- a target result object instead, avoiding returns from function
-- and passing to functions parameters. A simple and safe
implementation
-- may still go this way, and use an result object per sub-expression.
-- A optimized implementation may benefits from the enforced no-side
-- effect behaviour (whether or not an element is set, does not depends
-- on whether or not another element is set).
with ABC.Sets.Base;
generic
with package Base is new ABC.Sets.Base (<>);
package ABC.Sets.Functional is
type Expression_Type is limited interface;
subtype Expression_Class is Expression_Type'Class;
-- Instead of a function returning a set, an expression,
-- is a subprogram writing to a result target object. The
-- concrete implementation is responsible for the object
-- passed to be written to. The sub-program then write
-- its sub-expression to it, using basic methods such as
-- `Add`, `Remove` and so on.
subtype Element_Type is Base.Element_Type;
subtype Cardinal_Type is Base.Cardinal_Type;
type Instance_Type is limited interface and Base.Instance_Type;
subtype Instance_Class is Instance_Type'Class;
type Instance_Write_Only_Type is limited interface;
subtype Instance_Write_Only_Class is Instance_Write_Only_Type'Class;
-- A restricted façade to an `Instance_Type`: no read methods,
intended
-- to be written to only, as a target result object. It enforce the
-- functional semantic, via type safety. If an expression
sub-program
-- attempts to modify or read the target set (ex. using an object in
-- its scope), then an error is raised. This case cannot avoided by
-- type safety mechanisms, and runtime checks are still required to
-- prevent it (see `Is_Being_Assigned`).
Illegal_Assignment : exception;
Illegal_Operation : exception;
Illegal_Read : exception;
Nesting_Limit : exception;
Self_Dependency : exception;
-- The above exceptions may be raised by an implementation, even
-- when pre/post assertions are disabled.
--
------------------------------------------------------------------------
function Is_Being_Assigned
(Instance : Instance_Type)
return Boolean
is abstract;
-- Property used for runtime checks of the functional semantic (for
the
-- part of it which cannot be ensured by type safety).
--
------------------------------------------------------------------------
procedure Add
(Instance : in out Instance_Write_Only_Type;
Element : in Element_Type)
is abstract;
procedure Add
(Instance : in out Instance_Write_Only_Type;
Expression : in Expression_Class)
is abstract;
procedure Add
(Instance : in out Instance_Write_Only_Type;
Other : in Instance_Class)
is abstract
with Pre'Class =>
Is_Assigned (Instance_Type (Other)) and
not Is_Being_Assigned (Instance_Type (Other));
procedure Remove
(Instance : in out Instance_Write_Only_Type;
Element : in Element_Type)
is abstract;
procedure Remove
(Instance : in out Instance_Write_Only_Type;
Expression : in Expression_Class)
is abstract;
procedure Remove
(Instance : in out Instance_Write_Only_Type;
Other : in Instance_Class)
is abstract
with Pre'Class =>
Is_Assigned (Instance_Type (Other)) and
not Is_Being_Assigned (Instance_Type (Other));
--
------------------------------------------------------------------------
procedure Evaluate
(Expression : in Expression_Type;
Result : in out Instance_Write_Only_Type'Class)
is abstract;
-- `Expression` has `in` mode, so that it can be the one
-- returned by a function.
--
-- The `in` mode `Result` holds, does not mean it is readable
-- (see some of the comments above). That's just required for any
-- implementation to get a chance to be feasible.
--
------------------------------------------------------------------------
-- `Expression`'s `Evaluate` methods, will receive `Instance`
-- behind an `Instance_Write_Only_Type` façade.
--
-- During `Evaluate`, `Is_Being_Assigned (Instance)` is `True`.
procedure Assign
(Instance : in out Instance_Type;
Expression : in Expression_Class)
is abstract
with Post'Class =>
Is_Assigned (Instance);
-- The old status of the instance, may or may not be
`Is_Assigned`.
-- An implementation must support both cases.
--
------------------------------------------------------------------------
-- Only root level modification allowed. No modification, nor read
-- access allowed, while it is being evaluated by an expression
-- sub-program (which may be nesting).
overriding procedure Add
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance);
overriding procedure Add
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance) and
not Is_Being_Assigned (Other);
overriding procedure Assign
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance);
overriding procedure Assign
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance) and
not Is_Being_Assigned (Other);
overriding function Cardinal
(Instance : in Instance_Type)
return Cardinal_Type
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance);
overriding function Equal
(Instance : in Instance_Type;
Other : in Instance_Type)
return Boolean
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance) and
not Is_Being_Assigned (Other);
overriding function Has
(Instance : in Instance_Type;
Element : in Element_Type)
return Boolean
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance);
overriding function Has
(Instance : in Instance_Type;
Other : in Instance_Type)
return Boolean
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance) and
not Is_Being_Assigned (Other);
overriding procedure Remove
(Instance : in out Instance_Type;
Element : in Element_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance);
overriding procedure Remove
(Instance : in out Instance_Type;
Other : in Instance_Type)
is abstract
with Pre'Class =>
not Is_Being_Assigned (Instance) and
not Is_Being_Assigned (Other);
end ABC.Sets.Functional;
(you were warned, that's nothing wonderful)
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-11-02 8:59 ` Yannick Duchêne (Hibou57)
@ 2012-11-02 12:32 ` Yannick Duchêne (Hibou57)
2012-11-07 1:34 ` Yannick Duchêne (Hibou57)
1 sibling, 0 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-02 12:32 UTC (permalink / raw)
Le Fri, 02 Nov 2012 09:59:53 +0100, Yannick Duchêne (Hibou57)
<yannick_duchene@yahoo.fr> a écrit:
> […]
> package ABC.Sets.Base is
>
> […]
> package ABC.Sets.Functional is
>
Out of the primary topic, but a tiny tip I forgot while posting the
excerpts (as it may be worth to people discovering Ada).
Replace the beginning of `ABC.Sets.Base`, with the following:
package ABC.Sets.Base is
package Formals is
subtype Element_Type is Base.Element_Type;
subtype Cardinal_Type is Base.Cardinal_Type;
end Formals;
Then replace the beginning of `ABC.Sets.Functional` with the following:
package ABC.Sets.Functional is
package Formals is
package Base renames Functional.Base;
end Formals;
That's a useful tip, which will allow you to access the formal parameters
of an instance of a generic package, whenever needed. Otherwise, as an
example, `Base.Cardinal_Type` won't be visible from outside of its
hierarchy, if ever you needed to refer to it this way. Instead of
`Base.Cardinal_Type` you will then do `Base.Formals.Cardinal_Type`, and
will be OK. This question used to be discussed about two years ago on this
Usenet.
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-10-31 19:28 Design by contract and control inversion Yannick Duchêne (Hibou57)
2012-11-01 17:13 ` Yannick Duchêne (Hibou57)
2012-11-01 20:29 ` Adam Beneschan
@ 2012-11-02 16:45 ` Shark8
2012-11-07 1:51 ` Yannick Duchêne (Hibou57)
2 siblings, 1 reply; 9+ messages in thread
From: Shark8 @ 2012-11-02 16:45 UTC (permalink / raw)
On Wednesday, October 31, 2012 1:28:33 PM UTC-6, Hibou57 (Yannick Duchêne) wrote:
> Hi all,
>
> I wondered if there are known idioms to express predicates for callbacks,
> which may be via access to subprogram or interface/tagged types.
Hm, it seems to me that you could have an intermediate/pass-through handler.
(It would be rather limited, unless you make pre-/post-condition parameters... and if you do that it could get cumbersome quick.)
[NOTE: Not compiled or checked, just brainstorming/psudocode.]
Type Obj_Handle is not null access Object'class;
Type Handler is Access Procedure ( A, B : Integer; C : Obj_Handle );
-- this version applies the same preconditions to all handlers.
Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler)
with Inline, pre => A > 0 or B in positive'Range;
Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler) is
begin
D( A, B, C );
end;
To extend these with pre-/post-conditions we could add another type and another parameter to the handler.
Type Condition is access function( P : Parameter ) return Boolean;
Type Conditions is record
Pre, Post : Condition;
end record;
-- This version applies the psudo-pre/post-conditions, as supplied.
Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler; E : Conditions) with inline,
pre => (if condition'(E.Pre) /= Null then E.Pre),
post => (if condition'(E.Post) /= Null then E.Post);
Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler; E : Conditions) is
begin
D( A, B, C );
end;
Or something like it, no?
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-11-02 8:59 ` Yannick Duchêne (Hibou57)
2012-11-02 12:32 ` Yannick Duchêne (Hibou57)
@ 2012-11-07 1:34 ` Yannick Duchêne (Hibou57)
1 sibling, 0 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-07 1:34 UTC (permalink / raw)
Le Fri, 02 Nov 2012 09:59:53 +0100, Yannick Duchêne (Hibou57)
<yannick_duchene@yahoo.fr> a écrit:
> -- abc-sets-base.ads
>
> generic
>
> type Element_Type is private;
> type Cardinal_Type is (<>);
>
> package ABC.Sets.Base is
As it was posted, a note on this.
The `Cardinal_Type` is just a discrete type here, which may looks
over‑generalized to somebodies, and indeed the standard Ada containers use
a range type instead. The discrete type is enough even for a
implementation, but if you wish to add pre/post condition on the
cardinality in the specification, then a discrete type becomes a burden
(unless you enjoy Peano arithmetic a lot). There's no real rule here, that
just depends… Just still one thing: that's always OK to change from the
discrete type to a range type, but not the opposite; so that may be better
to start with the discrete type and change this later if required.
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Design by contract and control inversion
2012-11-02 16:45 ` Shark8
@ 2012-11-07 1:51 ` Yannick Duchêne (Hibou57)
0 siblings, 0 replies; 9+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2012-11-07 1:51 UTC (permalink / raw)
Le Fri, 02 Nov 2012 17:45:33 +0100, Shark8 <onewingedshark@gmail.com> a
écrit:
> On Wednesday, October 31, 2012 1:28:33 PM UTC-6, Hibou57 (Yannick
> Duchêne) wrote:
>> Hi all,
>>
>> I wondered if there are known idioms to express predicates for
>> callbacks,
>> which may be via access to subprogram or interface/tagged types.
>
> Hm, it seems to me that you could have an intermediate/pass-through
> handler.
> (It would be rather limited, unless you make pre-/post-condition
> parameters... and if you do that it could get cumbersome quick.)
> [NOTE: Not compiled or checked, just brainstorming/psudocode.]
>
> Type Obj_Handle is not null access Object'class;
> Type Handler is Access Procedure ( A, B : Integer; C : Obj_Handle );
>
> -- this version applies the same preconditions to all handlers.
> Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler)
> with Inline, pre => A > 0 or B in positive'Range;
>
> Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler) is
> begin
> D( A, B, C );
> end;
>
> To extend these with pre-/post-conditions we could add another type and
> another parameter to the handler.
>
> Type Condition is access function( P : Parameter ) return Boolean;
> Type Conditions is record
> Pre, Post : Condition;
> end record;
>
> -- This version applies the psudo-pre/post-conditions, as supplied.
> Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler; E :
> Conditions) with inline,
> pre => (if condition'(E.Pre) /= Null then E.Pre),
> post => (if condition'(E.Post) /= Null then E.Post);
> Procedure Intermediate (A, B : Integer; C : Obj_Handle; D : Handler; E :
> Conditions) is
> begin
> D( A, B, C );
> end;
>
>
> Or something like it, no?
Yes, that could do the trick. But this is a bit too much for a
specification, and even could not find a place in a specification, as Ada
2012 does not allow procedures in package specs (just functions), unless
I'm wrong with it.
Your idea made me think how again the interface type is the best: just
give the precondition you gave to `Intermediate`, to the handler method of
the interface type.
The only thing which still make me wish I could have something like this
with access to sub‑program, is that access to sub‑program can get an
access to an inner sub‑program (as long as it not to be stored, which is
the case here); you can't do this with an interface type, as a type
defined in a nested scope is not usable (an issue similar to the access
level with named access type).
By the way, I've dropped the initial design which was not safe enough.
Instead of adding preconditions everywhere, I will have an object which
returns two kind of objects, one for read access and one for write access,
and the relevant preconditions to ensure you can't have both read and
write access at the same time, will be only on the two functions returning
these two views. That's safer and simpler. I will just have to resign to
not have any‑more an object exposing the two views (I initially wanted too
much).
--
“Syntactic sugar causes cancer of the semi-colons.” [1]
“Structured Programming supports the law of the excluded muddle.” [1]
[1]: Epigrams on Programming — Alan J. — P. Yale University
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-11-08 5:39 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-31 19:28 Design by contract and control inversion Yannick Duchêne (Hibou57)
2012-11-01 17:13 ` Yannick Duchêne (Hibou57)
2012-11-01 20:29 ` Adam Beneschan
2012-11-02 3:40 ` Yannick Duchêne (Hibou57)
2012-11-02 8:59 ` Yannick Duchêne (Hibou57)
2012-11-02 12:32 ` Yannick Duchêne (Hibou57)
2012-11-07 1:34 ` Yannick Duchêne (Hibou57)
2012-11-02 16:45 ` Shark8
2012-11-07 1:51 ` Yannick Duchêne (Hibou57)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox