Monday, September 2, 2013

Minimum knowledge for saving faith

Today I was disappointed by a statement of John MacArthur. On his Grace to You radio program a question was asked about what knowledge was necessary for salvation. The asker had debated with a friend who believed that even today people could be saved like Abraham by a relatively uninformed faith while he maintained that more extensive knowledge was also necessary (e.g., in light of Romans 10:14-15--how can they call on God unless preachers are sent?).

John MacArthur initially stated that the asker was correct, correctly pointing out that knowing God as creator was certainly insufficient (that knowledge of God's holiness, of one's falling short, and of God's providing a way of reconciliation was necessary) and that God was certainly powerful enough and providential enough to bring the good news to whomever he has chosen. Yet he later added that it was possible that God could bring saving faith to a person without natural transmission of the gospel.

While I recognize that he was answering spontaneously (and his knowledge and reason is impressive in this context), I would wish that he had rather said something like:

You are mistaken in fact, but your friend may be mistaken in spirit. Saving faith does not require specific information about the mechanism but only a proper awareness of the need for salvation--that God is holy and one is a sinner--and that God is able and willing to save.

However, anyone who has been made alive by the Spirit will long to look into such things and will rejoice in the truth of the gospel as it is revealed.

The question is like asking if a few scraps falling from the table at a great feast is sufficient to sustain life. While such dirty scraps may be sufficient, it is madness to suppose that one would be content to allow just the aroma of the feast to stir hunger and bring people toward the table and to allow such people to subsist on scraps. Like the aroma of a feast, the Spirit may directly make people aware of the need and draw people to salvation, and perhaps some such may not hear the gospel in this life--subsisting on dirty scraps.

However, focusing on what is barely sufficient denies the spirit of the feast--grateful joy and abundance--and presents the danger of coming to believe that merely eating dirt is sufficient for life.

Given that I am not really satisfied with the above suggested alternative (e.g., there is no mention of the necessary reaction to the minimum knowledge) despite having more time to work on it, it should not be surprising that even John MacArthur would not always provide a spontaneous and fully satisfactory answer to such a question. One might argue that the spontaneous nature of the responses was unnecessary, but this form of question and answer may have been helpful for the original askers (and replaying such might best exploit his limited time) and the form itself may remind listeners of the importance of being prepared in season and out of season.

While it is good not to be satisfied with anything less than perfection, it is also necessary to rejoice in every grace and trust in God's providence--that even imperfections are compelled to declare the glory of God.

Saturday, July 14, 2012

Balance vs. fullness [a bit of a rant]

The popularity of using balance as a goal bothers me. Among the pitfalls of using balance as a goal are not recognizing common benefits (and along with this an antagonistic viewpoint where any benefit toward one aspect tends to be viewed as a detriment toward others--"balance" even seems to imply a dualistic perspective) and attempting to establish a single metric which can be applied equitably to all interests.

One problem with a single metric is that it becomes very tempting to choose one that is relatively obvious and easy to measure and apply it is a simple manner. E.g., a balanced taxation proposal might see individual income as an easily measured quantity and apply a flat (equitable by income) tax. However, even a flat tax based on discretionary income might be inappropriate. (Interestingly, a flat tax on assets would seem to have some attractive properties. Such would encourage the use of assets to increase productivity. Unfortunately, assets are more difficult to measure than income [education, aptitude, health, etc. are assets which influence potential productivity] and not all productivity [social good] generates income with which to pay a tax.)

(It is tempting to see this issue as analogous to the issue of the nature of Christ, where the "balance" perspective proposes that Christ's nature is part-human and part-Divine where the "fullness" perspective proposes that his nature is fully human and fully Divine. Such an analogy might be improperly biased in favor of my own perspective rather than seeking an understanding of truth.)

The use of fullness (with the concept of perfection or perhaps complete integrity) as a goal may avoid some psychological/moral issues, but it seems to draw out significant measurement issues (which has the good aspect of forcing thought and recognition of complexity but the bad aspect of potentially disintegrating into a contemplation of [or argument about] measurement rather than adopting a more integrated perspective which recognizes that while establishing measurements helps to clarify goals [at many levels] and estimate progress [and so guide resource allocation, including time], establishing measurement is a servant of other aspects [while the other aspects likewise submit to measurements]).

When using perfection (fullness/integrity) as the goal, one must also maintain a sense of context. A human goal of perfection takes into account finitude. A result that is perfect at a given time may easily be a very poor result if the achievement of that result is necessarily delayed by resource limits. Likewise a dependence on grace seems to be necessary (this may be related to Martin Luther's "sin boldly"). Knowing that even stupid failures (and many failures are 'recognized' as stupid in hindsight) are not damning provides a freedom to strive for perfection rather than being paralyzed by uncertainty (the measurement problem)--or even the certainty of failure (the recognition of inadequacy)--or settling for a safe result (like burying the talent [Matthew 25:24-30]).

In Mere Christianity C.S. Lewis wrote "The only fatal thing is to sit down content with anything less than perfection." This is not a paralyzing perfectionism (which is a significant issue for me), but a call not to stop being a soldier until the war is won. Such a seeking of perfection not only motivates a full commitment of effort (sometimes enabling success) but sometimes produces an accidental success beyond expectation.

(My own perfectionism--both from uncertainty of what should be done and from perception of inadequacy--very often leads to inactivity, which is very far from the striving for perfection that is the human calling. On the positive side, a more proper fondness for perfection may be involved in my perfectionism--i.e., my perfectionism may in part represent a corruption of a particular gift of affection for the highest and best.)

Of course, the very use of "vs." in the title demonstrates how easy it is to fall into an antagonistic (rather than holistic) perspective.

Wednesday, April 18, 2012

Nitpicking on "No Silver Bullets", part 1, HLL disadvantages

A recent post on John Cook's blog mentioned a presentation by Mike Swaim (titled "No Silver Bullet") about the advantages and disadvantages of certain programming techniques. Looking through the PDF, a few points bothered me. (Yes, nitpicking over bullet points is not exactly fair, and I am not at all claiming that the presenter was not aware of the issues.)

The disadvantages of high level languages were basically listed as reduced performance, "inappropriate runtime requirements" (which might be excessive resource requirements or things like garbage collection pauses in a tightly constrained hard real-time environment), and (based on the NT example) the inability to fix or even work around errors in architecture/design or implementation.

Performance issues

It seems that the performance problem comes from two issues. First, the compiler (and runtime system) does not fully exploit the available information. This comes from two sources: the lack of machine resources allocated to the compiler and the lack of programming effort applied to developing systems for translating higher level specifications to machine language code. While this can be seen as approaching artificial intelligence requirements, the difficulty of the goal is not as great as for generic artificial intelligence.

Some strengths of existing computing systems could be more fully exploited, most particularly the relative abundance of idle time on many computing systems and the relative reliability of detailed long-term memory. Offloading some analysis effort of the compiler to development time could provide more extensive analysis without increasing compilation time. Caching information about software could also substantially reduce the amount of analysis required at compilation time (or run time).

The software effort problem is unnecessarily hindered by issues with the definitions of behavior for programming languages (which can include behavior expected by existing software)

The second issue is the lack of communicating necessary information to the compiler (and runtime system). Information which is known (or assumed) by a human programmer is often not communicated to the compiler, and frequently the compiler is not allowed to ask questions for clarification. In some cases, the programming language provides no mechanism for communicating such information and seemingly more often makes communicating such information more difficult by the use of inappropriate default semantics (e.g., C pointer/array aliasing), discordant syntax for adding information (e.g., using __builtin_expect versus something like assuming (condition) {stuff to do} else {other stuff to do} or case 5 probability=20%: stuff to do; requiring the use of a different dialect or even language to express information adds an unnecessary barrier), and inability to use layered abstraction (e.g., providing a generic implementation of an operation with an optimized implementation that can "overload" the generic implementation with constraints [additional information] expressed so that the optimized implementation can be validated; furthermore optimized implementations could be provided as hints--"compiler, try this implementation"--or directives--"compiler, do it this way" and multiple implementations could be provided for different constraints--and the compiler might even use different implementations to generate a new implementation).

This second issue is compounded by the relatively weak support for profiling. Not only is the generation and use of profile information more complex than strictly necessary, but the overheads for generating such information seem to be greater than necessary. In addition, some information is generally not gathered by typical profiling tools. E.g., the temporal distribution of values for a variable and even the temporal correlation of values for different variables can be as important as the net frequency of values. Likewise, infrequent and limited use of whole program analysis prevents the compiler from using even relatively simple transformations in data structures and algorithms.

(Compiler writers not understanding computer architecture or being informed about specific microarchitectures--not necessarily by choice--does hinder the development of better compilers.)

While providing additional information would in many cases reduce the productivity advantages of a higher level language, in many such cases, the use of "overloading" implementations might provide good performance without losing the maintainance and extension advantages of the higher level representation. When a constraint of an optimized implementation is violated or the optimized version no longer matches the generic version, these effects can be noted allowing a decision to accept the optimization loss or rework the optimized implementation. In some cases, the reworking could be fully or partially--i.e., suggested fixes provided--automated. In some cases, multiple optimized implementations could be used to synthesize new implementations to better fit resource availability or optimization goals.

As an example, in many cases it should be possible for a compiler to recognize that the keys of a hash are immutable or restricted to a particular set of values or are accessed in a particular order, allowing a simple programming concept to be implemented in an optimized fashion. Providing directive or expectation information may allow the use of a higher level construct like a generic container while allowing the compiler to use an optimized implementation--preventing or generating a fatal error in the case of directives, dynamically recompiling or using an alternative implementation in the case of expectations. In theory, a hash which uses only unsigned integer keys could be implemented as an array. One might even generalize the hash concept to provide a syntax like container_of_records[element=variable] to index the element (or a collection of elements) where the record member 'element' equal the value in 'variable') with the ability to imply 'element=' based on the type information of 'variable' or the use of a default element (perhaps indicated by a qualifier in the definition like 'indexer' or by some precedence mechanism--though such could make bugs too easy to generate and too difficult to find) or a combination (where multiple indexers are provided and type information and/or precedence is used to determine which is used).

(Expectation information can also be used to reduce compilation effort. If the expectation is correct, the only overhead is in evaluating the expectation--compared to searching through all possibilities. If the expectation is incorrect, it may be acceptable to use less aggressive optimization and provide a notification or even delay final compilation of the component dependent on the expectation until a new expectation is provided by the programmer or possibly by some automated method, potentially taking advantage of lazy evaluation of the program source code. Obviously, the communication of the expectation should be lightweight, perhaps something like Some_record_type container[uint?] to define a container of 'Some_record_type' values that is expected to be indexed by unsigned integer values.)

Runtime Requirements

Runtime requirements issues would seem to have similar solutions as the performance issues. While it would be unreasonable to expect existence of even the limited form of artificial intelligence necessary to fully solve this issue, a more moderate degree of intelligence (or extensive searching) would seem likely to greatly reduce the problems. In particular, it is disappointing to me that automated memory management is often exclusively implemented as generic garbage collection. I suspect that in many cases the source code provides sufficient information to determine when a resource can be freed or when reference counting would be a better choice (with or without a saturating reference count--where a saturated reference count might indicate the use of a generic garbage collection mechanism); likewise, choices of allocators (region/stack/FILO/FIFO, binary buddy, fixed-sized arrays, limited size diversity exploiting expected frequencies and best fit with limited splitting and fusing of default partitioning, object caching, etc.) could be optimized by a compiler (or perhaps even a runtime system).

In addition, a high level language should support the ability to use lower-level directives (as well as communicate expectations) without degenerating into bare assembly language. (If assembly language is used, it should be annotated with constraints--obviously at minimum the assumed ISA--and a higher level implementation should be provided, which ideally could be used to validate the assembly language implementation.) Even if the lower-level directive cannot be validated against a higher-level representation or other information in the source code (or specification)--e.g., one might use a bald assertion which is not checked at runtime and need not be validated at compile time (though notification would be provided if compile-time validation was not possible), information would be available in the source code about the assumptions made and automated tools could use such assumptions to provide assistance in debugging. In some cases, debugging aids could be selectively applied (to reduce overheads). In the case of memory deallocation, in some cases it might be possible use virtual memory with page deduplication or compression to reduce the cost of not reusing deallocated memory by overwriting the
deallocated region with a given pattern.

Unfixable Environments

While the problems of bugs in the design or implementation of a runtime environment cannot be completely avoided (though using formal methods would limit such bugs to the design--and the development tools), using a modular design even in the runtime environment would seem likely to substantially reduce the impact of many design and implementation bugs/misfeatures. Allowing individual components to be swapped out would seem to greatly facilitate working around implementation and even design issues. Even layering a foreign interface over a required system interface might be practical in some circumstances. E.g., it might be possible to provide a specialized automated memory managment system over a generic garbage collector with modest overheads by using a minimum number of allocations using the system memory manager. Ideally, the functionality of such module implementations could be validated according to a higher level specification.

Unfortunately, not only are the inadequacies of tools a barrier to achieving such an ideal, but human factors are likely to hinder adoption. Humans are often disinclined to spend now to save later, often have difficulty admitting mistakes, often have difficulty throwing away a work (perhaps especially if it was well done and only recently became unfit for current or expected use), and often have difficulty recognizing the validity in another perspective (especially when that perspective is poorly communicated or is bundled with aspects that are invalid [Absolute tolerance of falsehood or wrongs is not a virtue, but often saying "you really shouldn't do that" is more appropriate than excommunication. {In the context of programming language design, making something awkward to do is often more appropriate than prohibition--with bonus points if repentance and reform is made easy.} The love of truth should be accompanied by humility and mercy.]).