-
Notifications
You must be signed in to change notification settings - Fork 1
Home
An idea I just had that I feel like recording is embedding some kind of choice signature into values produced from nondet functions--an index, perhaps, in the space of possible results, plus context. It could be pulled out directly, or used for inversion purposes: in particular, a half-solution to this challenge might take the form of substring, double, un-substring (or more like double under substring) rather than dealing with partitions explicitly. Have some means to choose whether or not to undo in such a fashion, likely restrict it to explicit cases and under. Or just scrap the choice-signature idea altogether and just have each builtin define its own under operator--implement under composite functions simply as a nesting
Any use for choice signature? The more I think about it the smarter it seems to make under its own thing entirely. Should everything even have a standalone in/obverse?
Some level of Jelly-like, or more broadly APL-family-like, tacit syntax, but how to undo dyads? One option might be to supply one argument and invert over the other--user chooses the root or the logarithm, for example. It could be nice to have a Brachylog-style option as well, where both arguments are free and generated as a pair of some sort. One idea I'm considering is to give some functions secondary return values of some sort on top of the choice-trace thing, but I can't think of good enough use cases to warrant making it even harder for me to decide on nice chain parsing--speaking of which, I sort of want to make certain structures automatically parse as single links, repatterning explicit structure compared to Jelly's strategy--likely more for the worse than for the better, but what matters is that it's different!
Thinking about the idea of secondary return values gave me the idea of some constraint functions having no return values, as opposed to, say, passing the input through. This would also be relevant for inverting nilads, turning them into equality checks. Not entirely sure what the best way would be to determine what parses into the no-return chain as opposed to what supplies the value used afterwards would be though... might want to study some Brachylog answers with ∧
s in them for some inspiration. Treat like specially-flagged monads?
Where an inverse of a builtin is itself a builtin, attempt to have divergent behavior for the assigned obverse, without necessarily matching it with under. For example, with something like Jelly's i
and ị
, perhaps the obverse of i
might fail given an out of bounds index, and the obverse of ị
might generate infinitely many possible modular indices.
Memoize recursive integer functions? Adding a certain amount of type checking into the whole mess won't make this any easier for me to actually bother writing, but if it's justified maybe it'll actually help me feel better about internal design decisions--I don't want to feel like I'm ripping off Husk when I'm already ripping off Brachylog and Jelly, but it really wouldn't be. Or just have some sequence-tailored operators ¯\_(ツ)_/¯
Rank system for functions?? Choice is an axis, not redundant with other axes because of special concat-map behavior
Any reason not to make vectorization strategy for dyads some recursive kind of zipwith? Guess it depends on how to handle non-matching shapes--truncate, cycle, outer product, or even fail?
For functions of lists, could have first-axis leading-axis pairs APL style, perhaps with modifiers to accommodate more exotic choices of axes--second axis, second-to-leading axis, choice axis, force map over digits/ranges... raises question of if perhaps arrays must have a single defined depth, or if flexibility could be advantageous. Reminds me, consider homogeneous array types? The idea appeals to me, but I can't think of how useful it would actually be
Heterogeneous depth and type could be necessary for choice, in fact, unless there's simply no construct like Brachylog's |
(Prolog ;
), which wouldn't be all that nice to do without. Depth could be accounted for with wrapping as needed only if infinite recursion is either forbidden or restricted to cases which preserve depth, but neither of those make a lick of sense for a language that seeks to avoid having useless errors!
What could sensibly be the result of attempting to obtain a monad's right obverse or right under? Ideally, the right under of a monad would still be an operator similar in nature to that of a dyad, so perhaps it could be the under of the obverse? Or maybe anything based on a right argument applied to a monad could first turn it into a dyad ignoring the left argument, as might be useful with dyadic chain parsing rules taking after Jelly (or APL and J, for that matter) in that regard. Should this occur before or after binding of dyad-nilad pairs? (With Jelly's exact dyadic fork rule, I can certainly see after being advantageous.) Could also conform more to the conventional notion of left and right inverses where there is such a distinction to draw, but that doesn't seem practical. Right-argument operators on nilads could trigger some kind of long-distance dyad binding, sweeping up monads in between...?
Infixy meta stuff requires precedence in a way suffix wouldn't... if, say, FuG$H
is "bind F under G to H" (FGuH$
), then what's "F under (G bound to H)" (FGH$u
)? Could do something profoundly stupid and turn infix into suffix by swapping the operator and the token to its right (from right to left), so in that example we might have FuG$H
and... FG$uH
. Pleasing in the case of left to right, and completely unreadable otherwise... original if nothing else! Not entirely sure it doesn't have logical problems on top of readability, but it should be fine. Whipped this mess up to play with it, and it's actually starting to make sense... I could even have it apply to unary operators, making them prefix...
Internally, should the right argument to a chain be a single choice, or should the data state of the program be a list of larg rarg pairs? I've been leaning towards the latter, but having just now thought of just doing the former, it makes a damned good bit of sense--when introducing a new rarg with choice involved, just treat it all as a single dyad
Instead of a few dedicated builtins for accessing other lines of the program, maybe overwrite some otherwise-defined builtins based on how many lines are above/below the current one? The first few would have to be relatively niche, but still worth assigning a single byte to, and across all three arities...
Choice can handle monads just fine--scanl (>>)
I'm vaguely remembering a thought I had about operators being bijective with regards to arity, for the purposes of modifying variadic trains in a variadic context. Obviously that can't be precisely true for non-unary operators, but there's at most one variadic operand, so the principle can remain...?
Operator to bind dyad-monad forks where the monad handles the right argument, perhaps as a full replacement for Jelly's }
--no such replacement for {
because that wouldn't make any sense at all
Prefer infinite lists over janky operator arguments--does raise question of how to handle >2 inputs cleanly. Full program approach not desirable--maybe just have an operator for (re)introducing nilad values, maybe add some kind of limited triad train parsing... stack mode?
Funny code page use idea: assign backtick to something relevant only to large programs, to avoid Markdown inline code formatting hassles
Dyad double-inverses, internally if nothing else, might be desirable for, say, some kind of unfold... that is, if I don't scrap inverses altogether. Without Prolog variables, things like un-length and un-behead could be either damned narrow or even more of a mess... In the case of double-inverses for unfold, perhaps that might be a bit saner in only the context of under.