Not entirely sure this is the right term. Summary is that there are several occasions where want to capture the truth value of a goal without failing. Examples are execution in a thread or on a remote server. If the remote call fails you want it to report this rather than failing and not sending anything back. For this purpose, I often define a predicate like
reify(Goal, Result) :-
( catch(Goal, E, true)
-> ( var(E)
-> Result = true
; Result = error(E)
; Result = false
This works fine. Of course you also want to turn this into normal true/false/error, e.g.
Maybe the problem can be reduced by having the first predicate unify Result with throw(E) instead of error(E), thus making the second predicate equivalent to call/1.
Taking this a step further, we can think of the first predicate as mapping concrete goals to canonically simplified goals that have the same “result”. This perspective suggests calling it something like goal_canonical/2, although I’m not sure that’s any clearer than reify/2…
That surely has the advantage that we do not have to invent a name When used to deal with remote connections though it offers a bit of a security issue It made me think that many dynamically typed garbage collected programming languages are considered “safe” as one cannot get trapped by the usual memory management issues. Most of them provide some mechanism to evaluate a string or make a call based on some data structure (similar to Prolog call/N). I’ve seen plenty of examples of people using that in server applications for handling requests or responses from clients. Not a good idea …
That is what the toplevel does: it prints a program consisting of =/2 conjunctions and disjunctions that produce the same result. That includes failure, but not exceptions (it would have to write throw(...) rather than a human readable error message). I wouldn’t call that canonical. More partial evaluation: given the context you create a simpler program.
I’m getting worried that the problem is similar to delete/3: there are simply too many scenarios one may one to handle using this type of “reification” (if that is the right term). As shows, provide the bindings through the original instantiated goal, provide the bindings as a reduced template that only contains the free variables in Goal, deal with non-determinism, etc.
I often think of that as mapping a non-Boolean type into Boolean type or error type. As you note, when writing code that interacts with code that returns non-Boolean results and needing a Boolean result so that the use of the non-Boolean code appears as a predicate such is often used. To this day the word reify is one of those terms I don’t really understand, just like closure, I have to read it in context to really understand its meaning. Personally I like the predicate but do not like the name for it; reify. The name alone is enough to make me avoid using it if I did not already know it.
When looking at both predicates, reify/2 and unreify/1, and then mentally adding the types the code makes even more sense to me. With the types mentally added and looking at the definition of reify it seems that reify is too broad as it is mapping into just a Boolean or error type, so maybe the name reify_boolean or reify_predicate is closer to what makes sense but I don’t like them either.
reify/2 seems a specific case of typing and some of my code modules are loaded with tens of typing predicates just for each abstract type in the module. When the term contains a list of types and then one needs to walk the list one creates many more predicates that then simplify down to predicates like
so reify seems quite appropriate to me, the “abstract idea” being success, failure, or error, and the “exlicit data model” being one of true, false and throw(Err) (using @oskardrums suggestion). Not sure what this has to do with types.
I think implicit unification when Result = true might just be the tip of the iceberg. What about other backtrackable side effects (global variables, setarg). These don’t really matter at the top level (just outputs a list of unifications) but may matter in a programming context. Probably simplest to just treat reify(Goal,true) as equivalent to call(Goal) in the absence of errors. (Also seems easier to explain.) In fact, I would use reify(Goal,true) as an elegant way (compared to catch/3) of turning errors into failures.
Unless there’s more to it than this, why wouldn’t you just make reify/2 a system meta-predicate like call.
reify/2 is based on catch/3 :). I don’t see a big different between reify(Goal, true) and catch(Goal, _, fail) although the first looks a little prettier.
Thanks for justifying the name. I’m reluctant to add stuff like this as a system predicate (unless it gets many likes ). Just adding a library with one predicate of just a few lines seems wrong as well. Best seems to find a library where it makes sense or add a new library of we see we can define more similar predicates.
So far library(error) and library(apply) seems the only two remotely related libraries we have.
My point exactly. And as I’m not particularly fond of errors, I like to bury them.
Not a huge deal, but “un-reification” is just call and true, false and throw are all system predicates. So it feels to me more like a system level thing rather than anything in the libraries you mentioned (" A.3 library(apply): Apply predicates on a list" and " A.16 library(error): Error generating support").
IMO, if it isn’t a system predicate why not just leave it up to the application; it’s simple enough. But it would make a good example for " 4.10 Exception handling" documentation.
Actually the biggest problem is the reverse name I’ve done a little work on trying to turn this into a library that can also deal with nondet scenarios and scenarios where the execution happens in a remote environment (thread, process), so we must also include the bindings in the result. You get on the client side something like
read result, re-read on backtracking. handle bindings and failure/exceptions.
And the server does
Send result, check whether client wants next.
That smells a lot like Pengines Just, more lightweight, Prolog ↔ Prolog only and abstracting from the transport using a primitive that can get a Prolog term to the other side.
A few days ago when reading the first post and sleeping on it, it reminded me of Railway-Oriented-Programming which I mentioned a while ago, the concept is common among many programming systems and noticeable once understood.
So with regards to a name, as much as I hate to say this, maybe an operator or a few operators would be better.
Ran across the following in my morning current events review and noting it here because much of what it talks about has connections with what I think Jan W. is trying to create, I could be wrong.
Recently, I have stumbled on a use case of reify/2. However, the current implementation provided by Jan has one drawback for me. It cuts all choice points inside the reified goal if the goal succeed with choice points. In my opinion, a more correct logical implementation would keep all choice points of the reified goal and indicate a failed reified goal only if the reified goal has never succeeded. I found out that I can implement reify as I want using the deterministic/1 predicate:
I’m sorry, I don’t really understand which variable I should be comparing or the use of the lookup_other predicate.
However, from your stack overflow link, I found the soft cut operator *-> which seems to do what I want in this case:
reify2(Goal, Result) :-
*-> Result = true
; Result = false
Do you see any drawback from this soft cut operator ?
Need to compare variables using ==, to know when alternates can be safely avoided, for determinism.
The variables in your code are the a and b in:
ab(a) --> [a].
ab(b) --> [b].
And the thing to reify is whether a and b are in the DCG list.
Take a look at my code. The == comparison goes on to perform a cut, because == means that e.g. it’s a ground a and therefore cannot be b, so we can add some nice determinism and prevent the comparison with b.
Or, if it’s e.g. var, then it can be either a or b, or something else (using dif/2 to prevent matching a or b).
The “other”, in your simple case, can be unification with  to result in the end of the DCG list. For your simple example,  can replace the usage of dif.
pack(stoics_lib) has holds/2 with similar semantics.
It only returns (also takes) true/false as the second argument as the scenarios
it is used is for indexing clauses of subsequent called predicates.
And it is called within a single SWI environment so errors are as per usual.
As you ld expect I think holds/2 is a better name for my use case- yours is a bit more general.