I found an example of shift-reset delimited continuations in Haskell here:
resetT $ do
alfa
bravo
x <- shiftT $ \esc -> do
charlie
lift $ esc 1
delta
lift $ esc 2
return 0
zulu x
This will:
Perform alfa
Perform bravo
Perform charlie
Bind x to 1, and thus perform zulu 1
Fall off the end of resetT, and jump back to just after esc 1
Perform delta
Bind x to 2, and thus perform zulu 2
Fall off the end of resetT, and jump back to just after esc 2
Escape from the resetT, causing it to yield 0
I can't figure out how to write the equivalent code using SWI-Prolog's shift/1 and reset/3.
The code below is my attempt. The output is the same, but it seems messy and backwards, and I feel like I'm misusing Ball to get something similar to the esc 1 and esc 2 in the Haskell example. Also, I am not sure what to do with return 0.
% not sure about this...
example :-
reset(step, ball(X), Cont),
( writeln("charlie"), X=1, call(Cont), fail
; writeln("delta"), X=2, call(Cont)).
step :-
writeln("alfa"),
writeln("bravo"),
shift(ball(X)),
format("zulu ~w~n", X).
I'm rather confused: Scheme/Haskell/ML-style shift-reset and Prolog shift-reset seem almost like entirely different things! For example, you pass a lambda into Haskell's shiftT but you do not pass a goal into Prolog's shift/1.
Where is the Prolog equivalent of Haskell's \esc -> ... esc 1 or return 0? And where is the Haskell equivalent of Prolog's Ball or call(Cont)?
I feel that a "proper" port of the Haskell example above would answer these questions.
The reset and shift operators originally come from Danvy; Filinski,1990. “Abstracting Control”. The corresponding interfaces in Haskell Control.Monad.Trans.Cont conform to the original semantics, except for some type restrictions. The delimited continuation interfaces in SWI-Prolog are not exactly the original reset and shift. They are more closely related to Felleisen,1988's prompt and control or Sitaram’s fcontrol and run operators.
Usually, it is not difficult to translate delimited continuation programs from Haskell to Prolog. The difficulty in your example is that it calls the same continuation esc twice with different values. For example,
example :-
reset(step, ball(X), Cont),
X=1, call(Cont),
X=2, call(Cont).
After the first call(Cont), X is already bound to 1, you cannot rebind it to 2.
TomSchrijvers' advice is to create copies of the continuation with fresh unification variables using copy_term/2 (yes, continuations are also terms in SWI-Prolog!), so the Prolog equivalent of your example is
example(X) :-
reset(step, Ball, Cont),
copy_term(Cont+Ball, Cont1+Ball1),
copy_term(Cont+Ball, Cont2+Ball2),
writeln("charlie"),
ball(X1) = Ball1,
X1=1, reset(Cont1, _, _),
writeln("delta"),
ball(X2) = Ball2,
X2=2, reset(Cont2, _, _),
X=0.
step :-
writeln("alfa"),
writeln("bravo"),
shift(ball(X)),
format("zulu ~w~n", X),
shift(ball(X)).
?- example(X).
alfa
bravo
charlie
zulu 1
delta
zulu 2
X = 0.
More detail discussion, see https://swi-prolog.discourse.group/t/naming-clarification-about-delimited-continuation-reset-3-and-shift-1
Pitty the CW631 paper here:
http://www.cs.kuleuven.be/publicaties/rapporten/cw/CW631.pdf
doesn’t show Felleisen prompt/control implemented with difference list rules:
/* Vanilla conjunction list solve/1 extended to control/3 */
control([], none, []).
control([prompt(A)|B], A, B).
control([control(L, P, Q)|B], X, Y) :- control(L, P, Q), control(B, X, Y).
control([A|B], X, Y) :- rule(A,C,B), control(C, X, Y).
Its quite fast, since it does nothing with the continuation, just takes it as is,
which I suppose is the spirit of Felleisen. Here the good ole shift/reset:
/* SWI-Prolog 8.5.14 */
?- time(run_state(fib(25), 0, S)).
% 2,306,455 inferences, 0.453 CPU in 0.499 seconds (91% CPU, 5090108 Lips)
S = 121393 .
And here the prompt/control:
/* SWI-Prolog 8.5.14 */
?- time(control([run_state([fib(25)],0,S)],_,_)).
% 3,641,788 inferences, 0.891 CPU in 0.980 seconds (91% CPU, 4089025 Lips)
S = 121393 .
/* Jekejeke Prolog 1.5.4, only 512 MB allocated, JDK 16 */
?- time(control([run_state([fib(25)],0,S)],_,_)).
% Threads 1,656 ms, GC 404 ms, Up 2,086 ms (Current 08/23/22 04:19:56)
S = 121393
The test case is here:
http://www.rubycap.ch/gist/felleisen2.txt
I have been using J for a couple of weeks now and recently started using it for simple problems rather than just toying with concepts.
This problem needs to replace all x characters in a string into y, which works but using a dyad form of the final verb gives me unexpected output.
Let's use the following example input:
input =. 'abcxdefxghi'
First, I need to find the indexes of x characters in the right argument for amend.
findx =. I.#:([:'x'&= ])
findx input NB. 3 7
0 findx input NB. 3 7
1 findx input NB. 3 7
Then, I amend the results of findx with a bonded y on the left.
trxy =. 'y'&(findx })
trxy input NB. abcydefyghi
_1 trxy input NB. domain error
0 trxy input NB. abcxdefxghi <= this is the really unexpected result
1 trxy input NB. abcydefyghi <= somewhat unexpected, works with strictly positive ints
'a' trxy input NB. domain error
There is two things I don't understand:
why trxy sometimes works as a dyad when I thought I bonded the left side of my amend ?
why a 0 left argument stops trxy from working ?
With 1 trxy input you are executing 1 'y'&(findx }) input – and x u&n y is maybe not what you expect. It is documented (somewhat hidden) on the bottom of this page: https://code.jsoftware.com/wiki/Vocabulary/ampm
It is equivalent to x (m&n # ] ^: [) y, thus applies n on y (with m on the left side) for x times. That's why with 0 trxy y you aren't executing anything, thus y stays the same. With 1 trxy y you are applying trxy once to y. As trxy has no trivial inverse, _1 trxy y results in an error. And because 'a' isn't a number, the last one is just a plain error.
If you – for whatever reason – just want to be able to write trxy as a monad and a dyad that ignores the left-hand side, you could use trxy =. 'y' findx} ]. (As you could also use findx =. I.#:('x' = ]) or just findx =. [:I. 'x'=].)
This question occurred to my while solving this problem.
NB. Find the next number whose prime factorization exponents
NB. match those of the given number.
exps=. /:~#{:#(__&q:)
f=. 3 : 0
target=. exps y
(>:^:(-.#(target-:exps))^:_) y+1
)
f 20 NB. 28
Note that in order to specify the while condition of the Do... While, I first had calculate the prime exponents of the argument y and save that answer to target. I was then able to write -.#(target-:exps) as the While condition.
This of course breaks the tacit style. So I'd like to know if there is a way to achieve the same thing that my verb above achieves, but do so as a single tacit verb?
The way I approached this was to think of f as the centre of a dyadic fork where the left argument is exps y which is the unchanging comparison target and the right argument is >: y which does the initial incrementing. The next step was to use ] at each ^: in f to keep exps monadic. The [ pulls in exps y from the left argument.
Written in tacit
exps=. /:~#{:#(__&q:)
ft=: exps >:#]^:([ -.#-: exps#])^:_ >:
ft 20
28
It's cool that 3 * 4 results in 12, and * 4 results in 1, but does using the same primitive for both operations ever provide a benefit? For example, let's say I were to define the following:
SIGNUM =: * : [:
TIMES =: [: : *
If I were to only ever use SIGNUM and TIMES instead of *, would I ever miss out on a clever use of *? That is, x TIMES y seems to be exactly the same as x * y for every x I can imagine (although my imagination is pretty limited in this regard). Is there an x where x * y produces the same result as SIGNUM y?
In case * : [: isn't immediately clear, the following should illustrate:
SIGNUM =: * : [:
TIMES =: [: : *
SIGNUM 4
1
3 TIMES 4
12
* 4
1
3 * 4
12
3 SIGNUM 4
|domain error: SIGNUM
| 3 SIGNUM 4
TIMES 4
|domain error: TIMES
| TIMES 4
Let's write conclusions from the comments down:
There is no direct language-level reason not to use names for primitives
Using names instead of primitives can however harm performance, as special code does not necessarily get triggered. I think this can be remedied by fixing verbs after building them with f..
The reason for having the same name for monadic and dyadic verbs is historical: APL used it before. Most verbs have a related actions in monadic / dyadic versions and inflections (a number of trailing dots and colons).
For instance, ^ can be expressed in traditional notation as pow(x,y) or exp(y) where x and y are left and right arguments, and e is Euler's constant. Here, the monadic version is the same as the dyadic version, with a sensible default left argument. Different inflections of the same root are all power-related verbs:
- ^. does logarithms (base e for the monad)
- ^: does Power conjunction, applying a verb a variable number of times.
Other relations between monadic and dyadic verbs can also exist, for example $ can be said to get or set the Shape of an array, depending on whether it is used as monad or dyad.
That said, I think that once one gets a bit of experience with J, it becomes easier to spot which valence a verb has based on the sentence it is used in. Examples are:
Monad # Ambiv NB. Mv is always used monadically, Av depends on arguments
Ambiv & Monad
(Dyad Monad) NB. A hook, where verb 1 is always dyadic
(Ambiv Dyad Ambiv) NB. A fork, the middle is one always dyadic
It was probably a mistake to use the same symbols for dyadic and monadic built-ins except for those where the monadic case is a default parameter to the dyad.
TIMES =: 1&$: : *
would be a good defnition that doesn't give an error.
As for ambivalent cases,
(3 * TIMES) 4
12
2 (3 * TIMES) 4
24
Another useful ambivalent verb is:
TIMESORSQUARE =: *~
*~ 3
9
2 *~ 3
6
In response to the question of FoldList like primitive in J, I wanted to create an adverb fold so that x u fold y is to fold y with verb u and inital value x:
fold =: 2 : 0
z =.x
for_item. y do. z =. z u item end.
z
)
But I got error when trying it out:
1 (+fold) 1 2 3
|value error: x
| z=. x
what's wrong here? thanks.
Just a couple small things.
First, the numeric code for an adverb is 1. The 2 : 0 you have is defining a conjunction, not an adverb. The way it stands now, J is expecting two direct arguments to fold, and you've only provided one (the +; the two numeric arrays are indirect, not direct, arguments). However, that's not what J is complaining about here, because the other issue is actually tripping it up first. I'll get to that in a second, but nevertheless the first thing you need to do is define fold as an adverb [1].
The more immediate issue that J is complaining about is that it doesn't know what you mean by x. Why? For the same reason that it would if you replaced 2 : 0 (or conjunction define) -- or even, more pertinently, adverb define -- with verb define. Because explicit verbs (direct or derived) are monadic by default and have no x argument (hence mentioning x is a value error). If you want to define a dyadic verb, you must ask for it explicitly.
Now, defining a dyadic verb directly is straightforward: instead of saying verb define, you simply say dyad define. But deriving a dyadic verb from a modifier (adverb or conjunction) is a little less obvious. You must use the special colon syntax which allows you to separate the monadic and dyadic valences of explicit definitions. This syntax applies to all explicit definitions, including verbs, adverbs, and conjunctions, but for adverbs and conjunctions it is the only way to derive an explicit verb.
Bottom line:
fold =: adverb define
NB. Note solitary colon on next line. Everything after that is dyadic.
:
z =.x
for_item. y do. z =. z u item end.
z
)
[1]: You may find using the standard covers for nameclasses easier to remember (and read later), as in adverb define and conjunction define (for one-liners, you can use def in place of define).