Encounters, Multiple Dispatch, generic fallback: Task 7 hard to solve and interfering with Task 6

Good evening

I’ve been martering my Mind for so many hours I finally want to ask for another hint on my code.


# Define an abstract type Pet
    abstract type Pet end

# Define an abstract type UO - unknowm
    abstract type UO end
    abstract type UDO end

# Define concrete types Dog and Cat
    struct Dog <: Pet
           name::String
    end

    struct Cat <: Pet
           name::String
    end

    struct Horse <: Pet
           name::String
    end

    struct Bicycle <: UDO
           license_plate::String
    end

    struct cars <: UDO
           license_plate::String
    end

    struct Car <: UDO 
           license_plate::String
    end

# Define a name() function

name(p::Pet) = p.name 

# Define multiple methods for meets(a, b)
# Only the first one is stubbed

meets(a::Dog, b::Dog) = "sniffs"
meets(a::Cat, b::Dog) = "hisses"
meets(a::Dog, b::Cat) = "chases"
meets(a::Cat, b::Cat) = "slinks"
meets(a::Pet, b::Pet) = "is cautious"
meets(a::Pet, b::Any) = "runs away"
meets(a::Any, b::Any) = "nothing happens"

# Implement the encounter(a, b) function

encounter(a::Pet, b::Pet) = a.name * " meets " * b.name *" and " * meets(a, b) * "."
encounter(a::Pet, b::Car) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."
encounter(a::Pet, b::Bicycle) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."

# Define three fallback methods for meets(a, b)
# Stubs are not provided for these, but look at the hints if necessary

@test encounter(buddy, t) == "Buddy meets $(name(t)) and runs away."

### Test Error

FieldError: type ExercismTestReports.RandomType17995412825786376931 has no field `license_plate`; ExercismTestReports.RandomType17995412825786376931 has no fields at all. Stacktrace: [1] getproperty @ ./Base_compiler.jl:54 [inlined] [2] encounter(a::ExercismTestReports.Dog, b::ExercismTestReports.RandomType17995412825786376931)

It seems that for this case, a method belonging to task 6 is called.
I’ve already checked the hints.

the methods

encounter(a::Pet, b::Car) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."
encounter(a::Pet, b::Bicycle)..

were added recently as I wanted to exchange
encounter(a::Pet, b::Any) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * “.”
with something fitting to the test case 7.2
but I found no solution and:
what’s also surprising to me:
the function

encounter(a::Pet, b::Car) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."

is not called, when
deleting:

encounter(a::Pet, b::Any) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."

Error:
FieldError: type ExercismTestReports.Car has no field name, available fields: license_plate
Stacktrace:
[1] getproperty
@ ./Base_compiler.jl:54 [inlined]
[2] encounter(a::ExercismTestReports.Dog, b::ExercismTestReports.Car)

please give me some more background information

thanks in advance,

Nisang

okay,
uncommenting this:

function encounter(a::Pet, b::T) where {T}
    string(a.name, " meets $(name(t)) and ", meets(a, b), ".")
end

solves 7.2
but then again 3 tests in 6 are not solved even though I introduced:

encounter(a::Pet, b::Car) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."
encounter(a::Pet, b::Bicycle) = a.name * " meets " * b.license_plate *" and " * meets(a, b) * "."
@test encounter(buddy, car) == "Buddy meets W-12345X and runs away."
### Test Error

UndefVarError: `t` not defined in `ExercismTestReports` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] encounter(a::ExercismTestReports.Dog, b::ExercismTestReports.Car) @ ExercismTestReports ./encounters.jl:65 [2] top-level scope @ ./runtests.jl:28

OKAY
that’s it:
solved it
I already had the idea that the text in Task 6
something it doesn’t know
wanted to tell me something!
to not spoil anything:
i changed the recent added function above and it works…
And still there are questions open:

why are the two methods of

  • encounter() (for Car and Bicycle) not called by multiple dispatch?? And,
  • does
$(name(b)) 

work both for b.name and b.license_plate ?
because when replacing this with either b.name or b.license_plate
one of the two test cases (6 or 7.2) doesn’t work… ???
it seems i still don’t really understand, what $(name(b)) exactly does!

thanks for your answers!

Nisang

Good morning,

okay, this morning , waking up I thought:
if it was a good idea to spoil almost the whole code

and removed the parts for task 6 and 7
I will submit it and publish it to the community…
today

