[m-users.] Some fun with insts
Julian Fondren
jfondren at minimaltype.com
Wed Jul 31 23:27:33 AEST 2019
Hello,
I don't have a question this time. I just thought this was
interesting:
:- module oop.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module string, list.
:- type class
---> mage
; fighter
; thief
; cleric
; wizard
; warrior
; ninja
; priest.
:- inst underclass for class/0
---> mage
; fighter
; thief
; cleric.
:- inst upperclass for class/0
---> wizard
; warrior
; ninja
; priest.
:- pred promote(class, class).
:- mode promote(in(underclass), out(upperclass)) is det.
:- mode promote(in, out) is semidet.
promote(mage, wizard).
promote(fighter, warrior).
promote(thief, ninja).
promote(cleric, priest).
:- mode promote == ground >> underclass.
:- pred promotable(class::promote) is semidet.
promotable(mage).
promotable(fighter).
promotable(thief).
promotable(cleric).
main(!IO) :-
M = mage,
promote(M, W),
io.format("%s promotes to %s.\n",
[s(string(M)), s(string(W))], !IO),
io.command_line_arguments(Args, !IO),
( if
Args = [ClassStr],
Term = ClassStr ++ ".",
io.read_from_string("args", Term, length(Term),
ok(Class), io.posn(0, 0, 0), _)
then
( if promote(Class, Upper) then
io.format("%s promotes to %s.\n",
[s(string(Class)), s(string(Upper))], !IO)
else
io.format("%s is already a promoted class!\n",
[s(string(Class))], !IO)
),
( if promotable(Class) then
promote(Class, _),
io.write_string("deterministic promote\n", !IO)
else true )
else
io.progname_base("oop", Program, !IO),
io.format(io.stderr_stream, "usage: %s <class>\n",
[s(Program)], !IO),
io.set_exit_status(1, !IO)
).
In particular, what's interesting is how this can fail.
1. what if you claim that a wizard is promotable?
oop.m:041: In clause for `promotable((oop.promote))':
oop.m:041: mode error: argument 1 had the wrong instantiatedness.
oop.m:041: Final instantiatedness of `HeadVar__1' was
oop.m:041: bound(
oop.m:041: cleric
oop.m:041: ;
oop.m:041: fighter
oop.m:041: ;
oop.m:041: thief
oop.m:041: ;
oop.m:041: wizard
oop.m:041: ),
oop.m:041: expected final instantiatedness was
oop.m:041: named inst underclass,
oop.m:041: which expands to
oop.m:041: bound(
oop.m:041: cleric
oop.m:041: ;
oop.m:041: fighter
oop.m:041: ;
oop.m:041: mage
oop.m:041: ;
oop.m:041: thief
oop.m:041: ).
2. what if M (first line of main/2) is a wizard instead of a mage?
oop.m:004: In `main'(di, uo):
oop.m:004: error: determinism declaration not satisfied.
oop.m:004: Declared `det', inferred `semidet'.
oop.m:045: Call to `oop.promote'(in, out) can fail.
3. what if promote(Class, _), which happens in the
promotable(Class) test, is itself a test?
oop.m:065: Warning: the condition of this if-then-else cannot fail.
4. what if the promotable(Class) test is negated?
oop.m:004: error: determinism declaration not satisfied.
oop.m:004: Declared `det', inferred `semidet'.
oop.m:065: Call to `oop.promote'(in, out) can fail.
5. what if instead of promoting mages into wizards, you have
promote(mage, fighter) ?
oop.m:034: In clause for `promote(in((oop.underclass)),
oop.m:034: out((oop.upperclass)))':
oop.m:034: mode error: argument 2 had the wrong instantiatedness.
oop.m:034: Final instantiatedness of `HeadVar__2' was
oop.m:034: unique(
oop.m:034: fighter
oop.m:034: ;
oop.m:034: ninja
oop.m:034: ;
oop.m:034: priest
oop.m:034: ;
oop.m:034: warrior
oop.m:034: ),
oop.m:034: expected final instantiatedness was
oop.m:034: named inst upperclass,
oop.m:034: which expands to
oop.m:034: bound(
oop.m:034: ninja
oop.m:034: ;
oop.m:034: priest
oop.m:034: ;
oop.m:034: warrior
oop.m:034: ;
oop.m:034: wizard
oop.m:034: ).
6. what if you promote a mage into a ninja?
(no error)
but if you add another mode,
:- mode promote(out(underclass), in(upperclass)) is det.
then the mage->ninja promotion starts to fail:
oop.m:030: In `promote'(out($typed_inst(oop.class, (oop.underclass))),
oop.m:030: in($typed_inst(oop.class, (oop.upperclass)))):
oop.m:030: error: determinism declaration not satisfied.
oop.m:030: Declared `det', inferred `nondet'.
oop.m:032: Inside the case ninja/0 of the switch on HeadVar__2:
oop.m:032: disjunction has multiple clauses with solutions.
oop.m:032: The switch on HeadVar__2 does not cover
oop.m:032: `wizard'/0.
6. what if you have exhaustive but completely wrong promotions?
no error again:
promote(mage, ninja).
promote(fighter, priest).
promote(thief, wizard).
promote(cleric, warrior).
oh well. These predicate facts are readable enough that a domain
expert could look at the table and point out any errors without
having to know any programming at all. In practice that might be a
better win for correctness than something like
// ATS, with correct promotions enforced by math
datatype class(int) =
| mage(0) | fighter(1) | thief(2) | cleric(3)
| wizard(4) | warrior(5) | ninja(6) | priest(7)
fn promoteclass{n:int | n < 4}(job: class(n)): [m:int | m == n + 4]
class(m) =
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
A more practical example of this kind of inst stuff might be a
state machine, where you can use insts to ward against invalid
state transitions.
More information about the users
mailing list