Fork me on GitHub
#re-frame
<
2018-06-21
>
mf11:06:34

Hi, Hoping someone can shed some light on some behaviour I'm observing when using a partially applied map function as the computational function with reg-sub. Example:

(ns me.scribble 
  (:require [re-frame.core :as r]
            [re-frame.db :as rdb]))

(defn x [key items]
  (map key items))

(r/reg-sub
 ::msgs
 (partial map :msg))

(r/reg-sub
 ::msgs2
 (partial x :msg))


(comment
  (defn test-me []
    (reset! rdb/app-db [{:msg "hello"} {:msg "world"}])
    (r/clear-subscription-cache!)

    [(deref (r/subscribe [::msgs]))
     (deref (r/subscribe [::msgs2]))])

  (test-me) ;;  [("hello") ("hello" "world")])

If you load the above namespace into a REPL for a Re-frame ("0.10.5") project and call the test-me function, then you should observe the following return value: [("hello") ("hello" "world")] From my point-of-view this is unexpected, I would expect: [("hello" "world") ("hello" "world")] I'm unclear why partially applying map produces different results to partially applying x?

guy11:06:12

I’ve tried just the functions in a repl and it seems to produce the same result

guy11:06:39

It might be worth seeing what data

(r/reg-sub
 ::msgs
 (partial map :msg))

guy11:06:44

Gets and trying it that way too

mf12:06:11

Hi @guy, I'm not sure I follow your suggested debugging technique?

guy12:06:59

Sorry i just mean, can you show me the shape of the data you get for ur reg-sub functions

mf12:06:41

The input data for app-db is : [{:msg "hello"} {:msg "world"}]

guy12:06:49

because when i try ((partial map :msg) [{:msg "hello"} {:msg "world"}]) i get the same thing as ((partial x :msg) [{:msg "hello"} {:msg "world"}])

mf12:06:26

yep when you run the functions outside of a subscription handler you get the same result for both functions

guy12:06:12

Can you try doing (map :msg) and see what happens instead of partial?

guy12:06:26

as that returns a function

guy12:06:32

(r/reg-sub
 ::msgs
 (map :msg))

guy12:06:35

like that i mean sorry

mf12:06:41

