Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What are Individuals and Lindies all about? #2

Closed
orcmid opened this issue Jan 1, 2018 · 25 comments
Closed

What are Individuals and Lindies all about? #2

orcmid opened this issue Jan 1, 2018 · 25 comments
Labels
discussion Project discussions question

Comments

@orcmid
Copy link
Owner

orcmid commented Jan 1, 2018

This Question is Closed

After experimental confirmations carried out with Roman Susi (@rnd0101), the treatment of Individuals and Lindies has stabilized.


In obtheory.txt, all obs, x, such that ob.a(x) = ob.b(x) = x are classed as individuals. ob.NIL is established as such an ob, and it is indicated that there can be any number of additional distinct ones. At the theory, these are distinguished by difference in the lexical forms of the names given to them.

In obaptheory.txt, as part of introducing universal functions obap.ap(p,x) and obap.ev(exp), a number of additional individuals are introduced. I.e., individuals such as obap.A, obap.C, obap.SELF, and obap.EV are identified as distinct primitives along with ob.NIL. In the reference notation for expression of obs in plaintext, these are designated as .A, .C, .SELF, .EV, and .NIL, respectively.

Also, in obaptheory.txt, we learn that there are lindies (short for literal individuals) that are distinct from primitives and symbolized in the theory by notation Ʃs where s is a distinct alphanumeric spelling taken as the identifier of the lindy. In the reference notation, that lindy is simply written s.

Then, in the formulation of the universal function obap.ap in the theory language, Ot, there are some elaborate predicates concerning lindies.

@orcmid orcmid added the question label Jan 1, 2018
@orcmid orcmid changed the title What What are Individuals and Lindies all about? Jan 1, 2018
@orcmid
Copy link
Owner Author

orcmid commented Jan 1, 2018

I created this #2 by mistake somehow, while creating #3. So I've repaired the title and will come back to it momentarily.

@orcmid
Copy link
Owner Author

orcmid commented Jan 1, 2018

First, About Notation

To facilitate distinction of constants for obs (i.e., fixed individuals in the domain of discourse), I need a way to distinguish, in plaintext, between variables in expressions of the logical theory, Ot, and such constants. The primitive individuals that are distinguished in the formulation of the universal function have names such as obap.ARG. These primitives are designated in oFrugal expressions for obs by contraction to .ARG, for example.

In homage to ALGOL 60, when there is more typographical freedom for reference notation, bold-face could be used, and I prefer ARG (or arg) in that particular case.

The primitives are fixed and distinguishable.

The lindies are more like the atoms of LISP. They are distinguished by their "print names." In obaptheory.txt, Ʃs is the form. In reference notation for obs, we just use the s, since there are no Ot theory variables to be confused with.

There is a grammar for the lexical form of s, but for now just assume alphanumeric string beginning with a letter.

Lindies are completely distinct from the primitives, and that is reflected in the notation.

In addition, primitives have specific and distinct interpretations with respect to obap.ap and obap.eval.

Lindies, on the other hand, although distinguished, have no individual significance with respect to the universal function. Their notional significance is as themselves -- literals. They can be freely chosen for whatever purpose an user desires.

Since every ob has an interpretation as a script, p, in obap.ap(p,x) and obap.eval(p), what is to be made of lindies then?

@rnd0101
Copy link

rnd0101 commented Jan 3, 2018

"Lindies are completely distinct from the primitives". Why lindies are needed? What is the reason Occam's Razor spared them so far? Why they need to be different from user-defined individuals? For example, even atoms (in Erlang or Prolog, not quite sure about LISP) are interchangeable to some degree with strings/lists, and in other languages string constants are enough for what atoms do.

@orcmid
Copy link
Owner Author

orcmid commented Jan 6, 2018

"Lindies are completely distinct from the primitives". Why lindies are needed? What is the reason Occam's Razor spared them so far? Why they need to be different from user-defined individuals? For example, even atoms (in Erlang or Prolog, not quite sure about LISP) are interchangeable to some degree with strings/lists, and in other languages string constants are enough for what atoms do.

Some level-setting:

  1. The primitives are not user-defined in the case of obaptheory. In some sense they are reserved and dedicated to particular purposes. They might be used arbitrarily too, in ways that don't encounter the "built-in" use that is made of them with obap.ap and obap.eval.
  2. The lindies are available for arbitrary use. They don't escape having applicative interpretations, as will be pointed out, but they are nonetheless arbitrary, all having the same treatment in obap.ap and obap.eval.
  3. There are no string data available in oMiser at this point. However strings are introduced, it will likely not be as lindies, even though the "print name" of a lindy will likely be accessible as a restricted-format string when that happens. The restricted-format part is challenging with regard to being able map a string to a lindy having that string as its identifier. It is not necessary to resolve that at the moment.

