Scryer-prolog: Does Scryer need a `when`?

Created on 9 Nov 2020  路  3Comments  路  Source: mthom/scryer-prolog

Hello everyone!
I am a long-time programmer but a relative newcomer in Prolog.

To learn myself Prolog I am trying to create a couple of simple example programs, the latest one being an implementation to perform a simple Caesar substitution cypher in a nicely general/logically pure way.

However, I encountered a problem:

A substitution cypher works by shifting all letters by a certain integer shift (looping around z -> a). Decryption is shifting the same amount but in the other direction. The way to specify this as a logical relation on characters pretty much requires you to treat them as numbers (e.g. ASCII codepoints).

Scryer of course uses the (default; but for good reason, let's not change it) double_quotes(chars)-setting, meaning that a string is indistinguishable from a list in which each character is an atom.

Conversion between a character in this form and its ASCII equivalent can of course be done using the builtin predicate char_code(Char, Code).

The problem lies in the fact that this predicate will fail if neither parameter is ground yet:
On encryption, we are able to perform the conversion for the plaintext character, but not yet for the cyphertext character (since we'll know its character code only after the main shifting logic has been performed!).

  • For decryption it is the other way around: we know the cyphertext character, but not yet the plaintext code.
  • This means that if we want the shift_plainchar_cypherchar/3 predicate to work in both directions (encryption and decryption), we need to delay the call of the appropriate char_code until one of the two of the variables is bound!

In SWI-Prolog, this would be the moment I'd use when to express this
(as e.g. when(nonvar(PlainChar) ; nonvar(PlainCode), char_code(PlainChar, PlainCode)).

Unfortunately when does not (yet?) exist in Scryer-Prolog, so I finally resorted to the following hack:

shift_plainchar_cypherchar(Shift, PlainChar, CypherChar) :-
    (nonvar(PlainChar) -> char_code(PlainChar, PlainCode) ; true),
    (nonvar(CypherChar) -> char_code(CypherChar, CypherCode) ; true),

    % ... cipher logic in the middle.

    char_code(PlainChar, PlainCode),
    char_code(CypherChar, CypherCode).

which seems like a rather suboptimal way of writing things (difficult to read because related things are happening in multiple places, repeated evaluation of predicates, arguably not really declarative since it is very order-dependent).

Full code in this gist.


Thank you for reading my long preamble. Now for my question:

  • Is when/2 a feature that should be added to Scryer-Prolog?
  • Or is there an alternative way to resolve the unfortunate situation described above?

I'm still very much a Prolog greenhorn, so it is rather likely that I have missed something obvious. I'm also very happy with any tips/improvements or other feedback you might be willing to give. (If you have the time, that is; don't feel obliged to do so please!)

Most helpful comment

In Scryer or in general?

Currently, there is a bunch of dif/2 goals, one for each alternate value, which is created incrementally by memberd_t everywhere. All of this is not strictly necessary. But optimizations do cost a lot of development time. And it is not even sure that this case is worth optimizing. Probably a good implementation of If -> Then ; Else is all what is needed in Scryer. This covers the usual mode.

All 3 comments

1mo, there is freeze/2 and I agree that when/2 and iwhen/2 would be nice to have. I am not that sure whether it should be supported efficiently (that is, comparable in efficiency to the block declarations in SICStus). That would be too many steps ahead. Currently.

2do,

shift_plainchar_cipherchar(Shift, PCh, CCh) :-
   if_( memberd_t(PCh, "abcdefghijklmnopqrstuvwxyz"),
      char_shift_offset_to_cypherchar(PCh, Shift, 0'a, CCh),
      if_( memberd_t(PCh, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
         char_shift_offset_to_cypherchar(PCh, Shift, 0'A, CCh),
         CCh = PCh
      )
   ).

Clearly, a reifiable nth/3 would be preferable. And, certainly, a better implementation of the else branch would be nice. I think this is not realistic to expect from anyone currently.

@UWN thank you for your response!

It seems from the StackOverflow-answer you linked to like iwhen/2 might be implemented on top of any ISO-conforming Prolog (even without coroutining constructs), but I don't think that I am fully grasping its intended semantics/use yet.
Do you know if there are any resources with more information on the origins of when/2, and iwhen/2?

I am not that sure whether it should be supported efficiently. [...] That would be too many steps ahead.

Yes, probably. :slightly_smiling_face:

And, certainly, a better implementation of the else branch would be nice. I think this is not realistic to expect from anyone currently.

In Scryer or in general?

In Scryer or in general?

Currently, there is a bunch of dif/2 goals, one for each alternate value, which is created incrementally by memberd_t everywhere. All of this is not strictly necessary. But optimizations do cost a lot of development time. And it is not even sure that this case is worth optimizing. Probably a good implementation of If -> Then ; Else is all what is needed in Scryer. This covers the usual mode.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

UWN picture UWN  路  3Comments

notoria picture notoria  路  3Comments

notoria picture notoria  路  4Comments

XVilka picture XVilka  路  3Comments

XVilka picture XVilka  路  3Comments