Fork me on GitHub
#clojure
<
2018-05-18
>
plins03:05:44

hello everyone, I'm trying to learn more about macros and want to replicate elixir with statement .. (hope the code is self explanatory) I' want it to look like this

(defn f1 [] [:foo "bar"])
(defn f2 [str] [:ok (str "hello " "bar")])
(defn ferror [str] [:error :vishh])

(with [[:foo str] (f1)
       [:ok str2] (f2 str)
       [:ok str3] (ferr str2)]
  ;(do-stuff-case-succeed str3)
  :else
    [:error err] :stuff
    [:error :vish] :another-stuff)
Im sure i will have to use core.match, but i dont know where to begin... any similar macros so I can look into the source?

dpsutton03:05:54

That looks like just the let macro

dpsutton03:05:30

Which you can mimic naively by creating functions involved with their binding. Ie (let [x 3] ...) Is ((fn [x] ...) 3)

plins03:05:22

id like the bindings to be interrupted if the next pattern doesnt match

plins03:05:42

and the result to be pattern matched again with the bindings of the :else branch

plins03:05:21

like an implicit chain of match between the lines

benzap04:05:32

I think a close alternative is cond->

plins04:05:24

i dont know even if its possble to syntatically do what I want

benzap04:05:28

This kind of makes me think of better-cond https://github.com/Engelberg/better-cond

benzap04:05:23

the :if-let functionality in better-cond is similar to what you want I think

dpsutton04:05:38

If let doesn't branch on the successful destructure though which is required here. I was playing around with a "let-if" macro recently

dpsutton04:05:28

Seems like you could rewrite this as successive match calls where you propagate the error catches as the wildcard matches in each match form

dpsutton04:05:42

Or grab a monad library

benzap04:05:47

hmm, i'm not sure then. The guy who made better-cond was pushing pretty hard to get his stuff in clojure.core, but the clojure devs chose to include as-> and cond-> over his advanced conditional stuff. So I wonder if it could be done succinctly with cond-> and as->

benzap04:05:19

so I attempted to write it... and it doesn't seem succinct.

benzap04:05:03

What you're doing is similar to a Monad, so you could try checking out a clojure library that implements monads and monoids

plins04:05:16

im aware its kinda a monadic bind

plins04:05:37

but id like to use variants instead of more complex stuff

plins04:05:52

somewhat of a try catch also

plins04:05:20

but using variants and erlang like tuples to represent monadic operations

plins04:05:53

stuff like this

plins04:05:52

easier to match stuff with datomic also

dpsutton04:05:58

If you try the nested match version I mentioned above I think it would work but you would be flirting with method size limitations. Match can get pretty big in the decision trees it generates

dpsutton04:05:11

What do you mean by variants

plins04:05:40

peek this youtube video, it talks about variants

plins04:05:05

where I can learn about method size limitations?

dpsutton04:05:54

I'm in a car so can't watch only read.

plins04:05:07

maybe i can try to CPS the match chaining inserting the fetched data in the context of the next match with a let?

plins04:05:43

heres an article talking about it

dpsutton04:05:22

That sounds interesting. I'd like to see your solution. And thanks for the link!

plins04:05:29

guess i will go for the naive approach

plins04:05:46

after that i can try to fix this 64kb issue

plins04:05:58

thanks for the info 😃

grav09:05:57

Would anyone care to guess what I actually get if I specify lein-release "1.1.3" in my project.clj under :plugins?

grav09:05:38

There is a lein-release plugin, but that's in version 1.0.9. Then there's a lein-release plugin that now is lein-tar which has version 1.1.3.

grav09:05:51

To me it seems like npm but worse ...

joelsanchez14:05:13

I need to do synchronous IPC between two microservices - I was looking at Thrift but it doesn't seem that the Clojure community likes it much What would be a better option? Would ZeroMQ be good for this?

mpenet15:05:49

it depends on the requirements really, ex can you afford to drop messages etc

joelsanchez15:05:04

sure -- I can just repeat the call

joelsanchez15:05:33

just wanted to know what options are there - I can try them myself

jgh15:05:37

zeromq works pretty good and is tcp so you dont typically have to worry about dropping packets

jgh15:05:03

i think it has a udp mode but that’s not the “default”

jgh15:05:50

you’ll still need to do your own serialization/deserialization though, 0mq doesn’t provide those facilities the way thrift does

jgh15:05:20

you could use protobuf or whatever with it easily though.

👍 4
joelsanchez17:05:59

thanks! I will try using 0mq then

👍 4
plins16:05:51

why

(match [1 2 3]
       [a 20] :foo

       else 1))
fails while
(def list [1 2 3])
(match list
       [a 20] :foo

       else 1))
succeeds?

hiredman16:05:49

what do you mean by fails and succeeds?

plins16:05:30

raises an exception

hiredman16:05:57

assuming match is from core.match, every test for core.match and every example I've seen passes match a literal vector

hiredman16:05:09

and uses :else not else

hiredman16:05:17

what exception?

plins16:05:49

CompilerException java.lang.AssertionError: Pattern row 1: Pattern row has differing number of patterns. [a 20] has 2 pattern/s, expecting 3 for occurrences [1 2 3],

hiredman16:05:30

how is that not clear?

plins16:05:54

so why

(def list [1 2 3])
(match list
       [a 20] :foo

       else 1))
does not raise the same exception?

plins16:05:30

replacing list with a literal raises an exception

the2bears16:05:39

I haven't used match at all, but I suspect the 2nd example is matching against the list as a single thing, rather than destructuring and looking for 3 elements>