My hope is that the next response on this question will help to clarify the intended arrangement.


PS: I had wanted to avoid reflection where lindy-names can be extracted as strings and strings can be used to name lindies. Likewise, I wanted to avoid allowing definable individuals (to be introduced in a later extension) revealing their definitions. However, I can't do without that for oFrugal to operate properly. Reflection is also important in demonstrating scripts that provide simulators of obap.ap and obap.eval. That works best if obs interpreted in the simulations are the same ones having the direct obap.ap and obap.eval interpretations. I.e., obap.ap(obap.ap(apsim, p),x) = obap.ap(p, x) and apsim is non-trivial.

@orcmid
Copy link
Owner Author

orcmid commented Jan 6, 2018

Second: About Applicative Interpretation

updated 2018-01-11: Make important correction in the rule for applicative interpretation of lindies.
updated 2018-01-10: Adjusted the initial introduction of traces to leave open the special case of pure-lindy traces applied to pure-lindy-traces.
updated 2018-01-09: Being a bit more rigorous, consistent, and careful with nomenclature.

For oMiser, every ob has an applicative interpretation. So obap.eval(exp) determines at most one definite ob for each given canonical ob as exp. Similarly, obap.ap(p,x) determines at most one definite ob for each given p and x combination of canonical obs. This wording acknowledges that some applicative evaluations might not produce any result, even when exp, p, and x are so definite.

The term applicative interpretation applies to how p is interpreted via obap.ap(p, x). The term applicative expression/evaluation applies to how exp is interpreted as an expression of applicative operations (that is, obap(p,x) cases) in accordance with obap.eval(exp).

The General Applicative Scheme

For obap.eval(exp), if exp = e1 :: e2, the ordinary eval of exp is as obap.ap(eval(e1),eval(e2)). In other words, this e1 :: e2 evaluation is as the application of the evaluation of e1 to the evaluation of e2.

For obap.ap(p,x), when p is a singleton, the applicative interpretation of p determines what function of x is provided. Every singleton p has a definite applicative interpretation.

When p is not a singleton in obap.ap(p,x), the applicative interpretation of p is ordinarily determined by the evaluation of obap.ev(p,x,p). That is like obap.eval(p) except that the obs supplied as p and x are available in the evaluation as operands supplied to the applicative evaluation. The extra-ordinary case is when interpretation of p is defined to be a trace for which further evaluation is not attempted. This will be addressed in the Third part of the response to this question.

Specific Applicative Interpretations

The course of applicative interpretation typically arrives at operations where p in obap.ap(p,x) is a singleton. There is a definite applicative interpretation for every singleton case. Similarly, there is a definite evaluation case when exp is a singleton in obap.ev(p, x, exp). There are also some special cases when s is a singleton and evaluation of a script expression has form s::more in obap.ev(p,x,"s*::more).

Below, obap.ap is abbreviated ap and we use simply ev(exp) when the p,x are understood (or unimportant).

Elementary-Primitive Applicative Interpretations

Four primitive individuals evaluate as themselves and have straightforward applicative interpretations.

op ap(op,x) ev(op) ev( op :: xe)
ob.NIL x op ap(op, ev(xe) )
obap.A ob.a(x) op ap(op, ev(xe) )
obap.B ob.b(x) op ap(op, ev(xe) )
obap.E ob.e(x) op ap(op, ev(xe) )

Enclosure Applicative Interpretation

Enclosures evaluate as the enclosed ob. The applicative interpretation of an enclosure is also the enclosed ob.

op ap(op,x) ev(op) ev( op :: xe)
v v v ap(v, ev(xe) )

Unary Use of Binary Operators

Two other primitive individuals evaluate as themselves and signify special operators in evaluations where they precede a pair. They have special applicative interpretations when preceding a singleton in an evaluation.

op ap(op,x) ev(op) ev( op :: xs) ev( op :: e1 :: e2)
obap.C op :: ‵(x) :: obap.ARG op ap(op, ev(xs) ) ob.c(ev(e1), ev(e2) )
obap.D op :: ‵(x) :: obap.ARG op ap(op, ev(xs) ) obap.d(ev(e1), ev(e2) )

where the result of obap.d(x,y) is obap.A if x = y and obap.B otherwise. We'll account for the unusual applicative interpretation of obap.C and obap.D after accounting for obap.SELF and obap.ARG below.

Special-Form Expression Operators

The three remaining primitives serve as operators that have special significance in the evaluation of the expression/script forms of obs.

op ap(op,x) obap.ev(p, x, op) obap.ev(p,x, op :: exp)
ob.SELF ‵(ob) :: ‵(x) p ap(p, ev(exp) )
obap.ARG ‵(ob) :: ‵(x) x ap(x, ev(exp) )
obap.EV ‵(ob) :: ‵(x) op obap.ev(p,x, obap.ev(p,x, exp) )

The interpretation of these individuals is specific to their occurrence in an ob that is being evaluated as an applicative expression. There is no comparable applicative interpretation because the evaluations are entirely dependent on p and x in a current evaluation.

There must be an applicative interpretation and in this case that requirement is satisfied by producing a trace of the attempted applicative operation. For example, ap(ob.SELF, x) determines the result ‵ob.SELF :: ‵(x).

These forms are termed "traces" since applicative interpretation goes no farther.

A trace is such that ap(trace, non-trace) = ev(trace) = trace.

Given how obap.ARG is interpreted in an applicative expression, it should be understandable how applicative expression ( ‵obap.C :: xs) ) :: ys has the same evaluation as (obap.C :: (obap.E :: xs) :: ys). Were there λ-expressions at this level, this result would be tantamount to λy.ob.c(x,y).

