Fork me on GitHub
#beginners
<
2023-02-13
>
M J08:02:00

I am using a multimethod (demulti) that handles a couple of stuff, which for example it takes "string" or "number" and return accordingly. How do I create a method that returns something for everything else not handled? Like a default defmethod

rolt08:02:42

defmethod f :default

🙌 2
rolt08:02:00

btw if you're dispatching on types you can also use protocols

M J08:02:10

What do you mean?

rolt08:02:43

something like (defprotocol PrettyPrintable (pprint [this])) (extend-type String PrettyPrintable (pprint [this] (clojure.pprint/pprint this))) (syntax is probably wrong but you get the idea) instead of (defmulti pprint (fn [obj] (type obj)))

Jesús Gómez12:02:22

@U02F0C62TC1 aside: type was enough, instead of (fn [obj] (type obj))

M J15:02:38

I keep getting a neverending loop of these warnings/messages in console Warning: Every element in a seq should have a unique :key: ([app.strive_forms.views.multi_question_page .... This is my code tat contains multi_question_page

(defn multi-question-page [{:keys [props]}]
  [v-box

       :children [
                  (map-indexed (fn [index field]
                                 ^{:key (str "new-" (:id field) index)}
                                (when (:condition) 
                                  [gap-space {:primary-gap (:header-to-question-question-card (:gaps-primary default-values))
                                              :secondary-gap (:header-to-question-question-card (:gaps-mobile-view default-values))}]
                                  [question-card {props}]))
                              display-fields)
       [gap-space {props}]
       [action-button {props}]]])

(defn form-swipeable-panel [{:keys [index display-fields selected-language theme-data form-messages current-page hidden-fields
                                    input-data on-submit pre-filled regenerate-display-fields]}]
  (let []
    (if-not (:is_group field)
      [single-question-page {props}]
      [multi-question-page {props}])))

(defn question-page [{:keys [props]}]
    [v-box
     :height "100vh"
     :justify :center
     :children [
                
                [(r/adapt-react-class SwipeableViews)
                 {:index @current-page
                  :onChangeIndex #(reset! current-page %)
                  :style {:display "flex"
                          :justify-content "center"}
                  :springConfig #js{:duration "2s"
                                    :delay "3s"}
                  :class (form-content-style)}
                 (map-indexed (fn [index field]
                                ^{:key (str "question-page-" (:id field) index)} 
                                (form-swipeable-panel {:props props}))
                              display-fields)]

                [form-page-footer form-messages]]])
Obviously made code readable by actually replacing the props with "props" and apparently the key I added isnt working. When I open console it keeps lagging too from all the endless messages of the warning

dgb2315:02:19

1. I would write a much simpler component that tries to isolate the problem as much as possible. 2. Another tip would be to avoid using indices when generating keys for react. if (:id field) is insufficient, your ids are not actually unique, then you might pre-generate uuids (for example) in your dataset, which are maintained between state changes. 3. I'ts unclear where display-fields comes from in your example and how it looks like.

dgb2316:02:04

Oh it also seems like you are attaching a key to the gap-space component but not to the question-card component in your :children expression. But I'm not a reagent user so I might be wrong.

hiredman17:02:40

You are attaching the metadata at read time using ^ it is being attached to the form of the function call, not the result of the call

hiredman17:02:18

I would try switching to with-meta

Markus17:02:26

I am trying to wrap my head around recursion in Clojure for a project, but I stumbled upon a problem when two functions are calling each other, like in this simplified example:

(defn doThis
  [times]
  (print "Doing this!")
  (when (> times 0)
   (doThat (- times 1))))

(defn doThat
  [times]
  (print "Doing that!")
  (when (> times 0)
   (doThis (- times 1))))
This produces an ‘unresolved symbol’ error for doThat inside of doThis. To me the problem seems to be that doThat is not defined before its usage in doThis. What can I do to solve this problem?

adam-james17:02:39

This is what declare is for, I believe. So, (declare doThat) above doThis should let you set it up.

nihilipster.dev17:02:45

You can also define those mutual recursive functions inside defn with letfn: https://clojuredocs.org/clojure.core/letfn

Markus17:02:15

Thats exactly what I was looking for! Thanks for the quick answers and pointing me in the right direction!

🙂 1
skylize23:02:31

Just remember to be cautious about choosing to use mutual recursion. For plain tail recursion, we can mimic tail call optimization with recur. There is no such construct for mutual recursion. Quite tricky to know for certain that your input data will always be constrained enough to not blow the stack. Might be able to solve that with a trampoline, but personally I find that to be a pretty clunky alternative.

Nastasja Bakovic18:02:42

I followed the https://www.youtube.com/watch?v=wq6ctyZBb0A tutorial and tried to use it to create my application, but I got stuck on this. I started lein cljsbuild once dev, and then lein figwheel, but nothing is rendered on the page, it reports an error in the console. This is my project.cljs file and console screenshot:

(defproject thats-life "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :main thats-life.core 
  :cljsbuild {:builds [{:id "dev" 
                        :source-paths ["src"]
                        :figwheel true
                        :compiler 
                        {:optimizations :none
                         :output-to "resources/public/javascripts/dev.js"
                         :output-dir "resources/public/javascripts/cljs-dev/"
                         :pretty-print true 
                         :source-map true}}]} 
  :plugins [[lein-cljsbuild "1.1.8"]
            [lein-figwheel "0.5.20"]]
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojure/clojurescript "1.11.60"]
                 [reagent "1.1.1"]]
  :repl-options {:init-ns thats-life.core})