Hey Guys,

today I checked the community solutions…
and saw that e.g. #mahbjr’s solution:
looks very simple and effective…
but it doesn’t work when I copy it to the editor and [run tests]

what happened here?

have a good time,

Nisang

That solution was submitted 10 months ago. I’m not sure what happened but if the exercise/tests were updated since then, it’s possible old solutions no longer pass the latest tests.

1 Like

Some tests were added in add numbers to concept testset names by depial · Pull Request #1026 · exercism/julia · GitHub, but solutions were not rerun at the time.

1 Like

Good morning Isaac, good morning Andras,

okay, this explains something
thanks for your answers!

I just talked to a Julia friend and want to let you know what thoughts both of us have in common:

  • I did something that was not asked for (defining structs Car, cars and Bicycle) and so went into the wrong direction… which was not helpful
  • I did’nt know that $ in “” works also for functions (which might be clear in the c and linux field), but wasn’t… and let to some misunderstanding and irritation.
  • the solution of mahbjr doesn’t follow the recent tasks and is not “DRY” - don’t repeat yourself
  • I had big trouble with .name and .license_plate…
    I have an idea to give the student a bit more direction to see where the path should go…
    To be precise: would it be a good idea to let him # Define a name() different from pet… in the process… so it becomes clear(er) that name can deliver values from different objects (what seems to be done in the background of exercism) ** without knowing how this is done exactly ** (e.g. Car, cars and Bicycle use license_plate) AND with Erik’s Solution it is not neccessary to know the field names.
    So that (to conclude my thought) the student might know in the process that using $name… provides the content needed… ?

thanks for your interest

have a good time and enjoy Easter!

Nisang

1 Like

Good evening

can you please answer these questions, too?

Sorry, I don’t know Julia that well.

Hi @AnandNisang! Sorry for the late reply here. I’ve been rather busy with some other work lately…

I think you’ll want to carefully read through the introduction or seek more information on multiple dispatch. It can be a bit tricky for some people to get used to, but once you’ve got it, it’s rather useful. You can also have a look at my solution, which is intended to be a minimalist implementation.

You should not be defining things such as Bicycle or Car, these are implemented for you in the tests. Only worry about defining Cat, Dog and the abstract type Pet. The point is for you to come up with some “catch all” type solutions, where the type can be arbitrary. You can see how this is done in my implementation.

The reason name(b) will work but b.name may not, is that the name() function is being multiply dispatched. So it works with name(c::Cat) and name(d::Dog), because these two methods have been defined. The dot notation accesses the fields of the struct explicitly. So, if you have a field with a different variable name, like license_plate for Car types, you can’t use car.name, since that field doesn’t exist. However, you can define:

name(car::Car) = car.license_plate

And the name method will work with the Car type. However, remember, you shouldn’t define the Car type or any methods that specifically reference it to solve the exercise since these are defined in the tests for you. Please see my solution for more details.

Let me know if there is anything else specific you’d like to know.

you’re welcome!

Well I had a look at #ErikSchierboom’s solution which seems to be almost identical.
and that’s why my questions arrouse.

We assumed that in the background (the exercism test sorrounding) there’s a different name function e.g. to resolve license_plate what you wrote in your explanations:

and

To make this more clear to students I suggested to let them define more than one name function e.g. for horses or bicycles.

I will reread the explanations again or did you mean to read the Julia’s documentation?

And - by the way
how can I let the source code of a method be displayed e.g. for sum() or broadcast “.” or map() or “+”

??

I think you’re slightly missing the point of the exercise, so I would be good to reread the information provided in the exercise, as well as anything else you might find online. Multiple dispatch is a quite interesting topic, but might require extra time working with it to have it “click”.

The key to multiple dispatch is type annotations. As it says in the introduction, Julia uses the most specific method when a function is called. For Cars, you don’t need to create a specific method for them since they are not animals. The methods below are in decreasing order of specificity:

meets(a::Dog, b::Dog) = "sniffs"
meets(a::Cat, b::Dog) = "hisses"
meets(a::Dog, b::Cat) = "chases"
meets(a::Cat, b::Cat) = "slinks"
meets(a::Pet, b::Pet) = "is cautious"
meets(a::Pet, b) = "runs away"
meets(a, b) = "nothing happens"

