NNice
NNice (pronounced N N ice) is an esoteric programming language created by User:Yayimhere as a solution to Bitch, Ienai!. It was created by building from the properties, up into objects, and opposed to the other way around.
Syntax and Execution
Programs are lists of lists of expressions. Lists of expressions are written (Expr_1, Expr_2, Expr_3, ...). Meanwhile, the lists of lists have the lists on their own lines. Execution order one act list is top to bottom. For The order within the expression lists, normally it would only ever execute the first expression, and then second expression, unless required otherwise. An expression can refer to another expression via its index. This is local to each list, the only way two different lists can refer to each other is by indexing per line, which is written lineindex_. Normal indexing is purely written a number. The first expression in a list may only reference indexes, not any not indexed values. Everything is 1-indexed. When an application of a function is done, and either the function or the value it is applied to refer to a currently unevaluated index, execution jumps to the value at that index, evaluates it, and jumps back. Application is written either `Fx or **Fx. The first kind is totally normal application, the second will evaluate F and mark it as evaluated if x hasn't already finished evaluating. Then, immediately after x finished evaluation, **Fx evaluates as a normal application. Theres some indexes which are written with a "symbol", i.e it isn't a number, but acts precisely as on. these have always been evaluated. They all have definitions, which are given later. Theres five of them. Additionally, execution is lazy, i.e a value is left unevaluated until absolutely needed. For example, an expression isn't fully executed until it needs to be applied to something, is has something applied to it.
Values and Functions
Values and functions are actually the same, unified under the umbrella term "Existent": a pair of a string and a function. When written as constants into code, they are only defined as strings and the functional part will be found by the program, however implicitly in execution, both sides are defined when for example something is applied. This means that an Existent can both be applied, and be applied onto. This also means all expressions are Existents, as an Existent applied to an Existent can never return a non-Existent value. Note that every Existant has a corresponding type. The strings are made up of characters from the alphabet <>λθ→L~, with a number of rules:
<>must always match as brackets, though there may be content in between the two characters.
→must be postfixed by>orλ, and must be prefixed by two characters or more.θmay not be prefixed and/or postfixed byθ.- for every
λthere must be a pair of<>'s, which both are located somewhere after thatλ. Lmay not be postfixed by anything other thanL.~may not be within, postfixed, or prefixed, by<>.
The function side is derived by taking the length of the string, ignoring all the L's, - 1 modulo 4 + 1, and then each number results in a specific function:
N/1: This function takes an Existent of typev(see below for types), and returnsM.l/2: This function takes an ExistentXand returns another Existent, which when applied to an Existent of typeXreturnsX, and otherwise returns the inputted Existent unmodified.M/3: The function takes an ExistentX, and returns another Existent, which when applied to an Existent of typeM, returnslapplied toX._/4: The function takes an ExistentX, and returns another Existent, which when applied to an ExistentA, returns another Existent, and when THAT ONE is applied to an ExistentB, returnsXapplied toA, applied to the result ofXapplied toB.
(Note: The actual string content is only extra temporary state to an Existent, the actual value part is the pair of the number of L's, and string length ignoring L's) The five symbols are defined as N = θLL, l = <>LL, M = <θ>L, _ = <θ>θLLLLL, and v = →L. Also note that when X is applied to Y the amount of L's in the result is replaced with the number from X, not Y.
Types
Types are very simply defined. They are pairs of a number N and a function F. A number X has that type if N/X or X/N is an integer, and F(X) = N. Its possible to make a type into an Existent and the other way around: For an Existent that is constant/Hasn't been applied to anything, or been modified, the conversion is as follows:
The length of the string ignoring L's determines the number half of the pair, and the function of the Existent is converted into the function half of the type:
Mbecomesi,tA(X) = X*A.
NbecomescA,cA(X) = A.
lbecomes-A,-A(X) = X-A.
_becomes%A,%A(X) = X modulo A.
The A number here, is calculated by the number of L's. For an Existent which is defined via application of two Existents, the type is a little weirder. The two Existents have their corresponding types, lets say the Existent acting as function is N and F, and the one acting as a value is M and G. The resulting type, will have its number be π(M,N), and its function be will be π(π(F(x),G(x)),π(A+C,B+C)), where π is cantors pairing function, and, A and B are the additional values for the function F and G respectively, and C is the NEW amount of L's. As such, for D to be in the resulting category, π(M,N)/D or D/π(M,N) must be integer, and π(π(F(D),G(D)),π(A,B)) = π(M,N).
How state modifies the Existents
State modifies both what the function of an Existent does, as well as, when an existent is applied to another, the applicants state modifies the state of the thing its applied to. First how it modifies the functions behaviour. This will require a bit of notation. The state modifies how a function applies to its input, so it modifies `Fx, but for simplicity well be using lambda calculus style F x and brackets. Additionally, x'S means the state string S, modified the function Existent, and F x|G y means "iff input is of type x, return F, if its of type y, return G, otherwise it acts as an identity." The following characters have these effects on the functions, it is interpreted from left to right:
(F x)'λ = (x F) (F x)(F x)'<S> = F (x'S)(F x)'θ = F x(F x)'→ = F (x'→) x(F x y z)'~ = ((F z) x | (z F) y)
Now, the state also, when the Existent its tied to applies to something, changes the state of what its applied to(beyond the L things). We use the same x'S, and the string being modified is represented as X, and the string being applied is F. This also changes it left to right:
λchanges the string intoX'X.<S>replaces the first instance of<Y>inXwith<Y'S>θhas no effect.→shiftsXleft, wrapping around(i.e the leftmost characters becomes the rightmost), the minimum number of times that keeps the string syntactically valid. The amount of shifts must be above 1.~replacesXwithF'X.
Proof
It obviously needs to be proven that NNice solves Bitch, Ienai!. So thats what we'll do! First, the values and things:
- The pair of
(String length without L's, Number of L's)classifies as the values. - The functions are the transforms(obviously).
- Existents are semi-entities.
- The types are properties.
- Writing an Existent at an index is defining. It gives the Existent a name, its index.
- Entities are just Existents/Semi-entity with an index assigned.
Now for the actual constraints:
- "Every property can be used as a semi-entity, and vice versa. This mapping is always unique." It is obvious that you can map any Existent thats simple/hasn't been applied to or onto, to a type, that process is explicitly described. It should be noted that, the string CONTENT gets lost in mapping, but the string content is state, not actual value. For the case where it's not simple, we can still assure uniqueness, as per the properties of the cantor pairing function
π, which is bijective. This means, if you can detect when a type VS isn't made from application, you can reverse the types use ofπand get the two original functions. And you can do that, by looking at the function, which explicitly written using aπ. Then you can reverse it and get all the pieces, and additionally don't lose the order, as perπbeing ordered. - "Transforms only work on either the actual entity or the properties of the entity they take in." this is easy to prove. Three out of four functions are already builtin, so must follow this. The last one also does, takes in an Existent i.e the semi-entity itself, and returns a function which also takes a semi-entity, and all layers do so.
- "When written in a program, syntactically semi-entities have to be written as either the value part, or the transform part, and the other will be derived. However, if a semi-entity is created by for example evaluation, or in some other non-syntactical way, it could be defined as both." Existents are always written as the value side, the string(which also includes the state), and the modulo is used to .
- "Every semi-entity has the universal property, called v." this would be
(1,c1). Additionally a value by this name exists, which in fact HAS that value(1,c1). - "There exists the following builtin entities, which are defined before the program starts: [...]" Those are
N,l,M.