Stream: beginners

Topic: Closest language to roc?


view this post on Zulip itmuckel (Mar 25 2023 at 09:58):

Hi! I thought I learn a functional language close to roc to get an idea of how to use the language to write stuff without using roc itself, because there is little documentation about how to use the language so far (and because there is no language server yet :nerd: ) What languages can I look at that are probably easy to translate to roc? I thought about Elm, but maybe it's too limiting? PureScript seems viable, although it has a few different syntax decisions, it seems to have similar effects systems.

My background: I tried to learn Haskell a few times, but I never reached the point where I could write applications that would actually do something useful. I once reached the point where I understood that there's no point in understanding Monads, because each Monad does a different thing. Then I saw that there are different solutions to combining different Monads and was lost in the way from understanding the basics of the language to writing a program that actually does something useful, which is really unfortunate. So PureScript 1. seems really close to Haskell and to have all the features that roc also plans to have 2. the book seems to have a plan for going from the basic to writing applications with Effects and doesn't stop somewhere in the middle and leaves you hanging. So before I invest a lot of time in a language I only learn to later write non-web-apps in roc, would you recommend a different learning path (you, who maybe went through similar learning paths)?

view this post on Zulip Luke Boswell (Mar 25 2023 at 10:07):

I started using Roc by going through the Advent of Code challenges. I found that to be fun, and the community here was very helpful when I ran into issues. I would recommend that for anyone interested in learning more about Roc. It was really helpfulto see other peoples approaches to the same problems. :big_smile:

view this post on Zulip Luke Boswell (Mar 25 2023 at 10:15):

The good thing about AoC I find is that they cover a broad range of problems, so you can learn lots of different things. The problems are careful to keep the scope focussed, but still interesting. The difficulty scales pretty nicely too, so there is something there for everyone. I think Roc is a great language and these problems really highlight just how nice it is to use. I recall modelling using Tags, no type annotations, and testing using expect being low friction which made it easy to be productive and solve the problem, but still having the comfort of the compiler and type checking to catch issues.

view this post on Zulip Brian Carroll (Mar 25 2023 at 10:42):

To directly answer the question, Roc is described as a direct descendant of Elm, so that's definitely the closest. PureScript and Haskell are usually considered harder to learn, with more focus concepts and less on just building stuff.

Regarding Elm possibly being too limiting: The main difference is what kind of IO or effects you can do, because it's very front end focused. Pure logic is very similar between the two.

But if your goal is to learn Roc, it might be less confusing to just focus on Roc alone, by looking at AoC solutions, as Luke suggested!

view this post on Zulip itmuckel (Mar 25 2023 at 11:14):

Do you have an example repository with the necessary setup? Like which platform to use, how to read files?

view this post on Zulip Brendan Hansknecht (Mar 25 2023 at 16:07):

Many people went through AOC last year and there is a lot of links and discussion in #Advent of Code. The examples may be slightly out of date due to Roc changing, but I think it should be relatively minor. I think essentially everyone uses the basic-cli platform. This is my day1 part1, for example. I think to make it work, it would just require updating the platform path to: https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br

view this post on Zulip Luke Boswell (Mar 25 2023 at 17:36):

@itmuckel I updated my AoC 2022 examples and checked they all compile with the latest version of Roc. These might help you if are looking for inspiration - for at least one way to solve the problems. :big_smile:

view this post on Zulip Luke Boswell (Mar 25 2023 at 17:50):

I was trying to learn how to do parsing in Roc around that time, and I set myself the challenge of trying to parse the input files. So some of the early examples are probably overkill, some simple string splitting around \n characters would have been quicker.

view this post on Zulip itmuckel (Mar 25 2023 at 18:49):

Great, thank you all!

view this post on Zulip itmuckel (Mar 26 2023 at 18:04):

Let me just reuse this thread:

    task =
        contents <- File.readUtf8 path |> await
        chunks <- (Str.split contents "\n")
        Stdout.line contents

I get the error

── TOO MANY ARGS ──────────────────────────────────────────────────── day1.roc ─

The split function expects 2 arguments, but it got 3 instead:

20│          chunks <- (Str.split contents "\n")
                        ^^^^^^^^^

Are there any missing commas? Or missing parentheses?

But how? I even put it in parentheses and it still thinks it gets three arguments. Where is the third argument? :eyes:

view this post on Zulip itmuckel (Mar 26 2023 at 18:06):

Oooh, I get it now. backpassing creates a lambda that gets passed implicitly to Str.split, right?

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:19):

Yeah, backpassing is creating a lambda here.

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:20):

Kinda surprised it ignores parens and doesn't create a floating lambda, but same sort of problem either way.

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:20):

Should just need to change from <- to =

view this post on Zulip itmuckel (Mar 26 2023 at 18:32):