The first four are rather specific and the last three become less specific each time. So, if you call meets with just Dog or Cat types, then the first four are called depending on your choice of arguments. Then if you call other Pet subtypes, it will be handled by the fifth method. If a Pet subtype is the first argument with a non-Pet subtype the second, the sixth method is called. Finally, if two non-Pet subtypes are used as arguments, it doesn’t matter what they are, just so long as they have name() defined, then the seventh method is called.

Since name() is defined for all non-Pet types used in the tests, you have no need to worry about what the actual other types are or what their structure is. If you are trying to use the Car type or define anything explicitly related to it, you are missing the essence of the exercise.

If you want, you can think of fallback methods as a kind of hierarchy of default behavior.

As for the exercise, just to be clear, we don’t want the learner to define name methods for horses or bicycles. That is not part of the exercise. My explanation was simply answer this question:

on a technical level, not at as an exercise hint.

If you want to see the source code, I usually find the function in the documentation, there they have a link to the source code in the lower right corner.

If you want quick access to the documentation for a function from the REPL, for example for sum, then type:

?sum
1 Like

"you will have to read the tests to understand the full and exact requirements: "

Dear Depial
thanks for your very detailed answer.

Yes, you’re right
I will answer this in detail on the weekend (including TDD).

the intention was not to “worry about something” it was more to lead the student into the direction where it should go. more to come

Also: to me it was not totally clear what fallback means.
I thought that was in terms of the animals behaviour but later it seemed this is meant about typical fallback reactions used in Julia!!

have a good time!

Nisang

Dear Depial,

here my answer as promised

In the process so far I was guided to develop TD (test driven) and first I didn’t know what that means.
The Exercism advice for that is to read the tests first.
For this exercise there are no tests available.
While developing I read the error messages and saw there’s another field in the struct for cars and also for bycicles.
So I draw the conclusion I should create a struct for this (which turned out to be wrong, later on)

So my considerations and questions are:

  • should I start searching the Internet before developing for every exercise?
    You (the people responsible for Exercism) have a reason not publishing the tests for concept exercises… ? → obviously not!
  • it seems my mind is working differently from most of the others and at some points I certainly don’t get what you want from me!
  • function name(): by letting the student think about the problem at an early state and let him create a function name() for cars/bycicles
    so that the student cannot avoid using the name function (instead of a.name) in the encounter function definition (e.g. writing a.name instead of name(a))…
    So it would’nt work at an earlier state and the student would’nt have the idea of redefining objects.
    Wouldn’t that be helpful in future for others?
  • you adviced me to “read anything else you might find online” … about
    Multiple Dispatch. I know this is a big request, but I would like you to at least think about it: What about offering more exercises about this topic?

I want you to take this into your considerations for a change request.

thanks for your interest.

with kind regards,

Nisang

There is a method to the madness, I assure you :smiley:

As you’ve noticed, the concept exercises do not have visible tests. I believe this is intended to force more focus on the concept rather than just passing the tests. This is to say that TDD is probably less applicable here, unless you are the one implementing the testing (which is possible but somewhat advanced).

Just to clarify, for concept exercises, since the documentation is the primary resource for information on completing the exercise, it’s best to spend good time trying to understand it before diving in to the exercise. Practice exercises, which have visible tests, are much more aligned with TDD and often have sparse instructions on purpose.

From a teacher’s standpoint, I can see that you are at a stage where a new idea (TDD) is being overused, which has led to you overlooking other ideas (i.e. materials in the introduction/instructions). This is a perfectly natural way to learn. We often under-use something, simply because we don’t know it exists, then we discover it and start to over-use it (often adopting it as a paradigm for some time), before we find the appropriate places to use it for what it is: a specialized tool. I want to stress how normal this is in a learning cycle, so there’s no need to think your “mind works differently than most of the others”. It’s just a matter of knowing when to use the hammer and when to use the screwdriver, which comes with experience.

Learning how to learn (which turns into independent learning) is a valuable skill in itself, and I recommend looking for materials on that. That said, it appears to be rarely taught explicitly. As to why that is the case, I haven’t the foggiest of ideas (I believe all primary schooling should have explicit courses on the subject). Exercism does encourage this implicitly by not always giving step-by-step instructions and indeed encouraging TDD on some exercises. This is why I’m not surprised you feel you “don’t get what we want from you”. We try to provide a varied environment to encourage the use of different tools and thinking methods, rather than a place where one idea can be used as a sledge hammer on everything. I encourage you to keep exploring, and keep and open mind as to what tool might be most useful for any specific task.

