phel-lang/phel-lang

Improve error handling for missing mandatory parameters

Closed this issue ยท 9 comments

Q A
Phel version 0.14.1
PHP version 8.2.20

Summary

This is maybe more of a feature request as I actually don't know if and how Phel guarantees the check number of mandatory parameters or if it leaves it to PHP by design. My feeling is that at this moment it seems a little bit too low level.

Current behavior

For example for this piece of code (I forgot to use reduce2 instead of reduce):

(def array1 [1 2 3])
(def array2 [4 5 6])
(reduce + (map * array1 array2))

I received the following exception:

Phel\Compiler\Domain\Evaluator\Exceptions\CompiledCodeIsMalformedException: Too few arguments to function Phel\Lang\AbstractFn@anonymous::__invoke(), 2 passed in /tmp/__phelnmQ4Gi on line 4 and exactly 3 expected
in /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/Domain/Evaluator/Exceptions/CompiledCodeIsMalformedException.php:14 (gen: /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/Domain/Evaluator/Exceptions/CompiledCodeIsMalformedException.php:14)

#0 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/Domain/Evaluator/RequireEvaluator.php(43): Phel\Compiler\Domain\Evaluator\Exceptions\CompiledCodeIsMalformedException::fromThrowable(Object(ArgumentCountError))
#1 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/Domain/Compiler/EvalCompiler.php(110): Phel\Compiler\Domain\Evaluator\RequireEvaluator->eval('// string
// ;;...')
#2 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/Domain/Compiler/EvalCompiler.php(69): Phel\Compiler\Domain\Compiler\EvalCompiler->evalNode(Object(Phel\Compiler\Domain\Analyzer\Ast\CallNode), Object(Phel\Compiler\Infrastructure\CompileOptions))
#3 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Compiler/CompilerFacade.php(57): Phel\Compiler\Domain\Compiler\EvalCompiler->evalString('(def array1 [1 ...', Object(Phel\Compiler\Infrastructure\CompileOptions))
#4 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Run/RunFacade.php(79): Phel\Compiler\CompilerFacade->eval('(def array1 [1 ...', Object(Phel\Compiler\Infrastructure\CompileOptions))
#5 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Run/Infrastructure/Command/ReplCommand.php(207): Phel\Run\RunFacade->eval('(def array1 [1 ...', Object(Phel\Compiler\Infrastructure\CompileOptions))
#6 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Run/Infrastructure/Command/ReplCommand.php(141): Phel\Run\Infrastructure\Command\ReplCommand->analyzeInputBuffer()
#7 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Run/Infrastructure/Command/ReplCommand.php(98): Phel\Run\Infrastructure\Command\ReplCommand->loopReadLineAndAnalyze()
#8 /home/kambo/workspace/cli-skeleton/vendor/symfony/console/Command/Command.php(279): Phel\Run\Infrastructure\Command\ReplCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /home/kambo/workspace/cli-skeleton/vendor/symfony/console/Application.php(1031): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /home/kambo/workspace/cli-skeleton/vendor/symfony/console/Application.php(318): Symfony\Component\Console\Application->doRunCommand(Object(Phel\Run\Infrastructure\Command\ReplCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /home/kambo/workspace/cli-skeleton/vendor/symfony/console/Application.php(169): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/src/php/Console/Infrastructure/ConsoleBootstrap.php(24): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#13 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/bin/phel(45): Phel\Console\Infrastructure\ConsoleBootstrap->run()
#14 /home/kambo/workspace/cli-skeleton/vendor/phel-lang/phel-lang/bin/phel(46): {closure}()

It's kind of hard to find out where the problem is only from this error.

How to reproduce

  1. Define two arrays as shown in the example code.
  2. Attempt to use reduce with map as shown.
  3. Observe the exception thrown.

Expected behavior

It would be helpful if Phel provided clearer error messages or checks for the number of mandatory parameters, making debugging easier, especially in larger projects.

I understand that this is from the perspective of how to display an error message when an error occurs due to a difference in the number of parameters.

Although clojure and phel are not the same, I think it will be helpful to know what kind of messages are displayed with clojure.

user=> (reduce +)
Execution error (ArityException) at user/eval1 (REPL:1).
Wrong number of args (1) passed to: clojure.core/reduce

I think it is useful to have a message that lets you know that there is a problem with the number of arguments in the reduce function.

@kambo-1st How would the ideal error message look like for you in this case?

@jenshaase
This is an interesting philosophical question. I think that this would depend on the target audience (or the more expected audience) for Phel.

Personally, as a primary PHP developer, I would expect something like this:
"Too few arguments to function map(), 2 passed in bug.phel on line 3, exactly 3 expected in bug.phel:2."

So basically, a very similar message to regular PHP.

@kambo-1st I created a PR for this, check this out #710

Sorry, @Chemaclass . I am afraid that I did not make myself clear, which led to confusion. I apologize for that. Your proposed change is definitely a step in the right direction, but I would expect the absence of low-level PHP exceptions in favor of resolving the issue during compilation.

Something more like this:

phel:2> (def array1 [1 2 3])
(def array2 [4 5 6])
(reduce + (map * array1 array2))

Too few arguments to function map(), 2 passed in string on line 3, exactly 3 expected in string:2
2| (reduce + (map * array1 array2))
Screenshot 2024-06-02 at 15 40 09 Alright. What about about this? I updated the PR to have a similar msg as your suggestion, considering `repl` and `run` commands when there is a `CompiledCodeIsMalformedException`.

Wow! That is awesome!

I talked to @jenshaase and we agreed to try to solve this from another angle. Instead of rendering the error on runtime (what I did; catching the PHP runtime error), we should try to do it on compilation time. Therefore, I will close the current PR and we will work on another PR in the coming days :)
UPDATE: Done here: #717

Fix implemented in #717

Scope main level

Screenshot 2024-06-08 at 22 07 36

Scope on def level

Screenshot 2024-06-08 at 22 10 46

Scope on defn level

Screenshot 2024-06-08 at 22 08 24

Although, there are some limitations:

  • It might not work if the function is not yet defined but only declared