Lindy Applicative Interpretation

Lindies are freely-chosen individuals with no individual interpretations other than as themselves. The requirement for applicative interpretations is satisfied by creation of traces. There is no basis for anything further.

op ap(op,x) obap.ev(p, x, op)
Ʃs op :: x op when x is an ob with only lindy individuals singletons
Ʃs op :: ‵(x) op for x otherwise

The production of a trace as a result may be indication of a mistake in the creation of an ob for use as an applicative expression. And, because the production of traces is a well-defined situation, there is prospective intentional use. This, and some trace-intending/-extending measures, deserve separate attention.

@rnd0101
Copy link

rnd0101 commented Jan 6, 2018

Great description! (BTW, have I missed some article with this information somewhere is the above description new one? Of course, all that is in the theory itself, but this is more human-readable).

For obap.eval(exp), if exp = e1 :: e2, the ordinary eval of exp is as obap.ap(eval(e1),eval(e2)). In other words, this e1 :: e2 evaluation is as the application of the evaluation of e1 to the evaluation of e2.

Aha! Here I see the key for lazy evaluation. Basically, the theory does not specify two evals (in obap.ap(eval(e1),eval(e2))) to be evaluated sequentially. The only important thing here is that it's ready when obap.ap is applied. (and of course it may be it is understood as equation altogether, so question of timing disappears, but then run-time environment must be much cleverer)

Maybe, you have thought of possible laziness differently, for example, coding simulation of evaluation / application functions, but do you think laziness can have a hook already at destructing pair? That is, the second eval does not happen immediately? In other words, there is a possibility eval(e1) has a say over the second eval? For example, it may totally ignore and produce a constant, or it may destruct e2 one step and decide whether to evaluate first or second member of e2_1 :: e2_2 ?

Maybe, I am missing some simpler mechanism you assumed to enable laziness (thus making oMiser layer more expressive). One way, which came to my mind, is that obap.ap(eval(e1), _) can be intermediate partial, say, obap.partial_ap(evaluated, e2), which will then decide what to evaluate from e2, if at all? This may require a new section of obap theory, and I am not sure it's justified in your eyes, but it may provide expressivity boost by enabling hooks into eval. I even think it can be very natural to applicative programming nature oMiser represents.

My intuition is that enclosures are better suited for laziness, but I can't see how specifically it can be helping lazy IF-THEN-ELSE, for example... I need to play with some run-time implementation to get more insights though.

@orcmid
Copy link
Owner Author

orcmid commented Jan 6, 2018

Aha! Here I see the key for lazy evaluation. Basically, the theory does not specify two evals (in obap.ap(eval(e1),eval(e2))) to be evaluated sequentially.

Quick Note.

  • oMiser is intended to be greedy. Put simply, all operands must have definite values before evaluation proceeds further. There are reasons for this and I am tempted to introduce a notation for it in obaptheory, so there is clarity about definiteness at the theory level. Have you seen anything like that?
  • My understanding is that this and the finiteness (and assured finite decomposability -- structural induction (?)) of obs is important for confidence around computability, although the halting problem cannot be avoided of course.
  • From an engineering perspective, this definiteness also means that a failure of any part is a failure of the whole. That does not mean there can't be parallelism. oMiser is side-effect free.
  • So laziness is ruled out. oMiser is definitely imperative, operationally. That does not mean there cannot be lazy approaches. It just happens at a higher level (e.g., using continuations, generators, and such). That strikes me as something interesting to explore, but without having to embrace it at the oMiser level.

