TIL: I Don't Understand This Gleam Pipe Compiler Error
Another Exercism exercise. This time, I was trying to be smart and use pipes with bool.or to complete the exercise.
Basically, the exercise wanted me to return a Bool
based on certain OR
conditions. Initially, I wrote it like:
pub fn root_for_team(team: Team) -> Bool {
team.coach.name == "Gregg Popovich"
|> bool.or(team.coach.former_player)
|> bool.or(team.name == "Chicago Bulls")
|> bool.or(team.stats.wins >= 60)
|> bool.or(team.stats.losses > team.stats.wins)
}
But the compiler complained:
error: Type mismatch
┌─ src/bandwagoner.gleam:37:22
│
37 │ team.coach.name == "Gregg Popovich"
│ ^^^^^^^^^^^^^^^^
Expected type:
Bool
Found type:
String
error: Type mismatch
┌─ src/bandwagoner.gleam:37:22
│
37 │ team.coach.name == "Gregg Popovich"
│ ╭──────────────────────^
38 │ │ |> bool.or(team.coach.former_player)
39 │ │ |> bool.or(team.name == "Chicago Bulls")
40 │ │ |> bool.or(team.stats.wins >= 60)
41 │ │ |> bool.or(team.stats.losses > team.stats.wins)
│ ╰─────────────────────────────────────────────────^
Expected type:
String
Found type:
Bool
That was weird for me. I would have thought that the first line team.coach.name == "Gregg Popovich"
would evaluate to a boolean. I cannot figure out why: not enough Googling, ChatGPT or Claude were clueless/hallucinating and online resources did not provide clear answers.
So I thought, “Let’s try to make it into a let
”. And that worked!
pub fn root_for_team(team: Team) -> Bool {
let coach_is_gregg_popovich = team.coach.name == "Gregg Popovich"
coach_is_gregg_popovich
|> bool.or(team.coach.former_player)
|> bool.or(team.name == "Chicago Bulls")
|> bool.or(team.stats.wins >= 60)
|> bool.or(team.stats.losses > team.stats.wins)
}
Alternatively, I could use anonymous function:
- let coach_is_gregg_popovich = team.coach.name == "Gregg Popovich"
+ let is_my_fav_coach = fn() -> Bool { team.coach.name == "Gregg Popovich" }
- coach_is_gregg_popovich
+ is_my_fav_coach()
While I still cannot figure out why the compiler complained, I looked at the Gleam tour again. This time, I re-read about blocks. This time round, the following worked:
- team.coach.name == "Gregg Popovich"
+ { team.coach.name == "Gregg Popovich" }
Then, I re-read the language tour and found this in the functions section:
Similar to blocks, each expression in the function body is evaluated in order and the value of the last expression is returned from the function.
Could it mean that the “last expression” in my original line was a String
type? Then again, the compiler had two error messages just for that block of code. It’s confusing.
I found this GitHub discussion which suggests it could be operator precedence. Seems like the pipe |>
has higher precedence than ==
, so Gleam parsed it as team.coach.name == ("Gregg Popovich" |> bool.or(...))
. I cannot find official docs on precedence yet.
In Elm, this would be fine:
rootForTeam : Team -> Bool
rootForTeam team =
team.coach.name
== "Gregg Popovich"
|> (||) team.coach.formerPlayer
|> (||) (team.name == "Chicago Bulls")
|> (||) (team.stats.wins >= 60)
|> (||) (team.stats.losses > team.stats.wins)
Obviously, we could just use the ||
syntax in both languages like a normal human. But I was just trying to explore and understand the language better.