I've built an autonomous Roc agent that uses claude to write Roc and fix it's own errors and test failures, it's implemented in Roc too!
Here you can see the process of writing and fixing a Roc function to find the longest palindromic substring:
INFO: Prompt:
Add a longest palindromic substring function to this Roc code, add tests using expect
.
That function should do the following:
Given a string s, find the longest palindromic substring in s. A palindrome is a string that reads the same backward as forward.
Roc instructions:
Nat
, use a type like U64
or I64
instead.head :: tail
, use [head, .. as tail]
instead. Make sure you do not forget the as
!increment = \n -> n + 1
Extra instructions:
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
import pf.Stdout
main =
Stdout.line! "Hello, World!"
expect 5 == 5
INFO: Asking Claude...
INFO: Claude's reply:
Here's the implementation of the longest palindromic substring function with tests:
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
import pf.Stdout
main =
Stdout.line! "Hello, World!"
longestPalindromicSubstring = \str ->
if Str.isEmpty str then
""
else
strLen = Str.countGraphemes str
maxLen = strLen
findLongest = \len, start ->
if len == 0 then
""
else
checkAllStarts = \currentStart ->
if currentStart + len > strLen then
findLongest (len - 1) 0
else
substring = Str.slice str { startAt: currentStart, len }
if isPalindrome substring then
substring
else
checkAllStarts (currentStart + 1)
checkAllStarts start
findLongest maxLen 0
isPalindrome = \str ->
reversed = str |> Str.graphemes |> List.reverse |> Str.joinWith ""
str == reversed
expect longestPalindromicSubstring "babad" == "bab"
expect longestPalindromicSubstring "cbbd" == "bb"
expect longestPalindromicSubstring "a" == "a"
expect longestPalindromicSubstring "" == ""
expect longestPalindromicSubstring "ac" == "a"
expect longestPalindromicSubstring "racecar" == "racecar"
expect isPalindrome "racecar" == Bool.true
expect isPalindrome "hello" == Bool.false
END
"
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose countGraphemes
:
12│ strLen = Str.countGraphemes str
^^^^^^^^^^^^^^^^^^
Did you mean one of these?
Str.countUtf8Bytes
Str.contains
Str.concat
Str.joinWith
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose slice
:
22│ substring = Str.slice str { startAt: currentStart, len }
^^^^^^^^^
Did you mean one of these?
Str.splitOn
Str.trim
Str.toDec
Str.concat
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose graphemes
:
33│ reversed = str |> Str.graphemes |> List.reverse |> Str.joinWith ""
^^^^^^^^^^^^^
Did you mean one of these?
Str.repeat
Str.trim
Str.reserve
Str.capacity
────────────────────────────────────────────────────────────────────────────────
3 errors and 0 warnings found in 21 ms
"
INFO: roc check or roc test failed.
(CmdError (ExitCode -99))
INFO: Prompt:
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose countGraphemes
:
12│ strLen = Str.countGraphemes str
^^^^^^^^^^^^^^^^^^
Did you mean one of these?
Str.countUtf8Bytes
Str.contains
Str.concat
Str.joinWith
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose slice
:
22│ substring = Str.slice str { startAt: currentStart, len }
^^^^^^^^^
Did you mean one of these?
Str.splitOn
Str.trim
Str.toDec
Str.concat
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose graphemes
:
33│ reversed = str |> Str.graphemes |> List.reverse |> Str.joinWith ""
^^^^^^^^^^^^^
Did you mean one of these?
Str.repeat
Str.trim
Str.reserve
Str.capacity
────────────────────────────────────────────────────────────────────────────────
3 errors and 0 warnings found in 21 ms
INFO: Asking Claude...
INFO: Claude's reply:
Analysis: It seems the Str module in Roc has different method names than what I used. Here's how we can fix it:
countGraphemes
, we can use Str.len
slice
, we can use string indexing with Str.fromUtf8
and Str.toUtf8
graphemes
, we can convert the string to UTF-8 bytes and work with thoseHere's the corrected code:
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
import pf.Stdout
main =
Stdout.line! "Hello, World!"
longestPalindromicSubstring = \str ->
if Str.isEmpty str then
""
else
strLen = Str.len str
maxLen = strLen
findLongest = \len, start ->
if len == 0 then
""
else
checkAllStarts = \currentStart ->
if currentStart + len > strLen then
findLongest (len - 1) 0
else
substring = getSubstring str currentStart len
if isPalindrome substring then
substring
else
checkAllStarts (currentStart + 1)
checkAllStarts start
findLongest maxLen 0
getSubstring = \str, start, len ->
bytes = Str.toUtf8 str
result = List.sublist bytes { start, len }
when Str.fromUtf8 result is
Ok s -> s
Err _ -> ""
isPalindrome = \str ->
bytes = Str.toUtf8 str
reversed = List.reverse bytes
Str.fromUtf8 bytes == Str.fromUtf8 reversed
expect longestPalindromicSubstring "babad" == "bab"
expect longestPalindromicSubstring "cbbd" == "bb"
expect longestPalindromicSubstring "a" == "a"
expect longestPalindromicSubstring "" == ""
expect longestPalindromicSubstring "ac" == "a"
expect longestPalindromicSubstring "racecar" == "racecar"
expect isPalindrome "racecar" == Bool.true
expect isPalindrome "hello" == Bool.false
END
"
── NOT EXPOSED in main_claude.roc ──────────────────────────────────────────────
The Str module does not expose len
:
12│ strLen = Str.len str
^^^^^^^
Did you mean one of these?
Str.trim
Str.toDec
Str.toU8
Str.toI8
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warnings found in 18 ms
"
INFO: roc check or roc test failed.
INFO: Asking Claude...
INFO: Claude's reply:
Analysis: It seems we need to use a different way to get the string length. In Roc, we can use Str.toUtf8
to convert the string to bytes and then use List.len
to get its length.
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
import pf.Stdout
main =
Stdout.line! "Hello, World!"
longestPalindromicSubstring = \str ->
if Str.isEmpty str then
""
else
strLen = str |> Str.toUtf8 |> List.len
maxLen = strLen
findLongest = \len, start ->
if len == 0 then
""
else
checkAllStarts = \currentStart ->
if currentStart + len > strLen then
findLongest (len - 1) 0
else
substring = getSubstring str currentStart len
if isPalindrome substring then
substring
else
checkAllStarts (currentStart + 1)
checkAllStarts start
findLongest maxLen 0
getSubstring = \str, start, len ->
bytes = Str.toUtf8 str
result = List.sublist bytes { start, len }
when Str.fromUtf8 result is
Ok s -> s
Err _ -> ""
isPalindrome = \str ->
bytes = Str.toUtf8 str
reversed = List.reverse bytes
Str.fromUtf8 bytes == Str.fromUtf8 reversed
expect longestPalindromicSubstring "babad" == "bab"
expect longestPalindromicSubstring "cbbd" == "bb"
expect longestPalindromicSubstring "a" == "a"
expect longestPalindromicSubstring "" == ""
expect longestPalindromicSubstring "ac" == "a"
expect longestPalindromicSubstring "racecar" == "racecar"
expect isPalindrome "racecar" == Bool.true
expect isPalindrome "hello" == Bool.false
END
"0 errors and 0 warnings found in 22 ms
0 failed and 8 passed in 90 ms.
"
One thing I thought was cool: it tried to use Str.countGraphemes
first, next it tried Str.len
and after that it realized it had to use Str.toUtf8
followed by List.len
. I did not tell it that Str.toUtf8
existed, it got that from its training data.
Source code:
roc-agent-claude.roc
prompt-palindrome.txt
Also, sed helper script because regex:
removeColorCodes.sh
Now I can catch up on advent of code :big_smile:
By the way; the agent still works if you use the cheaper (and less capable) haiku 3.5 model
This is very cool!
Very cool!
Looks like I can set up zed to do everything that roc agent does :eyes:
https://zed.dev/blog/fastest-ai-code-editor
Last updated: Jul 06 2025 at 12:14 UTC