Fork me on GitHub
#beginners
<
2018-03-16
>
brunobraga00:03:50

hey guys, I JUST started clojure as my first function language

brunobraga00:03:09

can a good soul explain me why my fibonnaci works?

brunobraga00:03:27

(defn fibIterative
  [x]
  (if (= x 0) 0)
  (if (= x 1) 1)
  (loop [i 0, x1 1, fib 0, x0 0]
    (when (<= i x)
      (recur (+ i 1) (+ x1 x0) (+ x1 x0) (- x1 x0))
    ))
)

brunobraga00:03:37

I never return fib, how does it work haha

noisesmith00:03:40

those ifs do nothing

noisesmith00:03:21

and when returns nil if the condition is false - so when it stops looping, it returns nil

brunobraga00:03:08

nil? it is returning the proper value here, when x = 10, it returns 55

noisesmith00:03:25

I don't see how that code could do that

brunobraga00:03:58

can I add an else to when?

noisesmith00:03:00

when either evaluates its body (here a recur), or returns nil

noisesmith00:03:07

you can add an else to if

noisesmith00:03:37

perhaps you want cond instead of the ifs and the when

noisesmith00:03:58

(cond (= x 0) 0 (= x 1) 1 :else ...)

brunobraga00:03:34

makes sense, but why do you say the ifs do nothing?

brunobraga00:03:46

if x equals 0 for example, it should return 0 right?

noisesmith00:03:48

because nothing uses their value

noisesmith00:03:54

they return, and are ignored

noisesmith00:03:11

@brnbraga95: in clojure forms have values, and if nothing uses the value of the form, and it doesn't have side effects, it's as if it wasn't there

noisesmith00:03:25

(except maybe it makes your CPU spin I guess)

brunobraga00:03:31

I see what you are saying, thanks...it is just very different than what I used to..but I am starting to get it

justinlee00:03:25

slightly off topic, i wonder if the compiler could detect and warn about ignored non-side-effecting forms

noisesmith00:03:28

the problem is that with many forms you could redefine them

noisesmith00:03:41

it would be very dumb to do so, but you are allowed to redefine clojure.core/= for example

noisesmith00:03:03

for a warning it would still make sense though

justinlee00:03:14

could be pretty handy

noisesmith00:03:50

I bet you could do it as an eastwood extension

justinlee00:03:37

its a real tragedy that doesn’t work for cljs. looks like a really good tool

noisesmith00:03:57

it's very useful, catches the stupidest bugs without having to create unit tests or run the app, and tells you exactly where they are

Bert Weidenflunders01:03:21

hi, I'm wondering what's the difference between doing an anonymous function with fn and with #, besides just how you refer to the arguments

Bert Weidenflunders01:03:41

it seems like there is implicit "do" and other gotchas that make them different

Bert Weidenflunders01:03:19

is one implemented or exanpds to the other?

Ryan Radomski01:03:57

They in fact both expand to the same thing

brunobraga16:03:58

nice, thank you very much

seancorfield01:03:16

@brnbraga95 You can delete those two ifs -- as @noisesmith said, they don't do anything.

seancorfield01:03:37

When Clojure evaluates a function, it evaluates each form in the body, but only returns the last form's value. In your case, the (if (= x 0) 0) is evaluated but then ignored. Then the (if (= x 1) 1) is evaluated and ignored. Then the loop is evaluated, and it's result value is returned.

seancorfield01:03:34