This one is really weird:

     task =
        contents <- File.readUtf8 path |> await
        chunks = (Str.split contents "\n")

        _ <- Stdout.line (List.first chunks |> Result.withDefault "err") |> await
        Stdout.line (Str.concat "list: " (List.first chunks |> Result.withDefault "err"))

The first Stdout.line doesn't output anything (not even my "Success" message that should appear after the program ran).
When I comment out the first Stdout.line, my output is

list: 1000
success

like I would expect. What's going on with Result.withDefault? If I'm using it wrong, shouldn't there be an error?

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:36):

Is the first line empty or just spaces?

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:37):

That's my only immediate guess.

view this post on Zulip itmuckel (Mar 26 2023 at 18:39):

No, it contains "1000", otherweise the second Stdout.line wouldn't output "list: 1000", right?

view this post on Zulip itmuckel (Mar 26 2023 at 18:42):

Maybe it runs into an error, but reports nothing?

    Task.attempt task \result ->
        when result is
            Ok {} -> Stdout.line "success"
            Err err ->
                msg =
                    when err is
                        FileWriteErr _ PermissionDenied -> "PermissionDenied"
                        FileWriteErr _ Unsupported -> "Unsupported"
                        FileWriteErr _ (Unrecognized _ other) -> other
                        FileReadErr _ _ -> "Error reading file"
                        _ -> "Uh oh, there was an error!"

                {} <- Stderr.line msg |> await
                Process.exit 1

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:42):

can you share the full program and the full output, starting from the $ roc run ... (or whatever you used to run it) command?

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:42):

that would help I think!

view this post on Zulip itmuckel (Mar 26 2023 at 18:43):

Oh, interesting, it segfaults :-D

list: 1000
Segmentation fault

view this post on Zulip itmuckel (Mar 26 2023 at 18:44):

Yes, I only mashed together a cli-example and read a file, here is the full program:

app "day1"
    packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
    imports [
        pf.Stdout,
        pf.Stderr,
        pf.Process,
        pf.Task.{ Task, await },
        pf.Arg,
        pf.File,
        pf.Path,
    ]
    provides [main] to pf


main : Task {} []
main =
    path = Path.fromStr "input-day-1.txt"
    task =
        contents <- File.readUtf8 path |> await
        chunks = (Str.split contents "\n")

        _ <- Stdout.line (Str.concat "list: " (List.first chunks |> Result.withDefault "err")) |> await
        Stdout.line (List.first chunks |> Result.withDefault "err")

    Task.attempt task \result ->
        when result is
            Ok {} -> Stdout.line "success"
            Err err ->
                msg =
                    when err is
                        FileWriteErr _ PermissionDenied -> "PermissionDenied"
                        FileWriteErr _ Unsupported -> "Unsupported"
                        FileWriteErr _ (Unrecognized _ other) -> other
                        FileReadErr _ _ -> "Error reading file"
                        _ -> "Uh oh, there was an error!"

                {} <- Stderr.line msg |> await
                Process.exit 1

view this post on Zulip itmuckel (Mar 26 2023 at 18:44):

input-day-1.txt is:

1000
2000
3000

4000

5000
6000

7000
8000
9000

10000

view this post on Zulip itmuckel (Mar 26 2023 at 18:45):

I originally started it with roc dev day1.roc and it doesn't show me that it segfaults, so I was asking here what was going on. roc build day1.roc creates an executable that tells me that it segfaults

view this post on Zulip itmuckel (Mar 26 2023 at 18:47):

environment: WSL2 with roc nightly pre-release, built from commit 41d7ade on Sa 25 Mär 2023 09:07:07 UTC

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 18:53):

Running on my machine prints both, but then kinda explodes at the end of the application run. no idea why:
https://gist.github.com/bhansconnect/bac5d18258987e6304a27671a12068e5

Maybe we recently had some sort of change or regression in basic-cli? I am quite confused by the output.

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:53):

seems likely to be a basic-cli problem

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:54):

like some sort of memory corruption issue

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:54):

could also be a builtin

view this post on Zulip Richard Feldman (Mar 26 2023 at 18:54):

valgrind might have some insight?

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 19:15):