hiredman16:05:48

I dunno, but given that every example and doc and test I've seen passes a literal vector, I would expect that is "undefined behavior"

plins16:05:49

if im not missing something ...

the2bears16:05:24

ie. does the list match rather than do the elements of the list match

plins16:05:35

you are right

plins16:05:42

quoting solves the problem

the2bears16:05:00

My watch must have stopped at this very moment 😉

plins16:05:03

(match '[1 2 3]
       [a 20] :foo

       else 1))
runs fine

hiredman16:05:25

if you look the tests for match, when matching a single element they do [x] not x

the2bears16:05:42

If you need this, you could destructure your 'list'

the2bears16:05:53

before the match I think

plins16:05:58

still cant make sense out of it

wolfgangshi18:05:49

`(defmacro match (let [[vars clauses] (if (vector? vars) [vars clauses] [(vector vars) (mapcat (fn [[c a]] [(if (not= c :else) (vector c) c) a]) (partition 2 clauses))])] ..... ~(clj-form vars clauses)))) source code of the match macro. When [1 2 3] is passed in the if clause returns true, whereas '[1 2 3] will results in false. In the latter case, the transformation will further wrap vars and patterns in clauses, a step that makes them pass the checks in a later stage. What I don't understand is that (vector? '[1 2 3]) returns true, but in the macro (type vars), given vars is '[1 2 3], results in clojure.lang.Cons.

noisesmith18:05:18

@wolfgangshi '[foo] is (quote [foo]), outside a macro it's simplified to ['foo], inside a macro it remains a list

noisesmith18:05:42

Clojure 1.9.0                                                                                                                                                                                                                         
(ins)user=> '[foo]                                                                                                                                                                                                                    
[foo]                                                                                                                                                                                                                                 
(ins)user=> ''[foo]                                                                                                                                                                                                                   
(quote [foo])                                                                                                                                                                                                                         
(ins)user=> '''[foo]                                                                                                                                                                                                                  
(quote (quote [foo]))                                                                                                                                                                                                                 

wolfgangshi18:05:25

user=> (defmacro ttt [v] (if (vector? v) [:yes (class v)] [:no (class v)]))
#'user/ttt
user=> (ttt '[1])
[:no clojure.lang.Cons]
but
user=> (class (quote (quote [1])))
clojure.lang.PersistentList
didn't expect clojure.lang.Cons here. This is irrelevant to the original question, but something puzzles me.

noisesmith18:05:40

@wolfgangshi: a cons is a list-like thing, the processing of args likely transforms the type

noisesmith18:05:07

any code that works on PersistentList but not Cons is insufficiently general, but there's lots of code that's legitimately picky about list vs. vector

noisesmith18:05:53

I wouldn't expect (def foo '[a 0]) (let foo a) to work and return 0 either, and that's effectively what you are trying to do to match

plins20:05:08

how could i write a macro which nest lets ? the output would be something like

(nest-lets 3)
;=> (let [a 1]
         (let [a 1]
           (let [a 1] )))

schmee20:05:16

you can have multiple bindings in a let:

(let [a 1 b 2 c 3])

plins20:05:30

yeah im concern about how to nest expressions with macros

plins20:05:53

i used the let as an example but id really like to nest match but that is beyond my question, i simply would like to now how can I nest an expression inside the body of the other, for n levels, using macros

noisesmith21:05:46

The simple way to do it is a recursive helper function. A macro is just a function that has its return value evaluated to create a new form (plus the metadata to tell the compiler to treat it that way and some magic args)

noisesmith21:05:20

almost had it...

plins21:05:47

a recursive function inside the macro not the macro itself?

noisesmith21:05:56

right, recursive macros are tricky, much simpler to do a recursive function inside a macro, returning the same thing you'd have the macro return

plins22:05:01

i ll try it out thx

schmee21:05:29

(defn nest-form* [n form]
  (if (= n 1)
    form
    (concat form [(nest-form* (dec n) form)])))

(defmacro nest-form [n form]
  (nest-form* n form))

👍 4
lwhorton21:05:52

does clojure have a mechanism for interning a ns kind of behind the scenes? my particular use case is that I don’t want each of my propert tests to have to require:

[cljs.spec.alpha :as s]
    [cljs.spec.gen.alpha :as gen]
    [clojure.test :as t :refer [deftest testing is are]]
    [clojure.test.check :as tc]
    [clojure.test.check.clojure-test :refer-macros [defspec]]
    [clojure.test.check.generators :as g]
    [clojure.test.check.properties :as prop :include-macros true]
    [testing-util :refer [passes?]]
and it’d be much more convenient if these could be made available “globally” for all sub modules like test/feature/foo_test.cljs

schmee21:05:34

make a namespace that requires all those files and then require that namespace in all the other ones

noisesmith21:05:17

that still doesn't make the aliases

ghadi21:05:42

Scala implicits

😄 4
schmee21:05:42

you could cobble together a helper function that calls require and alias manually, add that to a helper file, and call that first thing in each namespace that needs them

lwhorton21:05:25

ah but i dont think i can do that in cljs, since require can’t be dynamic ( at least im pretty sure)

schmee21:05:57

sorry, didn’t notice it was cljs, then I have no idea 😄

noisesmith22:05:01

these things are possible, but don't seem like good style for clojure code. I might make an exception for dev-time only tooling, but for actual namespaces used in the codebase it obfuscates things

noisesmith22:05:24

haha -- from the readme.md

is this a good idea?
Maybe!

dominicm22:05:34

Yeah, exactly my thinking too