Fork me on GitHub
Adam Helins12:02:13

WAT, the Lisp format for WebAssembly, seems to be valid EDN (given it doesn't contain comments such as (; comment ;) ). I find it really exciting as it opens many possibilities. However, there is one caveat: numbers are parsed as numbers and this can be tricky. For instance, WASM can declare unsigned 64 bits constants which could be parsed as BigInts and mess things up. The EdnReader doesn't seem to be very hackable. What I would like is to gain control over how numbers are parsed, which is defined by this static method on the EdnReader: Any idea? Or another direction (besides writing another s-expr parser)?


@adam678 has a postprocess hook in which you could possibly fix this. This is after the number has already been parsed but after that you can do whatever


I'm not sure what the exact problem is. What is the data you expect and what is the data that currently comes out?

Adam Helins12:02:08

For instance, the parsed WAT cannot be reliably stringified without doing a second pass and transforming numbers to symbols (at least BigInts).

Adam Helins12:02:37

(i64.const 18446744073709551615) -> (i64.const 18446744073709551615N) (`N` is appended) In that particular case, control over how BigInts are printed would be a simple solution (but haven't look at implementation)


@adam678 Yeah, you could do it like this:

user=> (e/parse-string "(i64.const 18446744073709551615)" {:postprocess (fn [{:keys [:obj]}] (if (instance? clojure.lang.BigInt obj) (symbol (str obj)) obj))})
(i64.const 18446744073709551615)
or just use a postwalk over the EDN


Note that symbols starting with a number aren't actually valid, so consider this a hack

Adam Helins12:02:45

Yep, too bad it requires a second pass just for this. Then I am a bit worried about subtle differences in floats since WASM is using a newer revision of IEEE754 (2019 vs 2008 in the JVM I believe). If there is any difference, then a second pass won't do, all numbers should be kept as symbols (albeit a hack).


In edamame this is not a second pass, it transforms while parsing


or if you mean, after you parse it to bigint, then yes


Maybe you can look at the source of tools.reader.edn, edamame is mostly based on this. It's not actually that much work to write a decent parser for an alternative EDN

๐Ÿ‘ 3
Adam Helins12:02:40

Hmmm, probably best to go that road for providing CLJS support anyways

Adam Helins12:02:14

But other than that, I find it remarkable. Being able to easily parse WASM as Clojure data structures. There is nothing better for handling a LISP than a LISP (and we have a good one).


Using this library for s3 service, everything is fine. Now need to add Amazon Gift Card Incentives API - which :api value should I provide on client creation? Tried dozen of words, such as :incentives, :AGCOD etc, but everytime got an error Cannot find resource cognitect/aws/AGCOD/service.edn . How can I find the right characters or where I can see the whole apis list? Or this library does not provide this api integration?


I think thatโ€™s not an AWS service, but an Retail API


Hm, that means I can not use library above for integration with it?



โœ… 3

Ok, thanks

Lyderic Dutillieux14:02:51

Hey, I just stumbled on this argument in favor of dynamic scoping : I was wondering if anyone knows a practical code sample that uses and takes profit of dynamic scoping ? I would like to see a concrete use case to soak up this concept

Lyderic Dutillieux15:02:01

Note : In Clojure, when I do

(defn get-x [] x)
I get a
Syntax error compiling at (REPL:1:15).
Unable to resolve symbol: x in this context
Whereas in EmacsLisp, I can define such a function when x is not declared yet :
(defun get-x () x)
And rebind x later, at runtime.
(let ((x 999)) (get-x)) => 999
In clojure, I can reach 'almost' the same behavior, given that I first (def ^:dynamic x 666) And then use something like binding
(def ^:dynamic x 666)
(defn get-x [] x)
(binding [x 999] (get-x)) => 999
x => 666


the errors you're getting that are different between elisp and clojure aren't to do with dynamic scope, but when the compiler/interpreter chooses to resolve the symbol. Clojure could equally have chosen to resolve the var x only when get-x in run without changing which x was resolved (you can do that by calling (resolve 'x))

๐Ÿ˜ƒ 3

I've used dynamic scope to hide things from an API in the past, so the caller doesn't need to know about some implementation details ... I'm never very proud of myself when I've done this, but it's usually to get around some other limitation placed on what I'm trying to do by another bit of code (which I think what that RMS quote is getting at) ... and it usually restricts the utility of that code in ways that I wish it didn't ... but I think that I'm just not clever enough to solve the problem without dynamic scope, so I use it and accept the trade off ๐Ÿ˜‰

๐Ÿ‘ 3
Lyderic Dutillieux21:02:24

I didn't know about (resolve 'random-symbol) I just tested, and it actually works as well, thanks for the tips. Cool feedback about your experience with this, tx ๐Ÿ˜‰

Ben Sless20:02:12

This might as well be an argument in favor of openness and extensibility. Dynamic scope allows that, but it's indeterministic. You can solve the same problem by passing a context map instead of fixed arguments, use protocols and multimethods

๐Ÿ‘ 3

Are dynamic vars an alternative to this?

Lyderic Dutillieux14:02:57

I guess they are a sort of workaround. In the case of EmacsLisp (where I discovered the concept of dynamic scoping), there is the concept of free variable, that are resolved in runtime, from several possible scopes (not especially global)

Lyderic Dutillieux15:02:15

I looked a bit deeper, you are right, it does the job


I thought dynamic vars were an implementation of dynamic scope ... and it looks different enough from lexical scope in Clojure, so you don't confuse the two, cos you usually want lexical scope


Is there an easy way to make failure messages part of clojure.test/are macro args? I have a large expr and many test values to run it over, and have to guess which test failed currently: I would like to avoid using a separate testing category and have to re-copy the are clauses over and over again. Thanks for any comment!


user=> (require '[clojure.test :refer :all])
(deftest foo
  (are [x y] (is (= x y) (str x " was not equal to " y))
    1 1
    2 2
    3 4))
user=> (foo)

FAIL in (foo) (NO_SOURCE_FILE:2)
3 was not equal to 4
expected: (= 3 4)
  actual: (not (= 3 4))

FAIL in (foo) (NO_SOURCE_FILE:2)
expected: (is (= 3 4) (str 3 " was not equal to " 4))
  actual: false


you can use the good parts of is


Woaw cool! I didnโ€™t think of that, thank you ๐Ÿ™‚


you'll see two test reports but small price to pay i guess


I usually avoid are and use doseq instead. ;)


Yes indeed, I could do without the extra report, but this is so much better than the alternative - was wondering whether writing a new/extended clojure.template for that would be much work..


@U04V15CAJ - thanks for the suggestion. How would you use the doseq to achieve the same efficiency as are?


..without the duplication? ๐Ÿ™‚


(doseq [[expected input] coll] (is (= execpted input (str input " didn't work")))


hmmm..interesting. I will try that inside are


instead of are


I think this is an alternative to are ๐Ÿ™‚ OK ths


That works well, thank you! A bit more to set up, with putting all the code at the end and partitioning but nice ๐Ÿ™‚


Yeah, like:

(deftest foo
  (doseq [[x y] [[1 1]
                 [2 2]
                 [3 4]]]
    (is (= x y) (str x " was not equal to " y))))
Obviously this is more verbose than are but for the life of me, I can't remember the are syntax

โž• 3

Actually it is the same as doseq, except for the code/data order and are doing the partitioning for you - it would be nice to have a new version of are that does the same. Thanks!


The partitioning is confusing imo. The grouping in a vector indicates... grouping :)


Indeed ๐Ÿ™‚ I agree, but I got used to the \n partitioning and not worrying about extra brackets, a matter of comfort I guess


My way to avoid are's double failure report is wrap in testing and add a true at test position. This pattern is captured in an IDE snippet so I don't have to remember various intricacies


Another argument to never look at are again ;)


Personally I'm a heavy arer and have convinced multiple teams of its benefits. There's also place for doseq, and occasionally use it. One day I'll like to summarize my thinking in an article


Another drawback of are is the forbidden de-structuring in the args list. During the few hours I have used it (in lieu of are), I have become more comfortable with doseq


I also like the natural flow of listing what I need (params + data), and as I type these the testing code foments..



(deftest my-test
 (are [...] (....)
  vals-1 "whoa val 1 bad!"

Sergiy Shevchuk17:02:07

Hi all. Please correct me, if itโ€™s not right channel for tech question. Iโ€™ll move it to other Have some trouble with core.match pattern. The goal is to detect arithmetic symbols in list, and construct final expression. E.G: โ€˜(+ 1 1 (* 2 1) (- 1 1)) -> 3 Was trying to parse list with next func:

(defn custom-matcher [x]
  (if (seq x)
    (clojure.core.match/match [x]
                   [([+ & r] :seq)] :plus
                   [([- & r] :seq)] :minus
                   [([* & r] :seq)] :multiplication
                   [([/ & r] :seq)] :division
                   :else nil))
But, it does not work properly. If Iโ€™ll call func with next argument: (custom-matcher โ€™(- 1 1)), works first case (:plus keyword), not :minus. Appreciate for advises.


you may need to quote symbols or something in match patterns


to match + and r are both the same kind of thing


and the way it treats symbols is as names to bind to parts of things


so you need some way to tell it that you intend it to check that the first thing is the literal symbol + and not just bind the first thing to the name +


so really it isn't working correctly for the plus case either


I don't know enough core.match to know what you need to do to make it match literal symbols, but I would just try quoting


(match '(- 1 2)
  (('+ & r) :seq) :plus
  (('- & r) :seq) :minus
  :else :nope)

Sergiy Shevchuk18:02:31

Yeeees, thanks! Quote helps!


the docstring mentions you can omit the vectors if you're matching on a single entity

Sergiy Shevchuk18:02:20

Thanks to all! Now I can continue my tests!