==38947== Invalid read of size 1
==38947==    at 0x484D40A: memrchr (in /nix/store/xkz8yydj9kl6l9r9bl2vn4q8lmfh02ns-valgrind-3.20.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==38947==    by 0x96433C: memrchr_specific (memchr.rs:23)
==38947==    by 0x96433C: memrchr (memchr.rs:39)
==38947==    by 0x96433C: memrchr (memchr.rs:50)
==38947==    by 0x96433C: write_all<std::io::stdio::StdoutRaw> (linewritershim.rs:248)
==38947==    by 0x96433C: write_all<std::io::stdio::StdoutRaw> (linewriter.rs:206)
==38947==    by 0x96433C: <std::io::stdio::StdoutLock as std::io::Write>::write_all (stdio.rs:731)
==38947==    by 0x965BD0: <std::io::Write::write_fmt::Adapter<T> as core::fmt::Write>::write_str (mod.rs:1661)
==38947==    by 0x593280: <&T as core::fmt::Display>::fmt (mod.rs:2361)
==38947==    by 0x98D20B: core::fmt::write (mod.rs:1198)
==38947==    by 0x964203: write_fmt<std::io::stdio::StdoutLock> (mod.rs:1672)
==38947==    by 0x964203: <&std::io::stdio::Stdout as std::io::Write>::write_fmt (stdio.rs:711)
==38947==    by 0x964A03: write_fmt (stdio.rs:685)
==38947==    by 0x964A03: print_to<std::io::stdio::Stdout> (stdio.rs:1014)
==38947==    by 0x964A03: std::io::stdio::_print (stdio.rs:1027)
==38947==    by 0x25BBCE: roc_fx_stdoutLine (lib.rs:311)
==38947==    by 0x23D4C5: roc_fx_stdoutLine_fastcc_wrapper (in /home/bren077s/Projects/roc-misc/roc/examples/day1)
==38947==    by 0x236E49: Effect_effect_closure_stdoutLine_b7aa9f7d377b2692ada596045493ead6d491b934dc9015fcbdd1a8e01477d (in /home/bren077s/Projects/roc-misc/roc/examples/day1)
==38947==    by 0x227C8D: Effect_effect_map_inner_1fee66ad667b912c4d10ada5f77fb9e8b2dfe9a4124f957b34ae7bc684ecaf1 (in /home/bren077s/Projects/roc-misc/roc/examples/day1)
==38947==    by 0x22A8D3: Effect_effect_after_inner_8b8e749a7d5dc4035aed2d09b8b4ad59fac5ad694339521a2df23bf1ac35c3 (in /home/bren077s/Projects/roc-misc/roc/examples/day1)
==38947==  Address 0x8000000004b7523b is not stack'd, malloc'd or (recently) free'd

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 19:15):

I am gonna make a guess that somehow it is related to slice, but no clear reason why at the moment.

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 19:23):

Oh, yeah, I think I see the issue. basic-cli is using a lock file. This means that it is not updating as roc-std is updating. So it is using a version of roc-std from before slices were added. Due to this, it is doing the wrong thing when passed in a slices.

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 19:23):

A workaround until we update basic-cli is to change the chunk line to:

chunks = List.map (Str.split contents "\n") Str.releaseExcessCapacity

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 19:47):

@Anton do you think it is worth removing the lock file from basic-cli so that we always pull in the latest roc-std when publishing it? Otherwise, maybe we should add a cargo update to the publishing process?

view this post on Zulip itmuckel (Mar 26 2023 at 21:40):

Brendan Hansknecht schrieb:

A workaround until we update basic-cli is to change the chunk line to:

chunks = List.map (Str.split contents "\n") Str.releaseExcessCapacity

Perfect, that did it:

list: 1000
1000
success

Is there some documentation on where roc uses slices and what the implications are or shouldn't I care about them?

view this post on Zulip itmuckel (Mar 26 2023 at 21:41):

Also is there an issue for this problem (lock-file) that I can follow on github?

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 22:41):

Just filed https://github.com/roc-lang/basic-cli/issues/25 to track

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 22:42):

Fundamentally, a roc user should never have to think about slices, only a platform author might need to think of them. They are a performance improvement we added to reduce copying and extra allocations.

view this post on Zulip Brendan Hansknecht (Mar 26 2023 at 22:42):

Some details here #contributing > PSA: Seamless Slice

view this post on Zulip Richard Feldman (Mar 27 2023 at 00:17):

yeah this is just a timing issue; they landed very recently in the compiler and represent only the second time the layout of that data structure has changed in Roc's history

view this post on Zulip Richard Feldman (Mar 27 2023 at 00:18):

so this shouldn't be something that comes up often!

view this post on Zulip Anton (Mar 27 2023 at 08:45):

I'll take a look at basic-cli#25 today

view this post on Zulip Anton (Mar 27 2023 at 18:08):

basic-cli 0.3.1 is building...

view this post on Zulip Anton (Mar 27 2023 at 18:27):

basic-cli 0.3.1 pre-release
Can you give it a try @itmuckel?

view this post on Zulip itmuckel (Mar 27 2023 at 18:34):

yes, that works :-)

view this post on Zulip Anton (Mar 28 2023 at 18:14):

@itmuckel I've made 0.3.1 the latest official release. The tar.gz is also no longer available, instead we have the much smaller tar.br: https://github.com/roc-lang/basic-cli/releases/download/0.3.1/97mY3sUwo433-pcnEQUlMhn-sWiIf_J9bPhcAFZoqY4.tar.br


Last updated: Jul 06 2025 at 12:14 UTC