The SICStus Prolog manual page on mutable terms states that:
[...] the effect of unifying two mutables is undefined.
Then, why does create_mutable(data,x) fail?
Shouldn't that rather raise an uninstantiation_error?
I cannot think of a situation when above case is not an unintentional programming error (X vs x)... please help!
The short answer to "Why does create_mutable/2 not throw an exception when output unification fails?" is just: Because this was how it was done when the feature was added to SICStus Prolog, and no one has made a strong case for changing this.
One important "difference between the stream created by open/4 and the mutable term created by create_mutable/2" is that open/4 has side-effects that are not undone if the output-unification of the call to open/4 fails.
In this sense, create_mutable/2 is somewhat more like is/2 which also just quietly fails if the output argument is some non-numeric non-variable term, e.g. in x is 3+4. This seems to be the common, and traditional, way of handling output arguments in Prolog.
I agree that a non-variable as second argument is most likely a programming error. The next version of the SICStus IDE, SPIDER, will warn for this (as it already does for is/2).
None of this, nor the example in the question, seems directly related to the cited documentation "[...] the effect of unifying two mutables [...]".
Related
While finalizing my upcoming Raku Advent Calendar post on sigils, I decided to double-check my understanding of the type constraints that sigils create. The docs describe sigil type constraints with the table
below:
Based on this table (and my general understanding of how sigils and containers work), I strongly expected this code
my %percent-sigil is List = 1,2;
my #at-sigil is Map = :k<v>;
to throw an error.
Specifically, I expected that is List would attempt to bind the %-sigiled variable to a List, and that this would throw an X::TypeCheck::Binding error – the same error that my %h := 1,2 throws.
But it didn't error. The first line created a List that seemed perfectly ordinary in every way, other than the sigil on its variable. And the second created a seemingly normal Map. Neither of them secretly had Scalar intermediaries, at least as far as I could tell with VAR and similar introspection.
I took a very quick look at the World.nqp source code, and it seems at least plausible that discarding the % type constraint with is List is intended behavior.
So, is this behavior correct/intended? If so, why? And how does that fit in with the type constraints and other guarantees that sigils typically provide?
(I have to admit, seeing an %-sigiled variable that doesn't support Associative indexing kind of shocked me…)
I think this is a grey area, somewhere between DIHWIDT (Docter, It Hurts When I Do This) and an oversight in implementation.
Thing is, you can create your own class and use that in the is trait. Basically, that overrides the type with which the object will be created from the default Hash (for %) and Array (for # sigils). As long as you provide the interface methods, it (currently) works. For example:
class Foo {
method AT-KEY($) { 42 }
}
my %h is Foo;
say %h<a>; # 42
However, if you want to pass such an object as an argument to a sub with a % sigil in the signature, it will fail because the class did not consume the Associatve role:
sub bar(%) { 666 }
say bar(%h);
===SORRY!=== Error while compiling -e
Calling bar(A) will never work with declared signature (%)
I'm not sure why the test for Associative (for the % sigil) and Positional (for #) is not enforced at compile time with the is trait. I would assume it was an oversight, maybe something to be fixed in 6.e.
Quoting the Parameters and arguments section of the S06 specification/speculation document about the related issue of binding arguments to routine parameters:
Array and hash parameters are simply bound "as is". (Conjectural: future versions ... may do static analysis and forbid assignments to array and hash parameters that can be caught by it. This will, however, only happen with the appropriate use declaration to opt in to that language version.)
Sure enough the Rakudo compiler implemented some rudimentary static analysis (in its AOT compilation optimization pass) that normally (but see footnote 3 in this SO answer) insists on binding # routine parameters to values that do the Positional role and % ones to Associatives.
I think this was the case from the first official Raku supporting release of Rakudo, in 2016, but regardless, I'm pretty sure the "appropriate use declaration" is any language version declaration, including none. If your/our druthers are static typing for the win for # and % sigils, and I think they are, then that's presumably very appropriate!
Another source is the IRC logs. A quick search quickly got me nothing.
Hmm. Let's check the blame for the above verbiage so I can find when it was last updated and maybe spot contemporaneous IRC discussion. Oooh.
That is an extraordinary read.
"oversight" isn't the right word.
I don't have time tonight to search the IRC logs to see what led up to that commit, but I daresay it's interesting. The previous text was talking about a PL design I really liked the sound of in terms of immutability, such that code could become increasingly immutable by simply swapping out one kind of scalar container for another. Very nice! But reality is important, and Jonathan switched the verbiage to the implementation reality. The switch toward static typing certainty is welcome, but has it seriously harmed the performance and immutability options? I don't know. Time for me to go to sleep and head off for seasonal family visits. Happy holidays...
I know what a function call is. I know what it does. I've debugged it a lot. I do have some intuitive sense on why it makes sense to use the term "function call", but when it comes down to it, I can't explain this properly like I could with a return statement.
For example, with a return statement the answer is: it's called a "return" because you return to where you came from. You return to the line from where the function was called. So it makes sense why the end of a function (implicitly or explicitly) returns.
I've noticed that for people who speak English as their native language this easier to grasp (especially with more obscure words such as "cache"). However, for people like me (Dutch, learned English through videogames and subtitled television), it's harder to grasp.
I googled for this question, but I get all kinds of entries what a function call is and how it works. I associate the word "call" a lot with telephones, since that's what I use it mainly for in English.
I asked a similar question on what the "de-" means in the word "dereference" here: What does the de- prefix in dereference mean? Is there a linguistic explanation for it?
The term call has a wide meaning. When used in calling a function, you would probably best interpret that as short for calling a function for execution. See the following meaning given by the Free Dictionary:
To order or request to undertake a particular activity or work; summon:
She was called for jury duty. He was called to the priesthood.
This is very close in meaning to another expression used for invoking functions: call upon a function.
That should make sense in the context of functions. The function provides a service, and it is called upon to provide it now. In both cases the meaning is: request the code in a function to be executed.
NB: In Dutch you may translate with oproepen or (less common) aanroepen.
Thanks to #hvj I noticed that the comments of the really related -- though not the same! Since I want to know it linguistically not historically -- question referenced a paper that was behind a paywall ( see http://dl.acm.org/citation.cfm?id=609816&CFID=888670230&CFTOKEN=46456506 ). I read the paper and then I found this small piece:
In this type of routine it is arranged that a sequence of operations is performed each time the subroutine is called into action.
So apparently the Zeitgeist of that time was phrasing it as calling sub-routines into action. Side note: they also talked about returning control from a sub-routine to the main program.
This other question asks almost the same, but not quite. Rather, how do I demand that a goal succeeds deterministically (exactly once) and does not leave behind any choice points?
This is especially useful in the context of Prolog programs that are used as command-line tools: possibly read from standard input, take arguments, and write to standard output. In such a program, leaving a choice point after doing the work is invariably an error of the programmer.
SWI-Prolog offers deterministic/1, so one could write:
( deterministic(true)
-> true
; fail
)
Another, more portable way to achieve the same was suggested:
is_det(Goal, IsDet) :-
setup_call_cleanup(true, Goal, Det=true),
( Det == true
-> IsDet = true
; !,
IsDet = false
).
However, it seems useful to throw an error when this happens, but I don't know what this error would be. I looked quite carefully through the ISO error terms and I could not find an error that would obviously describe this situation.
Is it indeed better to throw an error, or should I just fail? If throwing an error is to be preferred, what would that error be?
EDIT: I am not sure what to because especially when side effects are involved, like writing something to standard output, it feels very wrong to have the side effect happen and then fail. It is almost necessary to rather throw an exception. This makes it also possible to decide that the remaining choice point is harmless (if not desirable) and just catch the exception, then write to standard error or return a different exit code.
But I really have no idea what describes the exception properly, so I don't know what term to throw.
Check out call_semidet/1 as proposed by Ulrich Neumerkel on the SWI-Prolog mailing list:
call_semidet/1 - clean removal of choice-points
In it, he proposes:
call_semidet(Goal) :-
( call_nth(Goal, 2)
-> throw(error(mode_error(semidet,Goal),_))
; once(Goal)
).
This proposed mode_error is a very good start.
In spirit, it follows the other errors: In this case, mode semidet is expected, and this is reflected in the thrown error term.
I'm working on some lisp code that munges a CVS repository in order to get it into shape for a git conversion. As such, when something goes wrong it might not be a bug in my code but might instead be an inaccuracy in the input data which needs fixing.
Deep down in the bowels of my code, I'll have (say) some function that merges two date ranges by taking an intersection. If that intersection turns out to be empty, I'll raise an error. This is all good, but my "merge dates" function is missing lots of the information that the user (me!) needs in order to figure out what went wrong. For example, which CVS master file ("foo,v") was I working on? What branch was I thinking about? Etc. etc.
My partial solution to this problem is to handle and re-raise the error. For example, this sort of code:
(handler-case
(do-something)
(unclear-graft-point (c)
(setf (slot-value c 'master) master)
(setf (slot-value c 'branch) branch)
(error c)))
which sets some extra slots with useful info before re-raising the error.
The report function for the condition then checks whether those slots have been set and, if so, will use them to give a more helpful error message.
Unfortunately, the backtrace that I get stops at the top-level re-raise. That makes sense: it's the code I wrote. But it's not really what I want...
Is there a way in Common Lisp to "annotate" a condition as it hurtles past, without re-raising it and, hence, partially unwinding the stack without reporting that in the back trace?
I gave a reasonable amount of background info about what I'm doing in the hope that, if this isn't possible, someone will say "Ah, what you should do in this situation is...": am I just going about this the wrong way?
Yes, there is a Common Lisp technology applicapble to your case - it is called HANDLER-BIND. You can find a detailed explanation and use cases for it alongside other condition-handling machinery in Peter Seibel's Practical Common Lisp Chapter 19. Conditions and Restarts.
I'm having a hard time choosing whether I should "enforce" a condition or "assert" a condition in D. (This is language-neutral, though.)
Theoretically, I know that you use assertions to find bugs, and you enforce other conditions in order to check for atypical conditions. E.g. you might say assert(count >= 0) for an argument to your method, because that indicates that there's a bug with the caller, and that you would say enforce(isNetworkConnected), because that's not a bug, it's just something that you're assuming that could very well not be true in a legitimate situation beyond your control.
Furthermore, assertions can be removed from code as an optimization, with no side effects, but enforcements cannot be removed because they must always execute their condition code. Hence if I'm implementing a lazy-filled container that fills itself on the first access to any of its methods, I say enforce(!empty()) instead of assert(!empty()), because the check for empty() must always occur, since it lazily executes code inside.
So I think I know that they're supposed to mean. But theory is easier than practice, and I'm having a hard time actually applying the concepts.
Consider the following:
I'm making a range (similar to an iterator) that iterates over two other ranges, and adds the results. (For functional programmers: I'm aware that I can use map!("a + b") instead, but I'm ignoring that for now, since it doesn't illustrate the question.) So I have code that looks like this in pseudocode:
void add(Range range1, Range range2)
{
Range result;
while (!range1.empty)
{
assert(!range2.empty); //Should this be an assertion or enforcement?
result += range1.front + range2.front;
range1.popFront();
range2.popFront();
}
}
Should that be an assertion or an enforcement? (Is it the caller's fault that the ranges don't empty at the same time? It might not have control of where the range came from -- it could've come from a user -- but then again, it still looks like a bug, doesn't it?)
Or here's another pseudocode example:
uint getFileSize(string path)
{
HANDLE hFile = CreateFile(path, ...);
assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement?
return GetFileSize(hFile); //and close the handle, obviously
}
...
Should this be an assertion or an enforcement? The path might come from a user -- so it might not be a bug -- but it's still a precondition of this method that the path should be valid. Do I assert or enforce?
Thanks!
I'm not sure it is entirely language-neutral. No language that I use has enforce(), and if I encountered one that did then I would want to use assert and enforce in the ways they were intended, which might be idiomatic to that language.
For instance assert in C or C++ stops the program when it fails, it doesn't throw an exception, so its usage may not be the same as what you're talking about. You don't use assert in C++ unless you think that either the caller has already made an error so grave that they can't be relied on to clean up (e.g. passing in a negative count), or else some other code elsewhere has made an error so grave that the program should be considered to be in an undefined state (e.g. your data structure appears corrupt). C++ does distinguish between runtime errors and logic errors, though, which may roughly correspond but I think are mostly about avoidable vs. unavoidable errors.
In the case of add you'd use a logic error if the author's intent is that a program which provides mismatched lists has bugs and needs fixing, or a runtime exception if it's just one of those things that might happen. For instance if your function were to handle arbitrary generators, that don't necessarily have a means of reporting their length short of destructively evaluating the whole sequence, you'd be more likely consider it an unavoidable error condition.
Calling it a logic error implies that it's the caller's responsibility to check the length before calling add, if they can't ensure it by the exercise of pure reason. So they would not be passing in a list from a user without explicitly checking the length first, and in all honesty should count themselves lucky they even got an exception rather than undefined behavior.
Calling it a runtime error expresses that it's "reasonable" (if abnormal) to pass in lists of different lengths, with the exception indicating that it happened on this occasion. Hence I think an enforcement rather than an assertion.
In the case of filesize: for the existence of a file, you should if possible treat that as a potentially recoverable failure (enforcement), not a bug (assertion). The reason is simply that there is no way for the caller to be certain that a file exists - there's always someone with more privileges who can come along and remove it, or unmount the entire fielsystem, in between a check for existence and a call to filesize. It's therefore not necessarily a logical flaw in the calling code when it doesn't exist (although the end-user might have shot themselves in the foot). Because of that fact it's likely there will be callers who can treat it as just one of those things that happens, an unavoidable error condition. Creating a file handle could also fail for out-of-memory, which is another unavoidable error on most systems, although not necessarily a recoverable one if for example over-committing is enabled.
Another example to consider is operator[] vs. at() for C++'s vector. at() throws out_of_range, a logic error, not because it's inconceivable that a caller might want to recover, or because you have to be some kind of numbskull to make the mistake of accessing an array out of range using at(), but because the error is entirely avoidable if the caller wants it to be - you can always check the size() before access if you have no other way of knowing whether your index is good or not. And so operator[] doesn't guarantee any checks at all, and in the name of efficiency an out of range access has undefined behavior.
assert should be considered a "run-time checked comment" indicating an assumption that the programmer makes at that moment. The assert is part of the function implementation. A failed assert should always be considered a bug at the point where the wrong assumption is made, so at the code location of the assert. To fix the bug, use a proper means to avoid the situation.
The proper means to avoid bad function inputs are contracts, so the example function should have a input contract that checks that range2 is at least as long as range1. The assertion inside the implementation could then still remain in place. Especially in longer more complex implementations, such an assert may inprove understandability.
An enforce is a lazy approach to throwing runtime exceptions. It is nice for quick-and-dirty code because it is better to have a check in there rather then silently ignoring the possibility of a bad condition. For production code, it should be replaced by a proper mechanism that throws a more meaningful exception.
I believe you have partly answered your question yourself. Assertions are bound to break the flow. If your assertion is wrong, you will not agree to continue with anything. If you enforce something you are making a decision to allow something to happen based on the situation. If you find that the conditions are not met, you can enforce that the entry to a particular section is denied.