(you'll also want to get used to idiomatic Clojure indentation -- which means not putting ) on its own line -- and using kebab-case instead of camelCase)

seancorfield02:03:27

Here's a version of your function, with the unused binding removed from the loop, the unneeded ifs removed, and more idiomatic naming and indentation:

(defn fib-iterative
  [x]
  (loop [i 2, fib 1, x1 1]
    (if (< i x) 
      (recur (+ i 1) (+ x1 fib) fib)
      fib)))
  
(fib-iterative 10)

Josh Horwitz02:03:34

What is the difference between planck and lumo?

mfikes02:03:39

Planck sits on top of JavaScriptCore and has imitations of Clojure functionality found in and clojure.java.shell. Lumo is running Node / V8 with all of the benefits you get with Node, and additionally can be used beyond just as a REPL / script runner, to compile ClojureScript files all the way through Closure. They are both built using self-hosted ClojureScript, so launch instantly, and share lots of code between them for this purpose.

mfikes02:03:16

Planck targets macOS and Linux, while Lumo does that as well as Windows.

dpsutton02:03:35

And just so you know, that was the creator of planck explaining the difference. This is a wonderful slack

Josh Horwitz03:03:02

Sure is, thank you!!

Josh Horwitz03:03:45

Really want to get into clojurescript

derpocious03:03:28

what I'm referring to is this

derpocious03:03:45

((fn [x] (str "hello " x)) "world") returns "hello world"

derpocious03:03:12

but (#("Hello " %) "world") gives me TypeError: "Hello ".call is not a function

derpocious03:03:51

also, what's the best way to use rand-nth on a map?

derpocious03:03:48

I guess I would want it to give me a map of just one key-value pair

mfikes03:03:29

@derpocious That anonymous function literal needs a str in operator position, right, as in

(#(str "Hello " %) "world")

mfikes03:03:11

@derpocious I'm curious what the best way to do the random singleton map problem is

(let [m {:a 1 :b 2 :c 3}]
  (rand-nth (seq m)))
succinctly gives you a random map entry, but
(let [m {:a 1 :b 2 :c 3}]
  (find m (rand-nth (keys m))))
might be more efficient. In either case, you'd have to convert the map entry into a singleton map.

derpocious04:03:16

how about this one though

derpocious04:03:24

(#((println "Hey")
   (str "Hello " %)) world)
;; #error {:message "ERROR", :data {:tag :cljs/analysis-error}, :cause #object[TypeError TypeError: Cannot read property 'call' of null]}
 
((fn [x] (println "Hey ") 
         (str "hello " x)) "world")
;; Hey 
;; hello world

noisesmith04:03:20

@derpocious fn has an implicit do, #() does not

derpocious04:03:11

is the shorthand actually a macro? Or is fn a macro? What does it expand to?

seancorfield06:03:52

@derpocious

user=> (macroexpand-1 '(fn [x] (println "Hi!") (inc x)))
(fn* ([x] (println "Hi!") (inc x)))
in Clojure, at least, and fn* is a special form. See https://clojure.org/reference/special_forms#fn

seancorfield06:03:24

"The exprs are enclosed in an implicit do."

mfikes11:03:29

Anything that starts with a # is handled by the reader,

user=> (read-string "#(str %)")
(fn* [p1__13#] (str p1__13#))

okwori11:03:30

Has anyone manage to get Lacinia graphql implementation to work with luminus? Including getting the Docs explorer of GraphiQL to work with the query root and schema.

Drew Verlee11:03:07

A simple question, but i feel like it has large implications. Does it make sense to create getters? (get-x [m] (get m :x) or just use the function directly and expose the implementation details? The first feels more resilient to change, for example, you change m from a hashmap to a atom containing a hashmap.

Russ Olsen13:03:21

My inclination is to not create getters like this. Most of the time you will be dealing with actual maps or map-ish things like records. And if you have a atom you should probably not be passing the atom around like that. With atoms you want to pull the value out early and write most of your code to deal with the value, not the atom.

Drew Verlee13:03:42

The getter would create an abstraction over the data so that change from a hashamp to atom would be shielded from the rest of the code. This is just an easy example i can think of. But yea, I agree, you should be using a hashmap for your data, so its hard to see how its helping that much…

Russ Olsen13:03:13

So this is certainly a matter of degrees and probabilities. So think of my answer as an estimate of the probabilities: these kind of wrappers don't help very often so my default is not to write them. And I rarely have instances where I need to add the wrappers later.

Drew Verlee13:03:05

That makes sense, I don’t foresee a large cost to making that change either. I too, don’t do it in practice, i was just trying to reason if i should have a different default mode.

Drew Verlee11:03:19

However, it also implies that you should create a lot of encapsulation functions that are more or less wrappers around clojure core functions.

mdrago102611:03:59

Hi guys. I’m using clj-http. I’m trying to get a CSV file. When I go to the page in a browser, the CSV file downloads do to the

"Content-Disposition" "attachment; filename=report.csv"
header. However when I make the same request using clj-http, the headers are correct and the body is ALWAYS empty

mbjarland13:03:18

@mdrago1026 I'm a bit off context but are you sending in the correct :as params to clj-http? take a look at the following example: https://gist.github.com/philippkueng/11377226

mbjarland13:03:43

@mdrago1026 maybe a more elegant (and less prone to out of memory errors) way would be something like:

(
  (:body (client/get "" {:as :stream}))
  ( "report.csv"))

Michael Fiano15:03:37

You know, I've been using [Common] Lisp for over a decade, and learning Clojure this week makes me just say "wow". This is going to completely change myself as a programmer.

jimbob15:03:46

Why can’t i recur from within a try/catch block? ex: i want to exponentially backoff and have specific error handling

(async/go-loop [last-delay 0]
      (try
        (do-things)
          (when @active
            (recur)))
        (catch Exception e
          (let [delay (if (pos? last-delay) (* 2 last-delay) 5)]
            (log/error (str " Exception: " (.getMessage e)))
            (async/<! (async/timeout (* 1000 delay)))
            (when @active
              (recur delay)))))))

jimbob16:03:14

but get “cannot recur from tail position” 😞

mfikes17:03:26

@ben.borders ClojureScript currently accidentally allows this, and this JIRA ticket illustrates the nonsensical behavior that can be produced given that you can have a finally block with side effects: https://dev.clojure.org/jira/browse/CLJS-2476

bringe19:03:15

I'm writing an api wrapper library and I'm wondering the best way to get user secrets to authenticate. I have the authentication part currently working by me setting the secrets in a .env file that is ignored in source control, and I load them into a config map and the auth functions use the config map. That's probably not ideal for a library that other people would use. I come from a more OOP background so I'm trying to rewire my thinking, and I know in that space one might create a client object with methods. The constructor of the client would take in the secrets. Then all methods on the client would have them as object state. What's a good way to achieve something similar in Clojure? Supply an atom for secrets in the namespace that the user can set, and the functions that require auth use that atom? I'm probably thinking about it poorly.

greglook19:03:28

my approach would be to make a protocol defining the API, then a record which implements that protocol and has internal attributes to store whatever auth info you need

noisesmith19:03:33

@brandon.ringe if you supply the atom, your library is now a singleton and they can't use two sets of credentials in one codebase

noisesmith19:03:06

if they supply the atom (or better yet, provide credentials and then you return an object they can use - even if that object is just a function), then it's properly reusable

noisesmith19:03:12

@brandon.ringe if there's an advantage to disconnecting data from functions, do so (it's idiomatic in clojure), but that doesn't mean you need to disconnect data from code in all cases. If the function doesn't work without auth, why not a first class function that takes auth and returns a usable function? if it's better represented as multiple functions, then maybe it makes sense to make a record implementing some protocols

noisesmith19:03:05

(which is pretty much what @greg316 described while I was busy typing heh)

bringe19:03:49

Ohhh I see

noisesmith19:03:36

IMHO it's good to go with a default of "separate data and functions" because traditional OO overemphasizes tying those together (similarly avoid defining new data types and interfaces because OO as it's taught today overemphasizes these things), but we still have the tools for interface abstractions and defining types and methods if it actually fits the problem

bringe19:03:26

When you say separate data and functions, would that be the case of creating a http-client record that takes in the auth params and implements a protocol with the functions that use those auth params, or is that not separate? Sorry a little confused

greglook19:03:29

another upside of splitting out the protocol definition from the client code is you can easily sub in mocks or other implementations: https://github.com/amperity/vault-clj/blob/develop/src/vault/client/mock.clj

greglook19:03:59

@brandon.ringe I think you’ve got it - defining the protocol makes a set of functions for the consumers who want to use that API, but don’t necessarily care about the mechanics of how those requests are serviced “under the hood”. Whereas the authentication data is state which would be part of the client record you implement the protocol on.

greglook19:03:43

so write your business logic against the protocol and you can test it in isolation by giving it a mock protocol implementation, then in production you can construct a ‘real’ client with the required auth values and pass that into your business logic functions

bringe19:03:25

It's all coming together now lol

noisesmith19:03:55

@brandon.ringe in general what I mean by separating data and functions is eg. the way hash-maps and vectors work. We have N reusable container types, and N functions that all know how to use multiple collection types.

noisesmith19:03:37

a record implementing a protocol is tying data to functions - because the protocol methods are each functions that accesses the data you put in the record

noisesmith19:03:10

(and can only be called via an instance implementing their protocol or interface)

bringe20:03:29

@noisesmith I see now. But in cases where secrets are required this is a scenario in which that is ideal? Or do some people prefer to never use records with protocols that access record state, and rather prefer to use functions that return functions with the needed data enclosed (closure)? But in a case where say, 20 functions need the same auth params, you wouldn't want to have 20 higher order functions and require the user to pass auth params to every function right? I don't know what other options there are (literally don't know not saying there aren't).

noisesmith20:03:24

@brandon.ringe right - if the functions are useless without the data feel free to tie them, and a defrecord over a protocol is a good simple way to do that

noisesmith20:03:42

just saying that it should still be avoided unless it fits your problem (and it usually won't)

bringe20:03:59

Oh I see. Try not to do that, unless it really seems like the right thing

noisesmith20:03:02

too often I introduce someone to defrecord and they take that to mean "OK I do OO the way I learned in my java class now and use defrecord" - just trying to be clear that it shouldn't be your go-to - make things with plain data and plain functions first

greglook20:03:04

conversely, if you find yourself writing a lot of related functions that all take some “context” map, that’s a sign you should maybe make a protocol/record instead

greglook20:03:29

like (defn foo [ctx a b] ...), (defn bar [ctx a] ...), (defn baz [ctx c d e] ...) etc

oVerde20:03:09

How do you guys solve the SSR with reframe now a days? Is delivering an static page through Hiccup while ReFrame initialize on the client?

bringe20:03:04

@noisesmith @greg316 Makes sense. Thanks for the help.