I am not sure that it is completely neutral for the language. None of the languages I use have enforce() , and if I were faced with what I did, I would like to use assert and enforce in the sense in which they were intended, which might be idiomatic for that language.
For example, assert in C or C ++ stops a program when it fails, it does not throw an exception, so its use may not be the same as what you are talking about. You do not use assert in C ++ unless you think that either the calling user made the error so serious that they cannot be relied on for cleaning (for example, passing a negative number), or any other code elsewhere made such a serious error that the program should be considered to be in the undefined state (for example, your data structure looks corrupt). However, C ++ distinguishes between runtime errors and logical errors, although this may roughly correspond, but I think that these are mostly preventable and inevitable errors.
In the case of add you should use a logical error if the author intends that a program that provides inconsistent lists has errors and needs to be fixed or thrown at runtime if this is only one of those things that can happen. For example, if your function should process arbitrary generators that do not necessarily have the ability to report their length without waiting for a destructive evaluation of the entire sequence, you most likely consider this an inevitable error condition.
A logical error call implies that it is the responsibility of the caller to check the length before calling add if they cannot provide this by using a pure reason. Thus, they will not be transmitted in the list from the user without explicitly checking the length in the first place, and, frankly, they consider themselves lucky, they even received an exception, and not undefined behavior.
A run-time error call expresses that it is "reasonably" (if abnormally) passed in lists of different lengths, except that this happened on this occasion. Therefore, I consider it rather forced than a statement.
In the case of filesize : for a file to exist, you should, if possible, consider this as a potential possible failure (forced execution), and not an error (statement). The reason is that the subscriber does not have the opportunity to make sure that the file exists - there is always someone with more privileges who can come and delete it, or unmount the entire file system, between checking for existence and calling filesize . Therefore, this is not necessarily a logical flaw in the calling code when it does not exist (although the end user could shoot himself in the leg). Because of this, there are likely to be subscribers who can view this as one of those things that happen, an inevitable error condition. Creating a file descriptor can also fail due to memory, which is another unavoidable error on most systems, although not necessarily recoverable if, for example, the over-commit function is enabled.
Another example to consider is operator[] vs. at() for a C ++ vector. at() throws out_of_range logical error, and not because it is inconceivable that the caller might want to recover, or because you must be kind of numbskull to make an error accessing the array outside the range using at() , and therefore, that the error is completely prevented if the caller wants to see it - you can always check size() before access, if you have no other way to find out if your index is good or not. And therefore, operator[] does not guarantee any checks at all, and in the name of efficiency, access outside the range has undefined behavior.