@rnd0101
Copy link

rnd0101 commented Jan 6, 2018

My understanding is that this and the finiteness (and assured finite decomposability -- structural induction (?)) of obs is important for confidence around computability

Ok... The closest analogy I can see here is type safety. (eg, used in MLs).

In (some? many?) FP languages type checking and interpretation are separated. I do not follow latest developments, but I guess type-checking must be eager, and interpretation can be whatever. ML family perhaps went to extreme with type-safety, some languages are not type-safe at all (C, C++), so the spectrum is wide.

After your explanation above I understand that oMiser does both at the same time. Even though there are no types, it's just inevitable to check the constructions. I'd even said, that many of those conditions in the theory really are camouflaged type checks, it's that types are encoded structurally instead of being "in parallel".

Unfortunately, I've never worked with Hindley-Milner algorithm, but I suspect if oMiser's tree is given to it, it will be capable to deduce types!

@rnd0101
Copy link

rnd0101 commented Jan 6, 2018

In the context of this question, I tried to figure out the ontology of miser, ob and obap.

I got that idea in the morning, that theory's Obs and lindies remind me... semantic web approach.

So I thought of application, where obs are RDF resources, that is URIs (or IRIs), and lindies are data attributes of type string. Primitives are just special resources of the ob / obap ontologies (they can even have almost same abbreviations as in the theory, only using colon instead of dot obap:B (and of course the prefix needs to be associated with the ontology, but this is how SemWeb works, nothing special).

Then all those numerous is-functions I turned into classes in RDF understanding, where there is this Open World assumption and any resource can have any classes unless explicitly disjoint.

I have not yet experimented, but RDF graph, based on the ontology, can be an alternative language for oMiser programs. And any decent OWL reasoner will be preliminary "type-checker". Then there is a need for some Turing-complete engine to evaluate the program or, alternatively, SPARQL can be used to query the program or to query the results of computation.

What I like in RDF is that it's very primitive foundations: triples. Still it makes powerful knowledge representation media, which I think can be one of the pillars of Eternal Programming. One important feature at the very low level is concatenativeness: it's very easy and logically ok to just merge any number of RDF graphs together to combine knowledge. With open world it's harder to say "NOT", of course, because there may be some additional knowledge inaccessible at the moment of inference, which has the needed fact. But this is life: We do not have only yes / no answers, sometimes, it's proper to say "do not know".

First results here. The description logic (as reported by Protege software) so far is ALCH(D), but I only scratched the surface (individuals are not shown on the following OWLViz diagram):

screenshot from 2018-01-06 20-23-04

So now basically I am thinking of specific implementation of oMiser, which will be the Turing-complete rule language to interpret algorithmic knowledge in the form of RDF graph (and result in RDF graph or whatever further research will find more suitable). In this implementation I can offload heavy-lifting to some RDF library, yet to decide, and I think I will not be concerned at all with checking cycles. Not yet sure whether to go with Prolog or stick with Python's rdflib I am better familiar with. Plus some practicalities to be resolved, for example, now I have Pair as a class for and ob, but there is also a need for object relation called "pair", which corresponds to the a :: b. Luckily, oMiser does not require n-ry relations (those are uglier in RDF to represent).

PS. Perhaps, pair is better represented by a blank node after all, then the object relations will be ob:a and ob:b... I was wrong above, ob:c is ternary relation...

@orcmid
Copy link
Owner Author

orcmid commented Jan 6, 2018

Unfortunately, I've never worked with Hindley-Milner algorithm, but I suspect if oMiser's tree is given to it, it will be capable to deduce types!

I think that is possible in some constrained cases, but not for every applicative expression at the oMiser level.

This will come up in examination of (higher-level) representation of combinators in ‹ob›. I think there will be polymorphic type inference first.

@rnd0101
Copy link

rnd0101 commented Jan 7, 2018

It can be quite involved, but it seems current technology does it for even for machine code. This is more curiosity than something I'd liked to personally pursue.

@orcmid
Copy link
Owner Author

orcmid commented Jan 7, 2018

It can be quite involved, but it seems current technology does it for even for machine code.

Thank you. That is a valuable find. The ob representation of scripts to obap.eval and obap.ap are essentially machine-language programs and attempting to "decompile" them to a higher level with (denotational) types is a valuable idea. The case does apply to oMiser and it is something on the radar.

This will matter in the case of program transformation, where, if certain idioms are known, optimisations are possible. This is going to come up around lambda-abstractions and working with combinators and optimizations of applicative operations across apply operations when type restrictions can be inferred.

I have in mind nailing down oMiser at the obaptheory level and working on these other matters at a level of oMiser-application. Any optimization idea that are taken into an oMiser implementation itself will be invisible (apart from changes in performance).

That's all for future consideration with oFrugal/oMiser as a working laboratory fixtures.

@orcmid
Copy link
Owner Author

orcmid commented Jan 7, 2018

Apart from any clarification and refinement in editing, I have completed the "Second: About Applicative Interpretation" comment now.

The next thing I want to motivate is the use of traces and the additional properties on constructions involving lindies. That is coming up, here.

I notice that "applicative interpretation" is a broad term that I use rather broadly. Please note that the apint(p,x) sub-function used on behalf of the Ot representation of function obap.ap(p,x) simply collects together the applicative interpretations where ob-is-individual(p). Is there a better name than "apint" for this case?

Also, those are all of the cases for ob individuals so far. There will be some extensions later on.

@rnd0101
Copy link

rnd0101 commented Jan 7, 2018

Thanks! That is high quality description (I mean "About Applicative Interpretation" comment), which deserves to be in the miser documentation rather than here.

As for the term "applicative interpretation", I was thinking it's about some process in the applicative programming language, perhaps, interpreting expression according to the "script", where script is a result of a purely applicative programming (composition of functions).

But now that you raised this terminology point, I am no longer sure the term is descriptive enough, especially in "apint" abbreviation, where first thought is it's about integers. (but then there are no integers here)

OTOH, it's definitely about interpretation during application of the script, right?

BTW, are there any examples of the programs, something, which can help to decipher ob/obap theory semantics? I mean, "hello world", "factorial", tower of Hanoi, GCD algorithms or something everyone is familiar with or at least algo to add two numbers?

@orcmid
Copy link
Owner Author

orcmid commented Jan 7, 2018

it's definitely about interpretation during application of the script, right?

Right. Based on our exchanges, I think I want to say that an applicative-expression is an ob, exp, for which obap.eval(exp) interprets and evaluates as the applicative operations that are expressed in it. I would say that obap.ap(p,x) interprets p as the script for an applicative operation it applies to the operand ob, x.

I think, when our discussion stabilizes a bit, I need to rework the basic descriptions (and also fix the various indy-related predicate names).

@orcmid
Copy link
Owner Author

orcmid commented Jan 7, 2018

BTW, are there any examples of the programs, something, which can help to decipher ob/obap theory semantics? I mean, "hello world", "factorial", tower of Hanoi, GCD algorithms or something everyone is familiar with or at least algo to add two numbers?

oMiser is so primitive that there are nothing like numbers or strings, and no input-output. The input-output part could be handled by oFrugal though. Even arithmetic has to be defined.

There are some examples in the obapcheck.sml file, but they clearly do not resemble what they accomplish very much. oMiser is not very expressive compared to even moderately-higher-level languages.

Here's a simple representation for (Peano) arithmetic:

  • Let the representation of natural number be the obs rN.
  • Let ob.ARG represent 0.
  • For x in rN, let ob.B :: x be the successor of x. That is, ob.B::ob.ARG represents 1, ob.B::ob.B::ob.ARG represents 2, etc. (Recall here and in what follows that :: is right-associative.)
  • For x and y in rN, Miser operation obap.ap(ob.NIL::x, y) represents the arithmetic operation y dim x, the diminishing of "y" by "x". Diminishing is like subtraction but 0 diminished by anything is still 0.
  • (Note: The prefix with ob.NIL (having applicative interpretation as the identity function) is to avoid ending up with the trace applicative interpretation of ob.ARG as p.)

It is an interesting feature of this representation that subtraction by diminishing is easy, as is addition by 1 (successor). So there are rather crude kinds of counting and testing available. It is also an interesting contrast with untyped λ-calculus where substraction is considered difficult.

Here's a simple (inefficient) kind of test. Let x be the number of times some action is to be performed. Let y be how many performances have been already, starting with the rN of 0. At least one performance remains to be done if x dim y is not 0 (i.e., is not a singleton).

A practical version would be to simply check if x is 0 (a singleton) already and if not, perform a repetition in which x is diminished by 1.

Even more practical is traversal of a list structure where a singleton represents the empty list. Here, the diminish function on the list is like taking the tail n times, where the tail of the empty list is the empty list.

That's the kind of idea involved in programming via ob applicative expressions.

It's not that these representations are practical to use. The first-order interest is that such representations are possible and we can consider the computational-model capabilities on those terms.

There are two useful examples in obapcheck.sml. Using the ob.txt reference notation, there is introduction of an ob, cS,

cS = .C :: ‵.C :: (.C :: (.E :: (.C :: (.E :: .ARG)
                                           :: ‵.ARG) ) 
                            :: ‵(.C :: (.E :: .ARG) :: ‵.ARG) ) 

which, if I have done it properly, represents the combinator, S, such that

            ap(ap(ap(cS,x),y),z) = eval( ( (  ‵cS :: ‵x) :: ‵y ) :: ‵z ) 
                                                    = ap(ap(x,z),ap(y,z))
                                                    = eval( ( ‵x :: ‵z) :: ‵y :: ‵z )

Ob cS can be viewed as representing the hand-Curried λ-expression λx λy λz ( x(z) y(z) ) and that's exactly how I derived it.

updated 2018-01-09: corrected two typos

@rnd0101
Copy link

rnd0101 commented Jan 8, 2018

Thanks! During the week it's hard to contribute for me, but I will follow everything you write.

FYI, I've asked this question at stackexchange CS Q&A site. Lets see if anything useful comes up.

I think, efficient transformations and optimizations are crucial for upper levels of computational stack.

@orcmid
Copy link
Owner Author

orcmid commented Jan 9, 2018

In (some? many?) FP languages type checking and interpretation are separated. I do not follow latest developments, but I guess type-checking must be eager, and interpretation can be whatever. ML family perhaps went to extreme with type-safety,

I think of type-checking (at least in ML) as static so it happens at compile time (i.e., before interpretation). ML is also eager/greedy in the sense that evaluation is not lazy.

oMiser requires no [static] type checking, since everything is an ob. There is a kind of type inference that can be made, and that will be interesting to explore as a prospective avenue to introduction of types. It will show up when we represent combinators and lambda-abstraction.

@orcmid
Copy link
Owner Author

orcmid commented Jan 9, 2018

Third: About Traces and Lindies

update 2018-01-10: Complete discussion of pure-lindy traces and use in symbolic execution.

There is a somewhat paradoxical notion introduced with traces and having an interpretation for interpretation-free lindies.

Traces

Traces occur in three ways.

  1. There are cases where there is no meaningful applicative interpretation. For example, individuals that serve as special forms in an evaluation (obap.ARG, obap.SELF, and obap.EV) don't have any natural interpretation as operators in obap.ap(op, x). The interpretations in evaluation of special forms simply have no natural carry-over to a meaningful applicative interpretation of those individuals. Instead, occurence as operators is defined to produce a trace, a preservation of the "attempted" operation that serves as a result instead. Any use in an applicative context results in the same trace once again.

  2. Claiming that lindies are arbitrary and have no intrinsic interpretation leads to the same rationale as for (1). Applying a lindy as an operator in obap.ap(op, x) yields a trace. There is an useful variation on (1), however. If x is already what's known as a pure-lindy trace, the result is simply op :: x, also a pure-lindy-trace and that rule preserves pure-lindy traces. When x is not itself a pure-lindy trace, op :: ‵(x) is produced as the necessary trace case.

  3. It is handy that traces capture information at the obap.ap(p, x) point where p is an individual for which there is no further applicative interpretation. For lindies, there is the additional provision that a pure-lindy trace be any ob in which all singletons are lindies. In addition to the condition in (2), if p is a pure-lindy trace, not only an individual, and x is also a pure-lindy trace, the immediate result of obap.ap(p, x) is the pure-lindy trace p :: x. Otherwise, existing rules apply, including (1-2). The benefit is as follows. If p in obap.ap(p, x) has non-individual pure-lindy trace form, obap.ev(p,x,p) would ordinarily produce p, losing anything about x. In the case that x is also a pure-lindy, it is valuable to yield p :: x instead, since the combination will be a pure-lindy trace that preserves more of whatever information is to be gained by yielding that trace.

N.B: Thanks to insistent @rnd0101 (and earlier Paul McJones) questioning of the mysterious lindy-structure predicates, this explanatory effort has inspired improvement in obaptheory.txt representation of this situation and the nomenclature used. -- orcmid 2018-01-09

Lindy Traces and Symbolic Execution

There could be further special cases that would capture more when the applicative script, p, is a pure-lindy trace and the operand, x, is not. It is an arbitrary choice to build up only the case where p and x are pure-lindy traces themselves. This is also expedient from an engineering perspective. It then becomes useful in engineering of complex applicative interpretations.

Example 1: Confirmed Representation of a Combinator

Previously, the ob cS was presented.

cS = .C :: ‵.C :: (.C :: (.E :: (.C :: (.E :: .ARG)
                                           :: ‵.ARG) ) 
                            :: ‵(.C :: (.E :: .ARG) :: ‵.ARG) ) 

It is claimed that this ob represents what is know as combinator S with specific applicative interpretation.

            ap(ap(ap(cS,x),y),z) = eval( ( (  ‵cS :: ‵x ) :: ‵y ) :: ‵z ) 
                                                    = ap(ap(x,z),ap(y,z))
                                                    = eval( ( ‵x :: ‵z) :: ‵y :: ‵z )

In the SML mockup file obapcheck.sml, there is domonstration of that applicative interpretation by "symbolic execution" using the pure-lindy traces ƩX, ƩY, and ƩZ for x, y, and z respectively. The results of ap(ap(ap(cS,ƩX),ƩY), ƩZ) and of eval( ( ( ‵cS :: ‵ƩX ) :: ‵ƩY ) :: ‵ƩZ ) are each the symbolic-execution produced pure-lindy trace (ƩX :: ƩZ) :: (ƩY :: ƩZ).

More important than that result is that script cS was derived by working backwards from that desired result as demonstrated in the obapcheck.sml mockup. This also helps to ensure that the introduction of pure-lindy traces did not short-stop and obscure anything in the applicative interpretation of cS as a representation of combinator S.

The representation of combinators, including S, is important, later, in demonstration of the universality of obap.ap and obap.eval. Combinator representations are also representative of ways that scripts can be used to combine other scripts into ones with composed applicative interpretations, a fundamental facility in functional-programming.

Example 2: List Processing Operation

Another test case in obapcheck.sml concerns list processing. Symbolic execution was important in derivation and verification of the ob identified as "has." The idea is that has(x) applied to a list, L of items will report whether x is in the list or not. A list is represented, in this case, as an ob

   v1 :: v2 :: ... vn :: end

where end is a singleton and the list could be otherwise empty.

has = .C :: ‵EV 
         :: .C :: ‵(.D :: .ARG :: .B :: .ARG)
               :: .E :: .C :: .B 
                           :: .C :: ‵EV 
                                 :: .C :: (.C :: ‵D 
                                              :: (.C :: (.E :: .ARG) 
                                                     :: ‵(.A :: .ARG) ))
                                       :: .E :: ‵( .A :: .SELF :: .B :: .ARG)  

ap(ap(has, x), L) will yield obap.B if x is not one of the vi and obap.A if it is. Representing the truth-value by obap.A and false-value by obap.B, the script, has, can be taken to represent the functional-programming-style predicate

   fun has(x) List = let hasX L
                       = not is-singleton L
                         andalso (x = a L orelse hasX( b L )
                      in hasX List

Symbolic execution is indispensible in the formulation and verification of the has script. It will take more gradual development to development of facility with such derivations. This will be expedited by introduction of scripts, akin to the combinators, as idioms for construction of these more-extensive scripts.

Some Observations

The pure-lindy trace condition and traces generally are an engineering solution to the case when there is no already-defined applicative interpretation that is coherent with special-form employment of individuals. The benefit is a kind of fixed exception treatment that has operations yield definite results when there is nothing further to be done. Traces and pure-lindy traces are enshrined in the ‹ob› Ot theory to impose what is essentially an engineering decision on all valid computational interpretations of the mathematical formulation. It is thereby assured that oMiser implementations are interoperable in this respect, and any results from evaluation of representations of the same scripts will be the same. This opens the door for exploitation of pure-lindy traces in confirmation of the correct operation of scripts by providing symbolic execution as part of test suites and as an aid in the derivation of scripts.

Symbolic execution (and symbolic reduction of formulas) is a well-known method in confirmation that evaluation of a given formula or execution of a particular procedure yields a particular (canonical) result. This is usually performed mentally or with pencil-and-paper (now, maybe text editor). It is by a kind of heuristic operation using "pretend" variables and seeing how far an evaluation can go without disturbing those variables.

The oMiser ob and applicative interpretations are so primitive that having some mechanical way to explore evaluations via computer-aided symbolic execution is very important, as will be seen repeatedly in development of actual scripts for practical operations at the oMiser level.

Cautionary Note. One needs to be careful in reliance on pure-lindy traces in the development and confirmation of oMiser scripts. Because they are traces, they can be misleading when their preservation occurs where computations with different operands would take different directions. The kind of building up illustrated in worked cases will demonstrate how this pitfall can be avoided.

orcmid pushed a commit that referenced this issue Jan 10, 2018
The simplification and consistency of is-pure-lindy-trace(ob) tracing is
introduced, with obap.sml and obap.check alignment
@orcmid
Copy link
Owner Author

orcmid commented Jan 10, 2018

BTW, have I missed some article with this information somewhere is the above description new one?

No, everything was, and still is very skeletal as I initiated development of the project. Thanks to your great questions, I have stitched together this much by way of explanation. Your questions about Lindies here and in Question #4 also inspired an important simplification. Thank you.

@rnd0101
Copy link

rnd0101 commented Jan 10, 2018

Nice simplification! Now it makes sense.

Minor: Pls fix typo is-pre-lindy-trace in 9e63744#diff-fc98cd4bafcb3704c5ea20ef6896c9b3R126

Also, I am trying to find a paper, which presented minimum length universal combinator... If I remember correctly, it was about of length ~ 21. Could be good example for oMiser. Also there is an interesting article on combinators used in general AI: Universal Induction with Varying Sets of Combinators by Alexey Potapov, Sergey Rodionov. (in short: Genetic Programming with CL) This is near that what I tried to recall on search for algorithms. Maybe relevant for scope of Miser project.

I will try to find "universal combinator machine" as well. At least here there is an interesting survey of minimalistic machines for Chaitin & Co AIT. So maybe self-interpreting example for oMiser can be of illustrative value. Also, https://en.wikipedia.org/wiki/Combinatory_logic#One-point_basis can be yet another cool example.

orcmid pushed a commit that referenced this issue Jan 10, 2018
There was a typo and incomplete replacement when
obap.is-pure-lindy-trace was introduced as simplification of lindy-based
traces.
@rnd0101
Copy link

rnd0101 commented Jan 21, 2018

While adding more to the "standard library" , I've found, that:

  1. oFrugal uses reverse application order than is convention in combinators calculus (well, this is probably ok, there is some reason for that order, I explain it for myself as it corresponds to the pair-construction order, but I really do not know if there is greater rationale behind)
  2. Typing ^ is a nuisance. The question is, are lindies supposed to be more numerous in the expressions than frugal variables? Currently, I am using "this" for lindies, so variable names should not clash with them.

@orcmid
Copy link
Owner Author

orcmid commented Jan 21, 2018

@rnd0101: Typing ^ is a nuisance. The question is, are lindies supposed to be more numerous in the expressions than frugal variables? Currently, I am using "this" for lindies, so variable names should not clash with them.

Well, you are using "lindy" notation, i.e., string format. That won't be used in oFrugal because there is the hope of having strings some day 😄 .

Lindies are the closest there are, in oMiser, to LISP atoms. They are truly meant to be literals, and should be easy to use as/in obs representing data. It will also make lambda.lindy and rec.lindy be smooth (anticipating that the idioms will be for ^lambda.lindy and ^rec.lindy), based on the interesting case in Question #10. With lambda implemented via oMiser script, there may be much use of plain lindies in the oMiser idiom for lambda expressions.

@orcmid
Copy link
Owner Author

orcmid commented Jan 21, 2018

@rnd0101: oFrugal uses reverse application order than is convention in combinators calculus ( ... . I explain it for myself as it corresponds to the pair-construction order, but I really do not know if there is greater rationale behind)

Yes, I have been using the right-to-left instead of left-to-right order for over 50 years, so it is automatic for me. It also avoids treating a b :: c d as (a b)::c d although we have a problem to work out about the other side of it, and I am still pondering that challenging suggestion of yours at Question #10.

@orcmid
Copy link
Owner Author

orcmid commented Feb 2, 2018

@orcmid Yes, I have been using the right-to-left instead of left-to-right order for over 50 years, so it is automatic for me. It also avoids treating a b :: c d as (a b)::c d

Well, after having gone around and around on that, the current conclusion is that

a b :: c d :: d e = (a b) :: (c d) :: (d e) 

after all. This is how established in the summary grammar in Question #14 and #8, and the resolution of Question #13. Whenever I doubt it, all I need to do is look at the above identity.

The use of ^ as the first character of binding names is something we appear to be stuck with it, and there was a bit more about needing to differentiate three flavors of terms in Question #10. A better resolution has not come forward.

I think it is time to close this one too.

@orcmid orcmid closed this as completed Feb 2, 2018
@orcmid orcmid added the discussion Project discussions label Jan 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Project discussions question
Projects
None yet
Development

No branches or pull requests

2 participants