Fork me on GitHub
#beginners
<
2016-12-10
>
drewverlee01:12:43

how would i go about making this work (= “pos?” (let [f pos?] (??? f)))

drewverlee01:12:19

str works if called on the original function, but not if its a … reference?

drewverlee01:12:45

googling around is getting me no where

gdeer8101:12:55

What does str return when you call it on f?

gdeer8101:12:50

I'm afk so I don't have my trusty repl

gdeer8102:12:16

Oh wait, I keep forgetting my phone has a repl

gdeer8102:12:44

In both cases I get "[email protected]"

gdeer8102:12:35

This is on clojure version 1.7 since the repl for Android hasn't bumped to the latest version

kyle_schmidt02:12:53

Thank you @seancorfield for the resources!

gdeer8102:12:16

@drewverlee I forgot to mention you in my replies. Is what I got similar to what you got?

drewverlee02:12:24

@gdeer81 i get the same. I’m curious how i might get just the plan name. but its not that big of a deal.

gdeer8102:12:58

The issue is that when you ask clojure for the details of something it is very precise

gdeer8102:12:51

This might require a custom function to parse the long name into the simple name

gdeer8102:12:25

(clojure.string/replace (first (re-seq #"pos_QMARK" (str pos?))) #"_QMARK" "?")

gdeer8102:12:40

A naive implementation but demonstrates what I meant

drewverlee03:12:05

@gdeer81 gotcha. i can use the detailed name just as well.

gdeer8103:12:17

Too late, I've already spent an hour trying to figure out the regex that will match every possible name between the $ and @ in the long name

gdeer8103:12:56

Once I figure it out I'll create a library for it and throw it up on clojars

raspasov08:12:46

@gdeer81 just joined - what are you trying to figure out?

roelofw09:12:14

I have to calculate the lcm of several numbers.

roelofw09:12:39

I can calculate it for 2 numbers like this :

(defn gcd [a b]
  (if (zero? b)
    a
    (recur b (mod a b))))


(defn lcmv
  [a b]
  (/ (* a b) (gcd a b)))  

roelofw09:12:17

but how can I make it work with more then 2 numbers. Please some tips and not the answer

gdeer8110:12:25

@raspasov returning the simple name of a function as a string (let [f inc] (stringify-var-name f)) => "inc"

roelofw10:12:32

@gdeer81 do you ever sleep ?

ashnur10:12:41

i am going to try again today with clojurescript

roelofw10:12:19

@gdeer81 not healty

ashnur10:12:20

sleep is overrated

gdeer8110:12:06

@roelofw okay one last tip and then I'm out. See if you can use reduce

ashnur10:12:08

although i am only 34 and on a day-to-day basis i need at least 6 hours to sleep, that is, waking up with less is near impossible lately

roelofw10:12:42

@ashur a problem I know

gdeer8110:12:58

Not sure if that tip is too vague or if I gave away the answer

ashnur10:12:28

you gave away the answer

roelofw10:12:33

I think I have a idea

roelofw10:12:20

reduce lcvm 0 args

roelofw10:12:18

@ashnur I have some days I can sleep some 14 hours a day

ashnur10:12:52

anyone can sleep a lot 😄

ashnur10:12:23

although sometimes it's hard 😞. like when you are sick or in pain. so not quite true that anyone 😞

ashnur10:12:02

but generally speaking i always tell people that they should learn to meditate on a basic level and they would then be able to control their sleep much better

roelofw10:12:39

I sometimes to medidate on my yoga lessons

gdeer8110:12:18

This is a conversation for off-topic

roelofw10:12:09

yep, I will stop with this one

bcbradley10:12:04

I'm getting an error message I can't figure out for this code:

(defn transit [we]
  (let [transform (comp
                    (map (partial choose we))
                    (mapcat (fn [me] (make (:choice me) we me))))
        reduction (fn [acc me]
                    (let [me-gen (get me :generation 0) acc-gen (get-in acc [0 :generation] 0)]
                      (cond
                        (= me-gen acc-gen) (conj acc me)
                        (> me-gen acc-gen) [me]
                        :else acc)))]
    (transduce transform reduction [] we)))
Error:
Exception in thread "LWJGL Application" clojure.lang.ArityException: Wrong number of args (1) passed to: core/transit/reduction--73

bcbradley10:12:16

why would transduce call the reduction function with one argument?

olslash11:12:11

transducers have a different signature than reducers

olslash11:12:22

can be called with 0,1,2 args i think

olslash11:12:45

sorry, its a fn thats called with one arg, which returns another fn of 0,1,2 args

olslash11:12:49

always confuses me

olslash11:12:39

JK "f should be a reducing step function that accepts both 1 and 2 arguments, if it accepts only 2 you can add the arity-1 with 'completing'."

roelofw11:12:20

How can I test this function with clojure.test :

(defn read-data-painting
  "Reads the title, description, date , collection, colors and url of a image"
  [id]
  (let [art-objects (-> (str "" id )
                        (client/get {:as :json :query-params {:key (env :key) :format "json" :type "schilderij" :toppieces "True"}})
                        :body
                        :artObject)
        name        (-> art-objects
                        :principalMakers
                        first
                        :name)
        description (:description art-objects)
        date (get-in art-objects [:dating :year])
        collectie (first (:objectCollection art-objects))
        colors (:colors art-objects)]
    {:id id :name name :description description :date date :collectie collectie :colors colors}))  

roelofw11:12:58

I think I have to mock up the client-get part. Do I have to copy the whole file into a test function or can I do it another way?

bcbradley11:12:26

it doesn't say in the documentation at http://clojure.org/reference/transducers or https://clojuredocs.org/clojure.core/transduce what the arity-1 overload on f actually does. What is it used for?

olslash11:12:47

"If init is not supplied, (f) will be called to produce it"

olslash11:12:14

although i dont know why in your case it would need to

bcbradley12:12:44

@olslash (f) is zero args, not one arg

olslash12:12:09

yeah, sorry, i shouldnt be trying to answer questions during a late night binge -- best i can say is give it a 1ary impl and put a print in there 🙂

roelofw16:12:57

Nobody to help me ?

bigkahuna19:12:06

@roelofw I think function should be decomposed. It has too many responsibilities. Something else should grab Json that you want to deconstruct. The Json should be past into your function. Then you have a separation of responsibilities. I.e. SRP. If you follow that the test should become easier

kumarshantanu19:12:02

@roelofw Are you trying to simulate client/get for a test condition?

roelofw19:12:40

@kumarshantanu yes

roelofw19:12:30

@bigkahuna so seperate function that takes the id and returns the response ?

kumarshantanu19:12:11

I think you can get started with (with-redefs [client/get (fn [& args] mock-value)] …test-code-here...)

kumarshantanu19:12:39

but remember that with-redefs is only for very simple scenarios

roelofw19:12:47

and I can use that instead of the client/get so I copy the function and change that @kumarshantanu

kumarshantanu19:12:56

@roelofw Yes, with-redefs lets you temporarily mock vars

bigkahuna19:12:49

If the client/get is a REST call then it should be in its own function IMO. You can pass it into the function as well as the Id. That way you can mock it to return a stock Json output so your test can then concentrate on testing that the function destructs the json as expected.

roelofw19:12:36

oke, I have to think then how to do this on a right way

kumarshantanu19:12:11

I think ideally (as @bigkahuna suggested) you should define your fn thusly: (defn read-data-painting [http-get id] …), so that you can call http-get fn instead of client/get, and during test you can pass a mock version for http-get

bigkahuna19:12:53

Also bear in mind that when testing, you ideally want to test functions in a pure fashion so any collaborators should be externalised or to put it another way there shouldn't be any side effects. Having the REST call embedded within the function is a side effect which makes your function harder to test than it should be. You shouldn't need a real endpoint to test your function's logic. By passing in the function for REST means the function for destructuring can be tested in a pure fashion. In an OO world, this would be seen as externalising your collaborators so they can be mocked.

kyle_schmidt20:12:15

@seancorfield is there a way in Clojure to define my MySQL relationships in an ORM-like model? Or do I create tables and their relationships strictly in my database?

kyle_schmidt20:12:08

I’m wondering if there is a way when I run my application to build all of its database table dependencies?

agile_geek20:12:04

@kyle_schmidt think about what that looks like in a functional world. If you have data and functions then what you are asking is can I build a database from a data model. This can be done and is part of the concept behind Datomic. However, not sure anyone has written anything to auto generate a relational database schema from a Clojure data structure.

roelofw20:12:19

@kumarshantanu I need that url call there because it depends on the id . So I think I cannot make it pure

kyle_schmidt20:12:58

@agile_geek It sounds like a record might be a good abstraction for a sql table?

agile_geek20:12:08

@kyle_schmidt yeah or just a plain map

agile_geek20:12:28

I'm sure it would be possible to generate a relational schema from a plumatic schema or Spec definition

roelofw20:12:35

@agile_geek Do you know a good way to test a function which is not pure because It's needs a call to a external source ?

agile_geek20:12:41

It's not ideal but you can use with-redefs to redefine the fn that calls the external source but that's not ideal

agile_geek20:12:17

Or you can pass the fn that calls the source as an argument to your function.

agile_geek20:12:51

In either case in the test you can supply a dummy fn that returns the data you want for your test

roelofw21:12:58

Oke, so make for all api calls seperate functions and in the test make a function that return the same as the real function, @agile_geek ?

agile_geek21:12:03

Yes. Make the api call fns arguments to the function you want to test so in the production code you pass the real api fn and in test u pass the dummy one. Also you can use multiple arity versions of production code. One arity has the api fn and one that doesn't. The one that doesn't calls the one that does passing the real api fn.

agile_geek21:12:46

In the test you use the version with the extra api call argument

roelofw21:12:21

Oke, I have to think how to do that. I think I can better look for a bette way to achieve the same

roelofw21:12:42

The version that I made is it seems not testable at all

agile_geek21:12:06

This is a pretty std way to mock external dependencies and is quite powerful

roelofw21:12:23

oke, I will think about it . At this moment I understand what you mean but do not see how to make it work in real

roelofw21:12:34

Thanks for the help

agile_geek21:12:37

Ok have a think about it. Basically you are using high order functions. Passing a function as an argument to another. Good luck

seancorfield21:12:11

@kyle_schmidt I’m the wrong person to ask about ORM-like stuff — I’ve given conference talks on why ORM is evil and I’ve ranted and raged on mailing lists about that topic too!

seancorfield21:12:38

I would just use hash maps for the rows and avoid formal “modeling” as much as possible, to be honest. I don’t even like FK constraints: I treat the database as a dumb persistence system for the most part.

roelofw21:12:31

@agile_geek oke, then I have to hit some books to learn more about higher functions work in clojuer

roelofw21:12:04

that is a subject I did not see in the clojure koans or the first challenges of 4clojure

agile_geek21:12:00

You've probably already seen simple examples of passing a fn to a fn

kyle_schmidt21:12:16

@seancorfield: interesting. So do you build your tables in your DBMS and then build an application that references the tables? Separating data from logic?

seancorfield21:12:27

@kyle_schmidt The DBA is responsible for making sure the database schema is efficient and correct (in terms of data types). They don’t like FK constraints either so we’re in agreement there. Inside Clojure, tables are just sequences of hash maps (and rows are just hash maps). At the boundaries of the system, we read hash maps from the database and write data back to the database.

seancorfield21:12:19

After all, our application is just a set of functions that operate on data.

kyle_schmidt21:12:03

Gotcha. I guess I'm speaking from the DBA and engineer perspective since I'm in charge of both. Was looking to see if I could manage this somehow in one place

agile_geek21:12:22

I build my apps like that even without a dedicated DBA. Let relational databases do what they do best

seancorfield21:12:37

Not sure what “duplication” you’re trying to avoid tho’ @kyle_schmidt ? Unless you want “specs” of your tables, you’re not really going to have anything in Clojure that duplicates the DB schema… and I wouldn’t consider the Clojure / DB edge to be a system boundary that needed validation, to be honest (unless you’re specifically dealing with a 3rd party system’s DB that you can’t trust — but then you didn’t control the schema for that anyway).

roelofw21:12:19

@agile_geek oke, so your are talking about this read_date(f ) where f is in production the client-get and in test only something like this {:id 1 :name '" Roelof"} as a example

agile_geek21:12:35

I agree with @seancorfield. I would put Specs on the api side of my app but not the outbound database calls as you can control the data shape yourself.

agile_geek21:12:50

@roelofw a fn that returns that map in test but yes...except your syntax for read_date is odd in that example

seancorfield21:12:11

(read-date f) — stop using _ in Clojure names!

roelofw21:12:34

oke, I think I understand the idea

seancorfield21:12:50

Then in tests you can say (read-date (constantly {:id 1 :name ”Roelof”}))

roelofw21:12:05

now time to experiment with it the next few days

roelofw21:12:36

oke, both thanks, I think I get the idea now

roelofw21:12:57

as I said time to experiment with it the next few days

agile_geek21:12:37

Thanks @seancorfield struggling to type fast on phone... especially after 3 G & Ts 😉

kyle_schmidt21:12:53

Coming from a Python background and the SqlAlchemy module I can create Python classes which act as an abstraction and spec of SQL tables. If I change the spec of a class then it's reflected in the database without me having to login to the database itself

kyle_schmidt21:12:09

And call methods on these classes which are SQL queries under the hood

kyle_schmidt21:12:26

But I shouldnt worry about spec'ing out the tables in Clojure because it complects data and logic? I should just write functions that query those tables.

seancorfield22:12:53

There are no classes in Clojure.

seancorfield22:12:09

This is what I meant about ORM being evil.

seancorfield22:12:36

In Clojure, it’s just data. If you change your DB schema, when you load a row, it just gives you data.

seancorfield22:12:51

If you add a column to a table, that hash map has the extra key already.

seancorfield22:12:21

If you remove a column, that hash map no longer has that key. If you change the data type of a column, the hash map just has a different value type for that key.

seancorfield22:12:01

There is no code corresponding to the schema. In an OOP language, you have to specify code — an attribute definition — for every column.

seancorfield22:12:10

(and you have to have a class for every table).

kyle_schmidt22:12:14

Gotcha, sorry for making you have to spell it out for me but I definitely see where you’re coming from now.

seancorfield22:12:38

OOP is what creates that extra work, that duplication. ORM is a solution to a problem that simply doesn’t exist in FP.

agile_geek22:12:32

Yeah a poor soln to Object Relational impedance mismatch

kyle_schmidt22:12:36

interesting! Ya that makes a lot of sense. I like that approach a lot!

seancorfield22:12:51

With java.jdbc, you can say (jdbc/get-by-id db-spec :table-name the-key) and that’s it — it’ll get you a row of data from any table, given the PK. Nothing else is needed.

seancorfield22:12:21

(jdbc/insert! db-spec :table-name map-of-data) will (attempt to) insert the given hash map as a row in the table. As long as you have no additional keys in the map and you aren’t missing any non-default keys, it’ll just work. The JDBC driver itself does all of the work for you.