OCaml tests failing with segfault on site, but pass locally

All of my submitted iterations for atbash-cipher on the ocaml track are failing with

File "dune", line 5, characters 0-76:
5 | (alias
6 |   (name    runtest)
7 |   (deps    (:x test.exe))
8 |   (action  (run %{x})))
Command got signal SEGV.
make: *** [Makefile:4: test] Error 1

make run locally works fine. Here’s my latest code in atbash.ml:

open Base

let atbash ch = Char.(if is_alpha ch
  then of_int (to_int 'z' - to_int ch + to_int 'a')
  else if is_digit ch then Some ch else None)

let encode ?(block_size=5) s =
  let rec chunk s = if String.length s <= block_size then [s] else
    (String.prefix s block_size) :: (chunk (String.drop_prefix s block_size))
  in String.lowercase s |> String.filter_map ~f:atbash |> chunk |> String.concat ~sep:" "

let decode s = String.filter_map s ~f:atbash

I was able to submit a solution just now to the ocaml track ‘bowling’ exercise without any trouble, so it’s not impacting all exercises.

I don’t know this language or the test runner. Two things you can try to help a person who does know these is to:

  1. Run this example solution which should work. Let us know if it does
open Base

let substitute = function
  | 'a' .. 'z' as c ->
    let offset = (Char.to_int c) - (Char.to_int 'a') in
    let code = (Char.to_int 'z') - offset in
    Char.of_int_exn code
  | other -> other

let is_encodable = function
  | 'a' .. 'z' | '0' .. '9' -> true
  | _ -> false

let explode s =
  let rec expl i l =
    if i < 0 then
      l
    else
      expl (i - 1) (s.[i] :: l)
  in
  expl ((String.length s) - 1) []

let implode l =
  let character_at i = List.nth_exn l i in
  String.init (List.length l) ~f:character_at

let rec group n l =
  if (List.length l) <= n then
    [l]
  else
    (List.take l n) :: (group n (List.drop l n))

let encode ?block_size:(block_size = 5) text =
  let lowercase_text = String.lowercase text in
  let characters = explode lowercase_text in
  let filtered_characters = List.filter ~f:is_encodable characters in
  let groups = group block_size filtered_characters in
  let preprocessed_texts = List.map ~f:implode groups in
  let texts = List.map ~f:(String.map ~f:substitute) preprocessed_texts in
  String.concat ~sep:" " texts

let decode text =
  let lowercase_text = String.lowercase text in
  let characters = explode lowercase_text in
  let filtered_characters = List.filter ~f:is_encodable characters in
  let preprocessed_text = implode filtered_characters in
  String.map ~f:substitute preprocessed_text
  1. If it does, let us know what OCaml version you are using locally.
1 Like

That solution does pass tests when I copy paste it into the online editor.

Locally:

ocaml --version
The OCaml toplevel, version 5.2.1

I see the docs recommend 5.1.1, I’ll try that later: Installing OCaml locally | Exercism's Docs

1 Like

The problem is with String.filter_map but I don’t know why. Replacing

String.filter_map s ~f:atbash

with

String.to_list s |> List.filter_map ~f:atbash |> String.of_list

in both locations caused everything to work.

1 Like

So String.filter_map works on 5.2.1, but not 5.1.1? Is it possible filter_map was only added to the String module after 5.1.1? It seems a little strange to be honest they would add this method at all given you already have List.filter_map available.

1 Like

I cannot find this function in this API ref, nor in the type ref or any other place. The only sorta-matching reference is in StringMap, which, if I read the docs correctly, you need to make first. Other options are extra packages that seem to supply it.

  • this function doesn’t exist in the standard library and you are importing it from somewhere locally?
  • the doc are wrong?
  • my understanding is wrong?
  • 5.2.1 accidentally makes this available somewhere?
1 Like

I was confused by this too and looked further into this. The Base import for OCaml exercises on Exercism is a standard library replacement by Jane Street. String.filter_map is provided by Base here and is a name the test runner “knows” because switching it to String.filter_maap results in a different error as an unbound name.

Running make locally would run the same tests so I’m not sure what to say here. My gut was saying there might be a bug in the atbash function, but the tests would have presumably failed locally.

CCing @georgyo @kahgoh since they’re the OCaml track maintainers

1 Like

It was not present in 0.15.1 and became available in 0.16.0.

I can imagine that this is relevant, however I could not find any mention of pin, hold, or opam-lock in the test-runner, which would (hopefully?) have created a lock-like file which we could then check. 0.16.0 came out 2 years ago so I doubt this is the actual issue but :man_shrugging: .

Hopefully Kah or George can answer why this is happeneing :slight_smile:

1 Like

The version in the test runner is 0.16.3. I ended up pulling down the test runner image from Docker and checking it inside the container:

20582789ad59:~# opam list --installed
[WARNING] Running as root is not recommended
# Packages matching: installed
# Name                # Installed # Synopsis
base                  v0.16.3     Full standard library replacement for OCaml
base-bigarray         base
base-domains          base
base-nnp              base        Naked pointers prohibited in the OCaml heap
base-threads          base
base-unix             base

According to the docs, the latest version is v0.17.3. I’ll do some further testing between these two versions later tonight to see if this was something that changed between the two versions.

2 Likes

Interesting … I’ve just tested. I was able to reproduce @kevinastock’s issue with the test runner image and v0.16.3. However, I wasn’t able to when I tried base v0.17.3 (both times with OCaml 5.1).

I’m guessing something must’ve changed in the library. I had a look at the changes, but I can’t tell which one would have caused this difference.

I’m planning to look at updating the test runner later in the week.

4 Likes

@kevinastock OCaml has been updated to 5.4 and the Base library is now at 0.17.3. Your atbash-cipher solution should now work with the online submission.

3 Likes