Nastasja Bakovic18:02:19

and, this is the index.html file


<html>
  <head>
    <title>That's Life</title>
  </head>
  <link rel="stylesheet" type="text/css" href="main.css" media="screen" />
  <body>
    <div id="game"></div>
    <script src=""></script>   
    <script src="./javascripts/cljs-dev/goog/base.js"></script>
    <script src="./javascripts/dev.js"></script>
    <script>goog.require('thats_life.core')</script>
  </body>
</html>

Nastasja Bakovic18:02:48

I have no idea if I need to add a dependency somewhere else, any help is welcome

rolt22:02:12

you're importing react-development instead of react in the index.html file

rolt22:02:48

Also I'd recommend starting from a more recent project template

phill22:02:15

Or temporarily downgrade reagent! It used to include React, a great convenience.

kennytilton00:02:41

Changing the subject a tad, if you just want to get on with web programming, this will give you a working Reagent project:

lein new figwheel-main hello-world.core -- +npm-bundle --reagent 
I got that from here: https://github.com/bhauman/figwheel-main-template hth

😃 2
Matthew Twomey19:02:38

I feel like there is a built in way to do this that I’m missing (I’m probably crazy)?

(defn thing
  "Return (function value) if (funcion value) is truthy.
  Otherwise return value."
  [function value]
  (if (function value) (function value)
      value))

pppaul19:02:38

i assume most people will use something like if-let instead of writing a function

elken19:02:33

(or (function value) value)

☝️ 4
Matthew Twomey19:02:30

Thanks folks, some good options.

Ed06:02:52

Also, there's cond-> for use when threading

Andrew Leverette23:02:26

Does clojure.core have an absolute value function? I keep finding some conflicting information but I don't see it on ClojureDocs. Not a big deal either way, I'm just looking for clarification. My editor even references clojure.core/abs when a define my own abs function.

pppaul23:02:24

what version of clojure are you using?

Andrew Leverette23:02:28

Ah, it's in an alpha.

pppaul23:02:00

that's an old version, it should be in newer versions

pppaul23:02:32

1.11.1 i'm using that version, and i have abs in core

dpsutton23:02:34

(apropos "abs") for always up-to-date information

pppaul23:02:46

(defn abs
  {:doc "Returns the absolute value of a.
  If a is Long/MIN_VALUE => Long/MIN_VALUE
  If a is a double and zero => +0.0
  If a is a double and ##Inf or ##-Inf => ##Inf
  If a is a double and ##NaN => ##NaN"
   :inline-arities #{1}
   :inline (fn [a] `(clojure.lang.Numbers/abs ~a))
   :added "1.11"}
  [a]
  (clojure.lang.Numbers/abs a))

pppaul23:02:50

clojuredocs probably isn't up to date

Andrew Leverette00:02:52

Hmm, that's strange. When I call abs I get a runtime exception with an error "Unable to resolve symbol abs in this context"

dpsutton00:02:21

what’s the output of (clojure-version) ?

Andrew Leverette00:02:41

Oh, I just figured it out.

Andrew Leverette00:02:08

The project level Clojure version is 1.10.

Andrew Leverette00:02:27

Problem solved

👍 2
Jakub Šťastný23:02:19

Does the str fn call something on the types passed to it? As in (str (Person. "Jakub")), would it call a method on the Person type? I was expecting defining toString on Person would do the trick, but it gives me an error. I know I can define print-method for when used with println and the likes, but is there anything like that for str? https://gist.github.com/jakub-stastny/ed50811d1459f4376929c248137a00e3

hiredman23:02:50

str calls toString

hiredman00:02:13

So if you get an error then there is an error in your toString method

hiredman00:02:23

user=> (deftype F [] Object (toString [_] "foo"))
user.F
user=> (str (->F))
"foo"
user=>

Jakub Šťastný00:02:37

The code runs just fine if I call it stringify. The moment I replace stringify with toString, I get: `WARNING: Implicit use of clojure.main with options is deprecated, use -M Syntax error (IllegalArgumentException) compiling toString at (test.clj:11:20). No single method: toString of interface: user.Version found for function: toString of protocol: Version`

Jakub Šťastný00:02:29

I see it has something to do with the Object.

hiredman00:02:49

sure, because those inline definitions are grouped with the interface they are part of

hiredman00:02:38

so for your Version protocol there is no "toString" function, which is exactly what the error says when you replace stringify with toString

hiredman00:02:04

Methods should be supplied for all methods of the desired
  protocol(s) and interface(s). You can also define overrides for
  methods of Object. Note that a parameter must be supplied to
  correspond to the target object ('this' in Java parlance). Thus
  methods for interfaces will take one more argument than do the
  interface declarations. Note also that recur calls to the method
  head should *not* pass the target object, it will be supplied
  automatically and can not be substituted.

hiredman00:02:14

from (doc deftype)

hiredman00:02:40

toString is a method inherited from Object

Jakub Šťastný00:02:32

@U0NCTKEV8 how do I extend Version protocol with Object? Or am I thinking all wrong? I know I can't inherit from multiple protocols or at least it doesn't work, having (deftype QID [] Object Version).

hiredman00:02:15

Each spec consists of a protocol or interface name followed by zero
  or more method bodies:

  protocol-or-interface-or-Object
  (methodName [args*] body)*

Jakub Šťastný00:02:50

I seeee! got it!