As for your more specific concerns.

  • You can search the internet at any time, but for concept exercises, there should be sufficient documentation to complete the exercise (practice exercises are different).
  • Letting the student create a method name for cars/bicycles doesn’t defeat the purpose of the exercise, but it does limit it. You already create a method for cats/dogs, so doing it again adds little value. Learning how to work around needing that is much more valuable, and the aim of the exercise.
  • Multiple dispatch is a central feature of Julia, indeed it’s often referred to as a paradigm (as compared to OOP in Python, C++ and many other languages). It is not a trivial aspect of the language and deserves good attention to it. As for more exercises, this is such a central aspect of the language, you can probably find many ways to fit it into exercises you’ve already done. However, there are more advanced exercises that require multiple dispatch, but also require a bit more learning (Interfaces). I’ll look into linking some to the topic, but they might not unlock until you finish Interfaces, but I would highly suggest trying to get a good grasp on Encounters and why it is designed in the way that it is (because it is purposeful). If it still is mysterious to you, you haven’t understood the power of multiple dispatch yet.

To conclude, I feel most of your frustrations come from the Exercism environment encouraging a higher level of independent learning than you’ve likely experienced before. This is very normal. In fact, while writing this reply, I was interrupted to have a conversation which focused on how many people with PhDs are lacking important independent learning skills. However, I have noticed you are adapting and are trying to use new tools, thereby strengthening your independent learning. That is the most important part. In other words, you are doing well in that area, and even better than many people are capable or willing. Keep exploring!

3 Likes

I realized that I’ve been a bit vague about TDD, so it could be helpful to talk about how to use it as a tool on Exercism instead of a monolithic paradigm.

Tool usage:

  1. Get a good understanding of the problem (e.g. try finding corner cases)
  2. Make a real attempt at solving it
  3. Try your solution
  4. Try to guess/understand why any failures occurred before probing error messages
  5. Look at error messages or other sources for guidance (optional)
  6. Make a real attempt at addressing any failures
  7. Repeat if necessary

Paradigm failing:

  1. Get a vague/general idea of the problem.
  2. Make a half-hearted attempt at a solution
  3. Wait for error messages or other sources to guide you.
  4. Repeat

Here, you can see that a main failure point of TDD happens when someone becomes too dependent on test failures to guide them. Then they may start taking shortcuts around fully understanding the problem since this appears to no longer be needed. There is no substitute for depth of understanding and this should be the primary goal when using TDD as a tool. After all, if you have to write your own tests in the real world, you’ll need to have a good understanding of the problem or you’ll write poor tests (and thus poor code). For example, corner cases hide everywhere and learning to identify them is a skill. However, this skill might lay undeveloped or atrophy if we become too dependent on getting instant guiding feedback.

So, in the end, you can certainly still use TDD methods on concept exercises, even though you can’t see the tests. To be honest, the lack of explicit access to the tests injects a bit of more of the real world into it, and I wouldn’t be surprised if this is one reason they are not explicitly given to the student, but I still believe the main reason for it is to encourage depth of understanding of the concept as a primary goal.

As a final word, I would advise you to be careful of becoming overly dependent on test failures to drive your development, but, judging from what I’ve seen of your learning style, I don’t think you’re at great risk of this. It is something to keep in mind, though.

2 Likes

Dear Depial thanks for your detailed answer a lot!

I appreciate that very much!

this is really a good advice
but sometimes not that easy.
I will follow this guideline more in future.

Did you…
to me?
Okay, good you wrote that again.
I will follow that advise, too.

I feel very much sensed by your words and it shows you are sensitive to others.

What I’m not shure is, if I’m not used to independent learning as I learnd so many different topics (e.g. Perl, Herbs, working with children…) independently.

Yes it seems to be really helpful to get more into that.

I think this is the most important next step for me, as sometimes I started coding without totally having grasped what the problem is!

sincerely Nisang

I’ll just clarify a bit. I don’t believe you are not used to independent learning. What I’m trying to point out is that independent learning is a skill that can be honed and improved, but is not necessarily honed by simply using the same set of techniques over and over.

Maybe an analogy could help: Cooking is also a skill. Many people cook every day, but that does not necessarily make them good chefs.

So, while you already have a good independent learning foundation, you can still get better at it if you want to. From what I see, it appears you do want to improve (e.g. using new tools like TDD) and are indeed improving, so keep up the good work!

thanks for that!