I am getting compiler error on this
app "reg"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
imports [pf.Stdout]
provides [main] to pf
checkMatching = \ str, reg ->
matchStr = \ graph, pattern ->
when pattern is
Character val ->
if val == graph then
Consume
else
NoMatch
Dot ->
Consume
getFirstPat = (\ state ->
when List.first state.current is
Ok pat ->
{ pattern : pat , state : state }
Err _ -> getFirstPat { state & current : state.regex } )
Str.graphemes str
|> List.walk { regex : reg, current : reg, matched : "", result : Bool.false } ( \ state, graph ->
if state.result == Bool.true then
state
else
matchThis = getFirstPat state
updatedState = matchThis.state
when matchStr graph matchThis.pattern is
Consume ->
if List.isEmpty updatedState.current == Bool.true then
{ updatedState & matched : Str.concat updatedState.matched graph, result : Bool.true }
else
{ updatedState & matched : Str.concat updatedState.matched graph, current : List.dropFirst updatedState.current }
NoMatch ->
{ updatedState & matched : "", current : updatedState.regex } )
main =
res = checkMatching "dsads" [Dot]
Stdout.line "adfasfsa"
We can help if you tell us what the error is!
app
on the first line should not be indented I think
@Brian Carroll internal error I suppose , just copy paste and try to compile. There is no formal error just crash
at least for me
formal error I would easily fix myself
Did you try removing the indentation before app
?
I attempted to minimise and added as an Issue #5889
do you have suggestion how to avoid this problem ?
I would try to modify Luke's minimal reproduction in a way that produces the same output but using different data structures or functions that do not trigger this error.
I find it helpful to break things down into smaller functions, add tests using expect
, and type annotations. and then some combination of roc test
and roc check
usually helps to build up a working implementation. There may be a different set of types or data structures which work well as Anton suggested.
what roc test roc check stands for ?
The command line argument to for the Roc cli. So you can run the unit tests in the file with e.g. roc test myFile.roc
. And check is similar but just checks all the types and syntax and reports any issues without actually building an executable.
See also https://www.roc-lang.org/tutorial#tests-and-expectations
btw. abbreviation { struct & field : new_field } is crashing if I recreate entire structure from scratch there is no error
:+1: I've added this to #5889
I'm trying to tackle a couple documentation related GH issues, and I saw something odd in the docs regarding the pipeline operator. Specifically that a b c |> f x y
desugars to f (a b c) x y
. Why would the pipe apply to the first hole in the RHS function? In other FP langs it applies to the last argument. So I went to test it out in the REPL and managed to crash it a few times, including with the given desugar example.
The REPL I ran this in is latest master rev, FWIW
» "world" |> \w -> Str.joinWith w ["hello "]
Erroneous: Expr::Call
thread 'main' panicked at 'Roc hit an error', crates/repl_cli/src/cli_gen.rs:125:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Why would the pipe apply the incoming value the first hole and not the last?
One reason the |> operator injects the value as the first argument is to make it work better with functions where argument order matters.
Ok based on the docs it's an intentional design decision, but I don't follow the reasoning. In monomorphic operations like Num.div
the order absolutely matters, and as a user I'd expect the dividend to be the first argument ala 42 / 2
or Num.div 42 2
. If I'm then forced to write a notation like 42 |> Num.div 2
my intuition says this is 2 / 42
interesting! So I had the opposite intuition the first time I encountered this (back in the day), and also this is the behavior I want. For example, I've wanted to do things like this on several occasions:
num
|> doSomething
|> Num.sub 1
So far I've never wanted the opposite - that is, I've never wanted to perform a pipeline operation of "take a number (1
in this case) and subtract from it whatever I've built up so far." I've only ever wanted to subtract some number from what I've built up so far.
Another example:
|> List.append 1 # appends 1 to the list
that's an accurate comment in Roc (and in Elixir, which also does |>
the way Roc does) but it wouldn't be if |>
worked the other way
regarding the repl error - the compiler shouldn't crash there of course, but I believe the problem it's hitting is that the arguments to Str.joinWith
should be reversed!
Probably just wanted Str.concat
I agree with Richard, I find it way easier to conceptualise being the first argument. Maybe it's because I have the most experience with languages like python where mandatory arguments go first and the number of arguments can be unbounded, so for me, the first argument is the "primary" argument and so it makes sense to make it special. Maybe the other order better dissolves the boundaries between function application and composition.
I think the key here is precedence. List.append x
should always partially apply the first argument. Which is what I expect. And which is why the primary argument goes last in most functional languages. Because that's how partial application works.
But if
num
|> doSomething
|> Num.sub 1
is what you want in this case, surely that means the precedence is:
(((num |> doSomething) |> Num.sub) 1)
Which maybe we just need to be document. If that's the case, then I'd expect:
num
|> doSomething
|> (Num.sub 1)
to work in the other way.
Lakin Wecker said:
I think the key here is precedence.
List.append x
should always partially apply the first argument. Which is what I expect.
that's a reasonable expectation in a curried language, but to be fair, Roc isn't curried! :big_smile:
There is no partial application in roc.
:cry:
But |>
has a rule for applying that adds parens.
num
|> doSomething
|> Num.sub 1
becomes:
Num.sub (doSomething (num)) 1
Since no mainstream programming languages today are curried
This feels like a pretty bold claim
I mean you can spoof partial application with anonymous function, if you so need.
that said, if you ever need partial application, it is very easy to add a lambda
Lakin Wecker said:
Since no mainstream programming languages today are curried
This feels like a pretty bold claim
really? What's a mainstream curried language? haha
Haskell?
I'd say "Haskell is mainstream" is an extremely bold claim :sweat_smile:
Now we're just debating what is mainstream or not. :stuck_out_tongue:
const plus = (x: number) => (y: number): number => x + h;
Anyways, I'm not complaining. It's a design choice and it's not a hill I'll die on. I find it surprising, but it's fine.
While some other people are here, have we considered some syntax sugar that lets \x -> x*x*x
be reexpressed as \x*x*x
? Having to include function arguments in single-variable anonymous functions is a bit bloated, and I don't think it ought to cause too many issues.
it's always really interesting to see what different people find surprising when coming to Roc! There's quite a variety depending on what people are used to
@Declan Joseph Maguire we have not; I'd say start a new #ideas thread about it!
Declan Joseph Maguire said:
While some other people are here, have we considered some syntax sugar that lets
\x -> x*x*x
be reexpressed as\x*x*x
? Having to include function arguments in single-variable anonymous functions is a bit bloated, and I don't think it ought to cause too many issues.
Like scala's _
?
Richard Feldman said:
it's always really interesting to see what different people find surprising when coming to Roc! There's quite a variety depending on what people are used to
To be fair, I haven't learned roc. I just assumed: natively compiled Elm without the "web"
Which is probably a bad assumption. :stuck_out_tongue:
as evidenced here.
Fantastic! I haven't dug through all the threads and they often hold more than the title suggests, so I'm a bit hesitant to make suggestions that may have already been discussed and rejected - I ain't an expert at language design after all, I don't know what's useful and what's a footgun always.
I'll go make that asap
Declan Joseph Maguire said:
... what's a footgun always.
C++?
I think piping into the first argument is very natural for me because I learnt R and then Julia which both have packages that rely on that convention, and neither of which are automatically currying languages
Whoops I sniped everyone into a conversation and signed off for the weekend :sweat_smile:
I think the distilled point from my original post comes down to this: it is misleading from a user's perspective to have to write List.append 1
when they know the first argument is supposed to be a List Int
and not an Int
.
It distorts the notion of having a "strongly typed function" to apply a value of the wrong type to a positional argument.
However let's say the syntax involved a placeholder for the unapplied variable, it would read more clearly
[0, 1, 2]
|> List.append _ 3
Yeah, if you are used to currying, it reads weird. That said, I think it takes exceptionally little time to get used to always missing the first arg.
Also, we have discussed placeholders in general before. If we enable putting a place holder in the first position, people will want to use it in other positions. Discussions around allowing it in other locations have always led to the recommendation to just use a lambda.
With that in mind, adding a placeholder to the first location has issues. Either it is weirdly restricted to only the first position, or it means we have to accept general placeholders. Which again, so far have not gotten anywhere in idea discussions cause adding an extra lambda is simple.
Hmm...also, where do you put a placeholder with a raw lambda?
Yeah, if you are used to currying, it reads weird
Right, the pipe operator originates in curried languages (OCaml pretty sure). So most coming from that world will have the same reaction.
And yeah agreed the placeholder would open up the discussion for allowing it any position, which would be kind of a cool feature but also harder to implement
But to use a lambda on the RHS kind of defeats the purpose of pipelining / point free
If you think of the pipeline just as a list of stages to execute, I don't think a lambda defeats the purposes. Just means that the function the pipeline was referencing wasn't worth defining as a standalone function.
Last updated: Jul 05 2025 at 12:14 UTC