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: https://github.com/clojure/clojure/blob/ecd5ff59e07de649a9f9affb897d02165fe7e553/src/jvm/clojure/lang/EdnReader.java#L314
Any idea? Or another direction (besides writing another s-expr parser)?
@adam678 https://github.com/borkdude/edamame 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?
For instance, the parsed WAT cannot be reliably stringified without doing a second pass and transforming numbers to symbols (at least BigInts).
example?
(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 EDNNote that symbols starting with a number aren't actually valid, so consider this a hack
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
Hmmm, probably best to go that road for providing CLJS support anyways
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 https://github.com/cognitect-labs/aws-api 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 http://Amazon.com Retail API
Hm, that means I can not use library above for integration with it?
Correct
Ok, thanks
Hey, I just stumbled on this argument in favor of dynamic scoping : https://stackoverflow.com/a/3792028 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
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
Are dynamic vars an alternative to this?
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)
I looked a bit deeper, you are right, it does the job
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)
)
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 π
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!
Ideally:
(deftest my-test
(are [...] (....)
vals-1 "whoa val 1 bad!"
...))
user=> (require '[clojure.test :refer :all])
nil
(deftest foo
(are [x y] (is (= x y) (str x " was not equal to " y))
1 1
2 2
3 4))
#'user/foo
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
nil
user=>
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. ;)
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)
:minus
Yeeees, thanks! Quote helps!
the docstring mentions you can omit the vectors if you're matching on a single entity
Thanks to all! Now I can continue my tests!
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..
@borkdude - 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
thx
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
syntaxActually 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 https://raw.githubusercontent.com/zenmacs/.emacs.d/62d24aac7b2e57b3d31123d5de1c5980651054d5/snippets/clojure-mode/are so I don't have to remember various intricacies
Another argument to never look at are again ;)
ha
Personally I'm a heavy are
r 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
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 π