yep tried that and get the following returned from test-me: [#object[G__10856] ("hello" "world")]

guy12:06:58

alright cool

guy12:06:42

yeah i was wrong sorry

guy12:06:53

What i find odd is its just returning the “hello” and nothing else

guy12:06:38

dumb question have you tried killing the repl and restarting it, just incase you had some older functions in it?

guy12:06:31

(rf/reg-sub
  :time
  (fn [db _]     ;; db is current app state. 2nd unused param is query vector
    (:time db))) ;; return a query computation over the application state

Taken from https://github.com/Day8/re-frame/blob/master/examples/simple/src/simple/core.cljs an example

mf12:06:32

restarted repl: same result

guy12:06:02

So it looks like in the example they wrap the sub with a (fn [db _] ...)

guy12:06:11

Maybe you could try that?

mf12:06:22

In my example I'm using reg-sub with no explicit input signals, which means it will default to app-db

mf12:06:52

So I'm registering a computation function that will be automatically passed app-db (a vector in my example)

guy12:06:10

Sorry then i don’t know 😞 i’ll lurk around to wait to see if someone answers it

mf12:06:24

@guy appreciate your input!

guy12:06:51

The strangest part for me is it consuming just one message from the vector

guy12:06:12

That to me says its something to do with map and processing a series of collections potentially

mf12:06:20

BTW - I have observed this behaviour in the browser - so it's not just a REPL related thing

guy12:06:45

(map :msg [{:msg "hello"} {:msg "hi"}] [:a]) => (“hello”)

guy12:06:54

So you might be getting this

guy12:06:08

Where map takes two collections rather than just one

guy12:06:20

thats the only way i could get it to produce “hello” in a list

guy12:06:01

((partial map :msg) [{:msg "hello"} {:msg "hi"}] [:a]) => (“hello”)

guy12:06:06

looks like its the same

guy12:06:02

but that just assumes the data you get

mf12:06:54

Yeah - I see your line of thinking

guy12:06:59

So it might be a red herring 😞

mf12:06:26

Not sure how to confirm that hypothesis

guy12:06:31

haha yeah

guy12:06:41

ok well i’ll go back to lurking

guy12:06:46

ping me if you find out what was wrong 😄

mf12:06:50

:thumbsup:

👍 4
guy12:06:48

I think its what i said is happening

guy12:06:00

try doing

guy12:06:26

(rf/reg-sub
  ::msgs
  str)

guy12:06:36

just replace your partial map with str and see what you get back

guy12:06:41

on my screen i get something like

guy12:06:12

i changed the example in re-frame and did somestuff to get it but its essentially v similar

guy12:06:33

So that shows you, that u get given the db and a vector with an event in it

guy12:06:39

(map :msg [{:msg "hello"} {:msg "hi"}] [:a])

guy12:06:47

which was just like this (more or less)

guy13:06:04

So thats why you only get given “hello” back

guy13:06:25

because the way map works is it only maps over the same number of items in the given collections i believe

guy13:06:07

As to how to fix it? I think if you wrap it in a fn and destructure it a different way that might work?

guy13:06:36

(rf/reg-sub
  ::msgs
  (fn [{msgs :msgs :as db} _]
    (map :msg msgs)))

guy13:06:47

ok so my data structure isnt the same as yours

guy13:06:04

my db is like

{:time (js/Date.)         ;; What it returns becomes the new application state
     :time-color "#f88"
     :msgs [{:msg "hello"} {:msg "world"}]}

guy13:06:30

so i use a fn and destructure the mgs out then just map over them and it works

guy13:06:08

So for your example to get it working in your test-me function you could do something like

guy13:06:10

(reset! rdb/app-db {:msgs [{:msg "hello"} {:msg "world"}]})

guy13:06:17

Where you make ur app-db a map instead of a vector, you would need to change both functions i believe

guy13:06:28

anyways let me know how you get on :thumbsup:

mf13:06:52

@guy you're onto something - for sure

mf13:06:26

I confirm that the computational function is receiving both the db and the event-name

mf13:06:47

and when both are passed to map it behaves as you describe

mf13:06:05

I can also confirm that my x function also receives the additional argument (as you would expect) - although obviously x doesn't use it as map does (hence x worked!)

guy13:06:46

:thumbsup:

guy13:06:00

So i think if you just use the other format you will get the outcome you need

guy13:06:15

(fn [db _] ...) one

guy13:06:42

yeah also one thing to note, (reset! rdb/app-db [{:msg "hello"} {:msg "world"}]) I think this is a little unusual

guy13:06:51

as i think app-db is usually a map

guy13:06:02

and you are actually creating an atom with a vector in it

guy13:06:00

also

;; -- Application State  --------------------------------------------------------------------------
;;
;; Should not be accessed directly by application code.
;; Read access goes through subscriptions.
;; Updates via event handlers.
(def app-db (ratom {}))

😅

mf13:06:10

- yeah I'm just using reset! to enable testing in the REPL - I do use a map in real life 😉

guy13:06:13

so i’m not even sure you should be calling reset! at all

guy13:06:15

ah ok cool

mf13:06:48

(the entire test-me function is to enable me to recreate this issue in the REPL)

guy13:06:58

ah ok got ya

guy13:06:02

makes sense :thumbsup:

mf13:06:38

Still playing with it atm, will let you know more

guy13:06:10

:thumbsup:

mf13:06:55

@guy Yep it comes down to the fact that the computational function passed to reg-sub will always receive the event-name as an additional argument.

guy13:06:56

The thing that gave it away to me was the single “hello” thing really

guy13:06:10

I’ve used (map fn col1 col2) before

guy13:06:21

and it maps over each of the collections and does stuff

guy13:06:31

but only up the least number of items in each coll i believe

mf13:06:45

:thumbsup: Using the str function to debug the inputs gave the insight

mf13:06:50

(good thinking)

guy13:06:11

i’m a re-frame noob so i’m still learning

mf13:06:14

Anyway - really appreciate you helping me get to the bottom of it!

👍 4
guy13:06:49

anytime!

👍 4