gleam-lang / gleam

⭐️ A friendly language for building type-safe, scalable systems!
https://gleam.run
Apache License 2.0
16.4k stars 686 forks source link

`use` in combination with pipelines produces a confusing type error #3336

Open joshi-monster opened 3 days ago

joshi-monster commented 3 days ago

Hi! πŸ’œ

I noticed this after adding a |> result.try at the end of a long pipeline and use - ing the result. I've made a simplified example demonstrating the problem:

fn use_test(input: Int, callback: fn(result: String) -> result) -> result {
   callback(int.to_string(input))
}

fn f(input: Int) -> String {
  use result: String <- input |> use_test
  "The result is: " <> result
}

produces the following type error:

error: Type mismatch
   β”Œβ”€ /src/main.gleam:15:34
   β”‚
15 β”‚   use result: String <- input |> use_test
   β”‚                                  ^^^^^^^^

Expected type:

    fn(Int, fn(String) -> a) -> a

Found type:

    fn(Int) -> b

Playing with the types for a bit, it seems like the "Expected type" here is always just the type of the function at the end of the pipeline, and the "Found type" is the type of the function with the second argument (the callback) partially applied.

When the function expects more than 2 arguments, you get an "Incorrect arity" error instead, even when providing the remaining "middle" arguments to the function (Expected N arguments, got N-2)

joshi-monster commented 3 days ago

Probably not related to the error message, but using a curried function fixes the problem:

fn f(input: Int) -> String {
  use result: String <- input |> function.curry2(use_test)
  "The result is: " <> result
}

compiles and works as expected.

lpil commented 2 days ago

Hello! As the error says that function takes 2 arguments, but only one is being supplied.

Use does not have a special case for the pipe operator, it always behaves consistently with anything on the right hand side.

joshi-monster commented 2 days ago

Hey, sorry for following up, but I'm not quite sure I understand how I should interpret that error. I would have expected a normal arity mismatch error, or for the expected/found types to be the other way around, since the compiler expected a single-argument function either way (whichever it de-sugars first), but I provided one that takes 2 arguments instead.

Don't get me wrong, I think it's totally reasonable for this sort of thing to not be supported. Having to search for the last bit in a long pipeline after use <- can lead to code that is hard to read. I just think the error message is not very helpful :)

I would interpret the error I got as a bug in the error reporting or even the type checker:

joshi~

lpil commented 2 days ago

Ah! Ok! Thank you for that info. I understand better now and was too hasty before.

I'll read through this again and think about what to do later, it's lunch time :)