Conversation
.metta files are UTF-8 by convention, but filereader.pl reads them with read_file_to_string/3 and no explicit encoding, so SWI-Prolog falls back to the `encoding` flag - which is derived from the ambient locale. Where that locale is not UTF-8, multi-byte characters in string literals and symbol names are decoded in the locale codepage and each silently becomes several wrong characters. In the common case there is no error; the program just runs with corrupted strings. This is not Windows-specific. It reproduces under any non-UTF-8 locale, including the C/POSIX locale that minimal container images, CI runners and systemd units commonly have. Reproduced with a UTF-8 file containing !(println! "café naïve — em dash"), whose correct length is 35 characters: Linux, LANG=C.UTF-8 encoding=utf8 35 correct Linux, LANG=C encoding=text 24 Illegal multibyte Sequence Windows 11 en-US encoding=text 39 Setting the flag in main.pl, before anything opens a stream, makes source loading deterministic rather than environment-dependent. It is a no-op where the locale is already UTF-8: verified in a swipl:10.0.2 container that under LANG=C.UTF-8 output bytes are byte-identical with and without the change, so test.sh's grep for the pass/fail markers is unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
.mettasource files are UTF-8 by convention — that is what editors produce — but PeTTa reads them without specifying an encoding, so SWI-Prolog falls back to theencodingflag, which is derived from the ambient locale:Where the locale is not UTF-8, multi-byte characters in string literals or symbol names are decoded in the locale codepage instead, and each one silently becomes several wrong characters. In the common case there is no error — the program simply runs with corrupted strings.
This is not Windows-specific. It reproduces under any non-UTF-8 locale, including the
C/POSIX locale that minimal container images, CI runners and systemd units commonly have.Reproduction
A
.mettafile saved as UTF-8 containing:The correct length of that string is 35 characters.
current_prolog_flag(encoding, E)LANG=C.UTF-8utf8LANG=CtextIllegal multibyte Sequencetext(cp1252)Minimal repro on Linux, no Windows required:
(21 is correct for that shorter one-line file; 35 is the longer example in the table.)
Why this is worth fixing rather than documenting
The same locale default applies to
open/3, so files written by PeTTa and files written by anything else disagree about encoding. In a project that uses PeTTa's Python interop, this surfaces as a hard failure in one direction and silent corruption in the other:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x97I hit the first of these in production. An append-only
.mettalog accumulated lone cp1252 bytes from one writer, and every subsequent load truncated at the first bad byte — the only visible symptom being that older entries had "disappeared".Fix
One directive at the top of
src/main.pl, before anything opens a stream:Compatibility
This is a no-op where the locale is already UTF-8, which includes this repo's CI.
I specifically checked that it cannot disturb
test.sh, which greps stdout for the✅/❌markers. In aswipl:10.0.2container, reading a.mettafile containing❌and writing it back out:C.UTF-8CUnder
C.UTF-8the output bytes are identical with and without the patch, so the harness is unaffected. UnderLANG=Cthe markers already fail to round-trip today; this change neither fixes nor worsens that.One honest behaviour change: a pre-existing non-UTF-8
.mettafile now warns (Illegal UTF-8 continuation) instead of silently mis-decoding. I would argue that is the better failure, but it is a change.Alternative, if you prefer a narrower diff
Passing the encoding explicitly at the single read site:
I verified this also yields the correct 35. It fixes source loading without touching a global flag, but leaves
open/3inlib/lib_import.pland any Python interop still locale-dependent. Happy to switch to this form, or to set the flag inmetta.plinstead ofmain.plif PeTTa is expected to be loaded as a library rather than through its CLI entry point — just say which you prefer.Testing
swipl:10.0.2container under bothC.UTF-8andClocales.examples/roman_test.mettaproduces identical output with and without the change on my machine. It does report one pre-existing failure there (map-flat (partial + (1)) (1 2 3)) which reproduces on unpatchedmainas well — unrelated to this change, but mentioning it in case it is news.