Fork me on GitHub
#beginners
<
2020-02-01
>
jakubl01:02:33

speaking of spec - what is the preferred way? using s/valid? in :pre :post of defn - or using the s/fdef? and when might one choose one versus the other?

seancorfield01:02:00

@jakub696 It depends what you want to achieve.

seancorfield01:02:25

We use Spec in lots of different ways -- see https://corfield.org/blog/2019/09/13/using-spec/ -- s/fdef works with instrumentation and generative testing so that's dev/test only (well, you can run instrumentation in production but it's often an overhead you won't want).

seancorfield01:02:47

s/valid? and s/conform are great for production code working with data.

jakubl01:02:17

basically i'm static type a guy - and i want my code to crash if garbage goes in (i'll likely want this in production as well - overhead be damned)

seancorfield01:02:32

:pre/`:post` conditions are not very commonly used in Clojure as far as I am aware.

jakubl01:02:27

i am more concerned with my code - not really seeing the need to enable the assertions for 3d party libraries etc.

seancorfield01:02:52

If you're a "static type a guy", you may not find Clojure to your tastes -- Spec isn't anything like a type system, and all checking is runtime-based (it can do things a type system can't, but there are also things type systems provide that Spec does not).

seancorfield01:02:24

If you try to write Clojure as if you're using a type system, you may find yourself with some very non-idiomatic (and slow) Clojure code. Spec is good for "system boundaries" and for data validation, as well as providing additional support during dev/test. It's worth observing that instrumentation only checks function arguments -- not return values (nor the :fn predicate relating return values and arguments). The latter are only for generative testing. Also, if you spec a higher-order function, instrumentation will do generative testing on arguments that are spec'd as functions in certain ways, which can be a real surprise to some folks.

jakubl01:02:56

I totally get the part about it being slow - because it's done in runtime - but i find even in static type systems - i still do some sort of validation at runtime - i mean compiler will ensure int is coming for say persons age - but i still need to validate somehow that age is between 0 and 150 at runtime - the argument about system boundries i also get - but i would argue - in larger projects it's harder to say where those boundaries are - and i tend to be defensive by default

andy.fingerhut01:02:27

Nothing is stopping anyone from using :pre and :post in whatever Clojure functions they write, of course, so if you are comfortable with the run time overhead, you can put whatever run time conditions you want there.

andy.fingerhut01:02:04

or invocations of assert , or using run time validation to cause an error return value from a function, versus throwing an exception, which is what :pre :post and assert all do.

andy.fingerhut01:02:31

If you go in with eyes wide open on the potential performance impact, and/or measure it to see that it is acceptable to your application, I'd say be as defensive as you want. I think the performance warnings are there so commonly, because without them, as engineers giving advice, we would feel negligent if we didn't try to warn people.

jakubl02:02:06

for sure totally fair - if i have a operation that's gonna get called 1 million times in one invocation - certainly will think twice about how much validation i put there - regardless of what type system (or language) i'm using so back to my original question knowing all of this are there advantages of one (:pre :post) or the other s/fdef ?

seancorfield03:02:21

@jakub696 I didn't mean to duck out of the conversation -- it was just dinner time here. I agree with everything Andy said by way of follow-up, I just wanted to set expectations around Clojure and Spec, when you're coming from a static typing background.

jakubl04:02:14

no worries - in fact I read what you wrote and it's basically what i am looking for - https://corfield.org/blog/2019/09/13/using-spec/

4
andy.fingerhut02:02:56

By default I believe s/fdef gives you only :pre checks, never :post , and you must call s/instrument to turn them on.

andy.fingerhut02:02:43

Also, depending upon the complexity of checks you want to do, non-spec implementations of validation checks can be an order of magnitude faster than using the logically equivalent spec validation checks.

andy.fingerhut02:02:45

The example I am about to link to is an extreme one, perhaps, because the non-spec validation is a single call to set? , which is very fast, but it should at least make you want to do performance measurements on any spec validation checks you perform in your application: https://github.com/jafingerhut/funjible#wait-isnt-this-what-clojurespec-is-for

jakubl02:02:52

is it possible to turn on instrumentation only for some functions - and not others?

andy.fingerhut02:02:30

Yes. It is only turned on for the functions and/or namespace that you turn it on for. Never on by default.

andy.fingerhut02:02:25

(well, there may be dev tool environments that might turn it on in hopefully documented circumstances, but for this purpose I consider that "not on by default")

👍 4
Polaris02:02:14

I'm working with EDN format data on Node.js and would like to convert to JSON in order to get data. I used jsedn npm but not working well.

Polaris02:02:25

What is the best way to do it?

jaihindhreddy02:02:17

EDN is more expressive than JSON in two ways, it has more types and it is extensible. This means a generic EDN->JSON transformation cannot be done without losing information.

Polaris02:02:40

So to use EDN, I have to work with closurescript instead of javascript?

Polaris02:02:39

Actually I am new to clojure

andy.fingerhut02:02:27

I believe that simple examples of things for which there is an EDN syntax defined, but none for JSON, are sets of objects, and Clojure ratios (e.g. 22/7). I think a lot of people convert EDN to/from JSON while simply not using the things that JSON does not have.

👍 4
Polaris04:02:40

Sounds great. I am converting edn to json with cli but it's not converting some data which as # symbol as prefix. Can you let me know how to fix it?

Polaris04:02:02

for example echo {:a #inst 2020} | jet --to json is converting to {:a

seancorfield04:02:49

#inst means a time literal, so #inst 2020 is January 1st, 2020, 00:00:00. I'm a bit surprised jet doesn't handle that. Maybe ask @borkdude when he's around tomorrow?

seancorfield04:02:36

Oh, Looks like the EDN should be {:a #inst "2020"}

seancorfield04:02:01

what you have there (without the quotes) isn't valid EDN @software.dev0218

seancorfield04:02:23

$ echo '{:a #inst "2020"}' | jet --to json
{"a":"2020-01-01T00:00:00Z"}

Polaris04:02:33

@seancorfield Yes you are right. Actually it's {:data #inst "2020-03-03T00:00:00.000-00:00"}

seancorfield04:02:17

OK, that would convert just fine

$ echo '{:data #inst "2020-03-03T00:00:00.000-00:00"}' | jet --to json
{"data":"2020-03-03T00:00:00Z"}

Polaris04:02:33

I am still getting like this

Polaris04:02:02

this can be related to something else?

seancorfield04:02:08

You need ' around it

👍 4
seancorfield04:02:28

otherwise echo is sending three separate strings and jet is just reading one

seancorfield04:02:36

Compare what I posted above

Polaris04:02:32

It's working for now. I will try with real data. Thank you very much:+1:

Hi10:02:47

:thinking_face: REPL evaluates \space into it seld but does not evaluate \cosmos 😲

jakuzure13:02:45

Hi, I'm trying to do destructure in a let like so {{condition :main} :weather} x , problem is :weather gives back a vector (`[{:id 802, :main Clouds, :description Mäßig bewölkt, :icon 03d}]`), so I should do first on it before I can do :main, any way to do this inside the let?

Cameron14:02:17

I assume you mean something like {[{condition :main} & -rest] :weather} x ?

Cameron14:02:32

{[{condition :main} & _] :weather} x

Cameron14:02:39

I can't really say as to whether it should be done that way btw, as on one hand it looks like something you might have to unscramble a bit in your mind to figure out what's going on, due to the quite nested destructuring. On the other hand, I don't think it would take significant time to do so, and I don't know in the scheme of things if it would really matter either way. Its not something I've thought about enough to feel comfortable having a definite answer, so I'll just say that's at least how you would do that, if I'm understanding you, without commenting on whether or not you should do it

Polaris15:02:20

Hi. when I convert from EDN to JSON it also has to be working?

({:description "Massachusetts Presidential Primary Election", :date #inst "2020-03-03T00:00:00.000-00:00", :district-divisions #{{:ocd-id "ocd-division/country:us/state:ma", :voter-registration-authority-level :municipal, :election-authority-level :municipal, :voting-methods #{{:primary true, :instructions {:voting-id "You may be asked to show identification if: you are voting for the first-time in Massachusetts in a federal election, you are an inactive voter, you are casting a provisional or challenged ballot, or if the poll worker has a reasonable suspicion that leads them to request identification. Acceptable forms include (must show your name and address at which you are registered to vote): a MA driver's license or MA-issued ID card; recent utility bill; rent receipt; signed lease; a copy of a voter registration affidavit; or any other printed identification which contains the voter's name and address.\n\nVoters without ID: If you're a first-time voter who is unable to present ID when you check in, you may vote a provisional ballot and return with acceptable ID by close of polls. If you're asked for ID for any other reason, and are not able to present ID in such a situation, you must still be permitted to vote; however, your ballot must be challenged. Your ballot will be cast normally, and will only be re-examined in the case of a recount, court order, or audit."}, :type :in-person, :excuse-required false} {:primary false, :type :by-mail, :excuse-required true, :ballot-request-deadline-received #inst "2020-03-02T00:00:00.000-00:00", :acceptable-forms #{{:name :ma_absentee}}} {:primary false, :start #inst "2020-02-24T00:00:00.000-00:00", :type :early-voting, :excuse-required false, :end #inst "2020-02-28T00:00:00.000-00:00"}}, :voter-registration-methods #{{:instructions {:registration "You should know: you need an ID issued by the Massachusetts Registry of Motor Vehicles. If you don't have an ID issued by the Massachusetts Registry of Motor Vehicles, you can still register by mail to vote."}, :type :online, :supports-iframe true, :deadline-online #inst "2020-02-12T00:00:00.000-00:00", :url ""} {:deadline-postmarked #inst "2020-02-12T00:00:00.000-00:00", :instructions {:signature "To register in Massachusetts you must: \nbe a citizen of the United States \nbe a resident of Massachusetts \nbe at least 16 years old (must be 18 years old to vote on Election Day)\nnot have been convicted of corrupt practices in respect to elections \nnot be under guardianship with respect to voting \nnot be currently incarcerated for a felony conviction", :idnumber "Federal law requires that you provide your Massachusetts driver's license number to register to vote. If you do not have a current and valid Massachusetts driver's license, you must provide the last four digits of your Social Security number. If you have neither, you must write \"NONE\" in the box  and a unique identifying number will be assigned to you."}, :type :by-mail, :acceptable-forms #{{:name :nvrf}}}}, :primary-voting-method-source :state}}, :type :presidential-primary, :source {:type :state-list, :notes "", :date #inst "2019-12-05T00:00:00.000-00:00"}, :polling-place-url "", :id #uuid "5e262e0e-d971-461c-8b7c-31eeb50d7fcc", :population 6902149, :website "", :polling-place-url-shortened ""})

Polaris15:02:42

with echo '...' | jet --to json

Polaris15:02:00

I am getting weird issue, it's showing the syntax error near unexpected token ('`

dpsutton15:02:57

use a vector [] rather than a list (). I don't think the list or seq type is valid edn totally incorrect. see below

borkdude15:02:46

it is:

$ echo '(1 2 3)' | jet --from edn --to json
[1,2,3]

dpsutton15:02:58

ah my bad 🙂

dpsutton15:02:21

went and looked at the spec. i assumed there was function invocation going on rather than literal list

borkdude15:02:17

@dpsutton EDN doesn't know about function calls, it's just data without evaluation

borkdude15:02:10

fwiw if I paste that EDN in a file foo.edn and then do:

cat /tmp/foo.edn | jet --from edn --to json
it works fine. so there's probaby some issue with how you feed the EDN to stdin regarding quotes

Polaris15:02:15

Okay. Thanks. I will try

Cameron15:02:17

I suspect the single quotes you enclosed it in are being closed prematurely by a quotation in some text

Cameron15:02:40

such as the part that mentions "MA's driver's license"

Polaris15:02:24

So this can cause the issue?

borkdude15:02:57

you need to escape those single quotes or double quotes depending on the outer quotes

andy.fingerhut19:02:45

or put it all into a file, and use the command to read the file.

andy.fingerhut19:02:13

then you should not need to figure out how to do shell quoting correctly at all.

Polaris22:02:05

How to install jet-cli in win10?

Polaris22:02:51

Is it impossible?

borkdude22:02:34

hmm, at the moment I haven't got a Windows release for that project, but it's not impossible to make one. So far you're the first to request one. I'll make an issue for it

borkdude22:02:52

in the meantime, you can maybe use wsl(2)

borkdude22:02:52

@software.dev0218 Is your problem translating edn to json or vice versa? that's also possible with babashka which does have a Windows build: https://github.com/borkdude/babashka/

Polaris22:02:05

Yes my purpose is to translate from EDN to JSON

borkdude22:02:36

with babashka you can do this:

bb.exe -e '(->> "foo.edn" slurp json/generate-string (spit "foo.json"))'

Polaris22:02:24

Ah. Thank you!

borkdude22:02:08

sorry, forgot an edn/read-string:

bb -e '(->> "/tmp/foo.edn" slurp edn/read-string json/generate-string (spit "/tmp/foo.json"))'

borkdude22:02:44

@software.dev0218 You can install babashka using Ales's scoop bucket: https://github.com/littleli/scoop-clojure

Polaris22:02:17

Is it working on mac and linux as well?

borkdude22:02:34

(it = babashka, not scoop)

borkdude22:02:47

for mac and linux you can use brew to install

borkdude22:02:59

or just curl

Polaris22:02:07

I got it. Actually I am in need to translate edn to json with cli on my project

borkdude22:02:52

what exactly do you mean with cli?

Polaris22:02:29

I am translating edn to json with jet-cli on runtime.

Polaris22:02:38

It's Node.js project.

borkdude22:02:51

yeah, you can use babashka instead as well

Polaris22:02:02

exec(cat ./data.edn | jet --from edn --to json`, (err, stdout, stderr)`

borkdude22:02:16

same speedy startup.

Polaris22:02:37

Ah. It's okay. Then I will try with babashka

borkdude23:02:54

@software.dev0218 Is your project entirely in nodeJS?

borkdude23:02:49

If the EDN libraries for nodeJS aren't working for you (I don't know them personally), one other way could also be to use the sci library:

$ node
Welcome to Node.js v12.8.0.
Type ".help" for more information.
> const { evalString, toJS } = require("@borkdude/sci")
undefined
> toJS(evalString("{:a 1 :b 2}"))
{ b: 2, a: 1 }
You might need to add a single quote in front of the edn value to prevent evaluating it as Clojure code instead of just data, so like:
> toJS(evalString("'[foo bar]"))
[ 'foo', 'bar' ]

borkdude23:02:46

now I'm afk

Polaris23:02:04

@borkdude My project is entirely built with Node.js. I tried to use the 'jsden' npm but generated structure was a little mess.

Polaris23:02:20

This sci can be installed with npm?

borkdude23:02:52

yeah, just npm install @borkdude/sci --save

borkdude23:02:52

it is an entire Clojure interpreter, so it does a little more than only converting EDN

👍 4
Polaris23:02:13

Ah I see. Then I will try with it