exercism / elixir

Exercism exercises in Elixir.
https://exercism.org/tracks/elixir
MIT License
612 stars 396 forks source link

Take-A-Number not enough information #1482

Closed mallowmew closed 2 weeks ago

mallowmew commented 3 months ago

Take-A-Number is the first exercise in the PID and Processes concepts. I don't think Take-A-Number gives enough information to allow for success as a first introduction to these concepts.

Note: I have been working through each concept by doing the introductory exercise and then moving onto the next concepts - I'm saving the 'practice' exercises for when I know the whole toolkit.

angelikatyborska commented 3 months ago

Hi! Thank you so much for the feedback 🙏

Re: receive/do

The introduction to the concept / exercise does not show a good example of where actually to put the receive do ... end block

The introduction describes where to put the receive do ... end block. It says:

It is a common pattern to implement a recursive function, for example named loop, that calls receive/1, does something with the message, and then calls itself to wait for more messages.

Re: passing a named function

The syntax for passing a named function in Elixir is not obvious.

You're absolutely right, we forgot to mention anywhere in the course how to pass a named function as an argument 😱 I'll create a PR to fix that.

Re: cannot test before task 4 implemented

Before you do Task 4 (Stop) you can't run the tests to see if you're getting closer to the solution.

Can you give me more details about that? How exactly did your partial solutions look like, and what was preventing you from running the tests? I find it possible to run tests on partial solutions for every step of the exercise. I'm attaching videos to demonstrate.

https://github.com/exercism/elixir/assets/7852553/f11728d1-f848-4076-b4c4-765aa67ed203

https://github.com/exercism/elixir/assets/7852553/ea32a4fb-9c4e-4268-afe4-a20cb6361df2

https://github.com/exercism/elixir/assets/7852553/2a1df836-124a-4b59-b5f3-b96a2eb6e3d1

mallowmew commented 3 months ago

Re: receive/do

The introduction to the concept / exercise does not show a good example of where actually to put the receive do ... end block

The introduction describes where to put the receive do ... end block. It says:

It is a common pattern to implement a recursive function, for example named loop, that calls receive/1, does something with the message, and then calls itself to wait for more messages.

I remember my first attempts at just getting something to happen went something like:

def start() do
    spawn(fn -> send(sender_pid, {:report_state, self()}) end)  # I don't remember exactly what I was trying here but it didn't work
    receive do 
      {:report_state, sender_pid} -> send(sender_pid, x)
    end
end

Because that's what it kind of looks like is happening in the Elixir docs - except now I know they're using the interactive interpreter and doing something different.

I was still thinking about the first step of just trying to see a message being passed. Implementing the recursive function seemed like the next step after that, so I just didn't understand where receive do ... went. For me there was a gap between what you had written and the syntax that actually translates to.

Seeing the syntax in a full context where it will simply return one message would have helped me a lot:

def start() do
    spawn(fn -> 
        receive do 
            {:hello, sender_pid} -> send(sender_pid, "world")
        end
   end)
end

Before you do Task 4 (Stop) you can't run the tests to see if you're getting closer to the solution.

Can you give me more details about that? How exactly did your partial solutions look like, and what was preventing you from running the tests? I find it possible to run tests on partial solutions for every step of the exercise. I'm attaching videos to demonstrate.

I was incorrect about this. My problem with tests not running was because of not knowing how to pass in a named function and just trying stuff.

I was probably doing something like this:

def start() do
    spawn(loop(0))
end

This runs in Exercism but times out without indicating why it was wrong.

conradwt commented 2 months ago

In general, this exercise would better serve individuals if we were also asked to create the client interface. For example, there would be functions like current_number, take_number, stop_machine, and so on.

angelikatyborska commented 1 month ago

For me there was a gap between what you had written and the syntax that actually translates to.

There is this global Exercism guideline about writing concept introductions:

Code examples should only be used to introduce new syntax (students should not need to search the web for examples of syntax). In other cases provide descriptions or links instead of code. (source: building tracks / concepts)

Defining functions and recursive calls are not new syntax in the context of the Take-A-Number exercise so I'm not supposed to include code snippets for that. The goal of this guideline is: 1) keeping introductions short and relevant, and 2) preventing mindless copy-pasting and encouraging careful reading with understanding.

The syntax for passing a named function in Elixir is not obvious.

You're absolutely right, we forgot to mention anywhere in the course how to pass a named function as an argument 😱 I'll create a PR to fix that.

I just realized that this exercise doesn't need passing a named function as an argument at all 🙈 I opened a PR that reverts those changes, slightly redefines the first two tasks of the exercise and adds more hints that should lead people to write this code, which is the exemplar solution:

  def start() do
    spawn(fn -> loop(0) end)
  end

Sorry for the late response. I've been struggling to prioritize open source work lately.

I hope the changes in https://github.com/exercism/elixir/pull/1511 are enough to address this problem. if not, I'm always open to more suggestions.

mallowmew commented 1 month ago

I appreciate the need not to just give the answer in the exercise text. I think the changes in #1511 help a lot. Seeing an example of either way of putting a callback into spawn() properly would have been enough for me to reach the solution, so the PR changes defintely make the exercise do-able.