clojure-android / neko

The Clojure/Android Toolkit
Other
297 stars 36 forks source link

onClickListener not finding functions in the same namespace? #61

Closed Agendine closed 8 years ago

Agendine commented 8 years ago

It's possible that I've done something wonky, but I have image-buttons being defined in an adapter for a GridView, and for the most part working properly. However, calling a function defined elsewhere in the same file in the onClick procedure is causing "java.lang.RuntimeException: Unable to resolve symbol: in this context, compiling:(NO_SOURCE_PATH:0:0)" errors, even though the same exact call can be run, successfully, in the REPL.

In fact, what it's doing is looking up an entry in a predefined map, 'function-name, and eval-ing that to call function-name, which it isn't finding. Am I missing something here, or is the onClickListener having issues with either the scope or the quoting somehow?

alexander-yakushev commented 8 years ago

I'd rather see a sample code of what you are doing. But in any case, using eval in your application is a bad sign.

Agendine commented 8 years ago

Certainly! And, of course, pilot error is a definite possibility here.

So at the moment I have a :grid-view in the main activity, populated by an imagebutton adapter, like so:

screen shot 2016-02-04 at 12 26 08 pm

ID's are currently 3000 + cell#, just to label them programatically and avoid conflicts. I'm sure there's probably a better way to do that than what I'm doing, but it does work. The update-fn isn't currently being used to do anything.

display-moves is then just directly setting the imageDrawable for image-buttons in-place. screen shot 2016-02-04 at 12 27 29 pm

screen shot 2016-02-04 at 11 36 37 am

Wherein the (get-in game [:board x y :type]) is pulling from a singleton property map for each piece-type in the game, such as: screen shot 2016-02-04 at 11 37 05 am

and move-forward, etc, are just producing a vector of [x y] pairs for available moves.

As said, (display-moves (*a) 3) does work as it should from the repl, but the same command from the onClick produces "Unable to resolve symbol: move-forward".

(on clicking a piece) screen shot 2016-02-02 at 11 21 55 pm

Sorry if I've given more info than strictly relevant, but I do appreciate the extra eyes. Neko's been great to me so far, but I'm still wrapping my head around it.

alexander-yakushev commented 8 years ago

OK, so here's the problem: you don't store functions in your singleton map, but mere symbols. Then you try to resolve those symbols using eval, but since that may be called in some other namespace, the functions then can't be resolved.

In Clojure we don't do that. Instead, you put actual functions inside your map:

{:moves [[move-horizontal 1]
         [move-forward 1] ...

And then you don't use eval when calling the function:

(fn [[move-function num-spaces]]
  (move-function (get game :board) num-spaces ...

Problem solved! :)

alexander-yakushev commented 8 years ago

Looks you are doing an awesome project, by the way!

Agendine commented 8 years ago

Thanks so much, for both the help and the kind words. I'm kicking myself that I didn't see the problem sooner. Still a goodly ways to go, but if you'd like I'll shoot you an email when it's done!

alexander-yakushev commented 8 years ago

Yes, absolutely! I'd love to see this finished.

A few more notes. You can actually save not the function objects in the map, but their Vars.

#'move-forward 1
...

This way you won't have to reload the map every time toy redefine the function. It will have one extra layer of indirection like you had with symbols, but without eval.

You can also just use multimethods. It's rarely needed to implement this kind of dispatching manually.

On Fri, Feb 5, 2016, 03:17 Agendine notifications@github.com wrote:

Thanks so much, for both the help and the kind words. I'm kicking myself that I didn't see the problem sooner. Still a goodly ways to go, but if you'd like I'll shoot you an email when it's done!

— Reply to this email directly or view it on GitHub https://github.com/clojure-android/neko/issues/61#issuecomment-180138039 .

Agendine commented 8 years ago

After doing some reading, I've realized you're completely right: multimethods are a much more idiomatic way of handling things, and I think I'm going to chart out some refactoring based on that. I think my OO background was showing.

However, I'm still not 100% sure there isn't some weirdness going on with the onClick functionality. For example,

(.setOnClickListener image-button
    (proxy [android.view.View$OnClickListener] []
        (onClick [v]
            (display-hl-pos activity 0)

works in the image-button definition, as does

(.setOnClickListener image-button
    (proxy [android.view.View$OnClickListener] []
        (onClick [v]
            (do
                (display-hl-pos activity 0)
                (display-hl-pos activity 8)
                (display-hl-pos activity 72)
                (display-hl-pos activity 80)))))

and

(map (fn [pos] (display-hl-pos (*a) pos)) [0 8 72 80]))

when put into the REPL.

But putting in

(.setOnClickListener image-button
    (proxy [android.view.View$OnClickListener] []
        (onClick [v]
            (map (fn [pos] (display-hl-pos activity pos)) [0 8 72 80]))))

to the image-button definition causes nothing to happen.

I've worked around this by another method, but it seems odd that mapping would fail when the same operation called explicitly succeeds.

alexander-yakushev commented 8 years ago

map is lazy, and since you are not using it's return value anywhere it might not be executed at all. Try wrapping it in doall, or replace with mapv.

On Sun, Feb 7, 2016, 04:00 Agendine notifications@github.com wrote:

After doing some reading, I've realized you're completely right: multimethods are a much more idiomatic way of handling things, and I think I'm going to chart out some refactoring based on that.

However, I'm still not 100% sure there isn't some weirdness going on with the onClick functionality. For example,

(.setOnClickListener image-button (proxy [android.view.View$OnClickListener] [] (onClick [v](display-hl-pos activity 0)

works, as does

(.setOnClickListener image-button (proxy [android.view.View$OnClickListener] [](onClick [v] %28do %28display-hl-pos activity 0%29 %28display-hl-pos activity 8%29 %28display-hl-pos activity 72%29 %28display-hl-pos activity 80%29%29)))

and

(map (fn [pos](display-hl-pos %28*a%29 pos)) [0 8 72 80]))

in the REPL.

But putting in

(.setOnClickListener image-button (proxy [android.view.View$OnClickListener] [](onClick [v] %28map %28fn [pos] %28display-hl-pos %28*a%29 pos%29%29 [0 8 72 80]%29)))

to the image-button definition causes nothing to happen.

I've worked around this by another method, but it seems odd that mapping would fail when the same operation called explicitly succeeds.

— Reply to this email directly or view it on GitHub https://github.com/clojure-android/neko/issues/61#issuecomment-180913023 .

Agendine commented 8 years ago

Aaaaaaah. It all makes sense now. Once again, thank you.

alexander-yakushev commented 8 years ago

No problem!