Fork me on GitHub
#clojurescript
<
2018-11-26
>
frankiesardo09:11:03

Hello all, I'm running cljs tests (using @olical great https://github.com/Olical/cljs-test-runner ) and when I load namespaces that require Expo code which is using ES6 module import I get “Uncaught SyntaxError: Unexpected String”. I'm not expert of js but how can I tell cljs test runners to not error when some of the modules use ES6? Has anyone run tests successfully against Expo? I see there is https://github.com/expo/expo/tree/master/packages/jest-expo for JS but I have no idea how to integrate it with cljs

Olical11:11:23

By default, cljs-test-runner executes your code within your local nodejs instance, so I guess that doesn't support the ES6 syntax? (I have no idea, I'm not keeping up with JavaScript features 😬) It's not ideal, but maybe you could try running your dependencies through babel or whatever to compile the unsupported syntax out? Might be worth asking the expo people if they've ever dealt with anything similar. I'd hope they'd provide a version that used require syntax but maybe not.

rarous12:11:15

ES6 modules are not supported in Node.js jet. You have to transpile them to require() form.

frankiesardo13:11:49

Ahhh it makes sense but I have no idea how to transpile them as part of a test pipeline 😞

frankiesardo13:11:04

Thanks for your reply, but I'm still stuck

cjohansen11:11:52

is it safe to (cljs.reader/read-string x) when x is the output of (with-out-str (cljs.pprint/pprint data)) (assuming data does not contain JS objects and other un-serializable values)?

Dustin Getz13:11:26

i think you want clojure.tools.reader (and cljs.tools.reader)

joshkh12:11:14

is there a sane way to get bootstrap 4's js on the page and bundled into a cljs project? just something to simply skip requiring it from a cdn (but not as complex as using react bootstrap components)

dnolen14:11:19

@joshkh use it as a foreign library

Yehonathan Sharvit14:11:29

In Clojurescript, it is possible to make a type callable as a function (like keywords and maps) by extending the IFn protocol simply like this:

(extend-type js/String
  IFn
  (-invoke 
    ([s coll]
     (get coll (str s)))))
Is it also possible in Clojure?

bronsa14:11:08

@U09K620SG FWIW you also need to implement applyTo there if you want apply to work

Yehonathan Sharvit14:11:49

Thanks @U09K620SG and @bronsa clojure.lang.IFn is an interface and not a protocol. So we can extend IFn with deftype but not with extend-type.

user=> (extend-type java.lang.String
  #_=>   clojure.lang.IFn
  #_=>   (-invoke
  #_=>     ([s coll]
  #_=>      (get coll s))))
Evaluation error (IllegalArgumentException) at clojure.core/extend (core_deftype.clj:784).
interface clojure.lang.IFn is not a protocol

Yehonathan Sharvit14:11:21

what’s the difference between protocols and interfaces?

Dustin Getz14:11:40

TIL, i thought protocols were interfaces

Yehonathan Sharvit14:11:30

@bronsa can you illuminate us regarding interfaces vs. protocols?

bronsa14:11:48

protocols are backed by interfaces

bronsa14:11:56

each protocol has a matching interface

bronsa14:11:05

when you implement a protocol inline in a deftype, that interface is implemented

bronsa14:11:24

when you extend an existing type to a protocol, the implementation is added to a runtime virtual dispatch table

bronsa14:11:49

when you invoke a protocol method, clojure first checks the type for direct implementation of the interface, then looks into this dispatch table

bronsa14:11:00

there's various caches and optimizations but that's an impl detail

Yehonathan Sharvit14:11:16

Thanks Why IFn is not a protocol?

bronsa14:11:36

bootstrapping

bronsa14:11:53

IFn needs to exist before protocol exist

bronsa14:11:11

+ protocols make use of IFn in their implementation

bronsa14:11:14

so it would be circular

Yehonathan Sharvit14:11:28

somehow in cljs it works

bronsa14:11:46

it's not impossible to make IFn a protocol in clojure, but it would require a significant reimplementation of clojure

bronsa14:11:58

well cljs is a different beast than clojure

bronsa14:11:06

the runtime is different, has different limitations and strenghts

bronsa14:11:14

and most importantly, cljs was designed with protocols at the bottom

bronsa14:11:16

clojure wasn't

bronsa14:11:25

protocols came to clojure years after the original implementation

Yehonathan Sharvit14:11:18

Any idea why records are not functions in Clojure and ClojureScript?

bronsa14:11:38

it's by design, not a limitation

bronsa14:11:00

no idea, ask in #clojure-dev

bronsa14:11:07

I suspect because records are still considered lower level than maps, and you might want to define invoke as a different operation than get

bronsa14:11:23

but that's just my guess

Yehonathan Sharvit14:11:26

Why do you think records are considered lower level than maps?

bronsa14:11:19

that's what they were designed for, to be a lower-level, faster, interoppy alternative to maps

bronsa14:11:43

maybe lower-level is not the appropriate word here

bronsa14:11:10

but I don't know how else to express it

Yehonathan Sharvit15:11:01

what about “less generic” than maps?

Yehonathan Sharvit16:11:45

@bronsa in Clojure, we can read a record like this (read-string "#user.Person{:firstname 1, :lastname avc, :age 3}") Is there something similar availailable in cljs?

Yehonathan Sharvit16:11:04

I tried this in cljs (cljs.reader.read-string "#my.playground.Person{:firstname Dave, :lastname Smith, :age 42}")

Yehonathan Sharvit16:11:21

And I got: “No reader function for tag my.playground.Person.”

bronsa16:11:23

it couldn't work

bronsa16:11:44

or rather, it could only work in self-hosted cljs

bronsa16:11:21

actually, hmm, not sure that is true

Yehonathan Sharvit16:11:25

I am interested in self-hosted cljs

bronsa16:11:51

give me 10 minutes to think about it, I forgot the details of how this works in cljs

bronsa16:11:35

ok sorry, what I was talking about earlier relates to a different thing than what you're asking

bronsa16:11:19

i.e. in clojurescript source code you can't have #my.record[1] inlined, as the record exists at cljs runtime while the reader is executing in the clojure environment at cljs compile-time

bronsa16:11:43

as to why you can't have (read-string "#my.record[1]"), it looks like I simply forgot to implement that part when porting over from the clj impl

bronsa16:11:48

can you make a ticket in the tools reader jira?

Yehonathan Sharvit16:11:20

I’m in another call for the moment

Yehonathan Sharvit16:11:27

Will get back to you in an hour or so

bronsa14:11:12

not for existing types

dnolen14:11:44

not JS primitive types

dnolen14:11:49

I even think you may be able to make it work, but strongly discouraged as potential to definitely break stuff

Yehonathan Sharvit14:11:25

I know that it is dangerous but it seems to work with JS strings

dnolen14:11:15

the potential issue is that some JS libraries will type dispatch on .call property to distinguish fns

dnolen14:11:29

and that'll lead to a read head scratcher if that check comes before string check

dnolen14:11:04

anyways - unless this is for some kind of toy - not recommended

Yehonathan Sharvit14:11:24

it is for a toy (I mean a talk)

dnolen14:11:38

then put red flag on that

dnolen14:11:38

it's not a feature

Yehonathan Sharvit14:11:10

Any idea why records are not functions in Clojure and ClojureScript?

Yehonathan Sharvit14:11:44

I mean with a map you can have ({:a 1} :a) but now with a record

bronsa14:11:54

very nice

👏 4
dnolen15:11:33

@viebel huh, I didn't know that, probably Clojure JIRA has some information, it must have come up before

Yehonathan Sharvit15:11:03

OK. I wrote a blog post (2 years ago) “records are wacky maps” based on a talk by Michał Marczy https://blog.klipse.tech/clojurescript/2016/04/25/records-wacky-maps.html

Yehonathan Sharvit15:11:10

But it’s only today (2 years later) that I am asking myself the “why” question :thinking_face:

dnolen15:11:11

can only speculate - but I would still consider records to be a performance construct, I personally almost never use them

dnolen15:11:23

(:a record) is an optimized case

dnolen15:11:50

so perhaps IFn omission was to discourage using them if maps suffice

Dustin Getz15:11:45

Given spec and Cognitect guidance to prefer maps of namespaced keywords from many namespaces, is defrecord legacy at this point?

john15:11:56

If I needed a map that did more than usual map like things, I'd probably start with defrecord, rather than extending the map

john15:11:21

But I never really need to personally

john15:11:17

It's like an object with the convenience of a getter/setter interface of a map

Dustin Getz15:11:28

multimethod + map seems to beat defrecord + protocol in the happy path

john15:11:42

performancewise?

Dustin Getz15:11:52

no, flexibility and power

john15:11:11

That's my pref

Yehonathan Sharvit15:11:20

but for simple use cases “defrecord + protocol” are more easy to grasp For instance, you don’t have to explicitly mention that the behavior depends on the type of the first argument

Yehonathan Sharvit15:11:47

I mean code written with “defrecord + protocol” will be easier to understand

Dustin Getz15:11:26

That's not immediately obviously true to me, i will need to think on that, thanks

dnolen15:11:48

I would disagree with any claims of readability and records

dnolen15:11:37

Clojure kind of bucks the legacy notion - nothing is going away - if it’s useful - use it

sova-soars-the-sora16:11:38

Halo I just discovered macchiato and am using cljs to make a node.js server. i would also like to have my clojurescript application compile to a separate .js file from the server.js ...

sova-soars-the-sora16:11:01

is there a way to write out such a way in the project.clj to get 2 separate js files from the /src/... dir?

sova-soars-the-sora16:11:53

okay, it looks like modules are a thing...

thheller17:11:50

@sova that is not what modules are for. you basically want 2 different builds. one for the lcient and one for the server

sova-soars-the-sora17:11:48

Aha. So it is! okay I have one.. :target :nodejs ... what's the defacto? :target :???

sova-soars-the-sora17:11:22

and the correct answer was, delete that line when not node.js ^_^

sova-soars-the-sora17:11:06

So how do I ask lein to build my build named :client ?

borkdude19:11:21

argh, I just spent 30 minutes finding out why str/starts-with? didn’t work with my regex… solution: it doesn’t accept a regex at all (why not actually?)

Alex Miller (Clojure team)19:11:36

you can use the existing re- functions for that

Alex Miller (Clojure team)19:11:24

(re-find #"^foo" "foobar")

borkdude19:11:25

yeah, it’s easy to forget in cljs:

(str/starts-with? "" #"https?://") ;; false, no error

dnolen20:11:21

in Clojure you get a cast exception (from JVM)

dnolen20:11:36

in ClojureScript we just defer to goog.string which doesn’t do any validation of any kind

borkdude20:11:49

yeah, it all makes sense

borkdude20:11:33

someone should write a spec for it 😉

rnagpal21:11:48

I am trying to use web workers in my project and I get this error The key :target at (:cljsbuild :builds 1 :compiler) has a non-conforming value: :webworker. It should satisfy #{:nodejs} I have [org.clojure/clojurescript "1.10.238"] and [figwheel "0.5.17"] [figwheel-sidecar "0.5.17"] cljs builds work fine, I get this error when I try to start repl programatically by figwheel-sidecar.repl-api/start-figwheel!

lilactown22:11:29

the error seems pretty specific. you have a :target :webworker in your compiler config, but that's not valid

rnagpal22:11:33

yeah, but its valid since ClojureScript “1.10.238”

sova-soars-the-sora22:11:24

what are all the options for clojurescript on the server via node?

nenadalm06:11:33

I noticed that you're in macchiato channel. That's only framework I know of. You can also use reitit routing library that allows you to generate swagger doc and have input validation using spec: https://github.com/macchiato-framework/macchiato-core/issues/7 It is not perfect, but people working on macchiato and reitit react pretty quickly on issues and pull requests 🙂

jaawerth23:11:00

@sova I've only used it for quick scripts thus far rather than a full project, but I've had good luck with lumo (installable via npm undre lumo-cljs, which can both run cljs directly via its embedded node and be used to run a compile script if you want to run in an existing node project (or different version than that embedded in lumo)

johnj23:11:42

@sova clojure is one option 😛

sova-soars-the-sora23:11:55

Cool. I'm very happy using cljs on the clientside but now that I want to have some sort of minimal cross talk and persistence i'm trying to keep it all looking like cljs ^_^

jaawerth23:11:58

and yeah you can just use clojure itself + the regular compiler these days with pretty good results

johnj23:11:19

joking aside, cljs ecosystem is not as mature as clojure for server side stuff, at least that's my view for experience both, which has been very little time

jaawerth23:11:28

lumo just saves you some overhead waiting for the jvm to initialize and whatnot

sova-soars-the-sora23:11:52

yeah the server machine i'm using it'd be much nicer to do an npm deploy because it doesn't need many resources to get started

sova-soars-the-sora23:11:30

i'm frustrated by this new term "serverless" that is just a lie

johnj23:11:51

serverless is just moving the complexity from one place to another

johnj23:11:26

more complex from what I have seen

jaawerth23:11:05

yeah I am very skeptical. people like it because it seems like less complexity but it adds more complexity the same way microservices do once you have to deal with every single thing happening over a network boundary

lilactown23:11:17

it's very much more on the easy side of the scale, than the simpler side

jaawerth23:11:29

there are some frameworks that ameliorate this but I also don't like the fact that it couples you to a proprietary service without a universal protocol

lilactown23:11:49

it's super easy to author a Node.js AWS Lambda in the browser very quickly

lilactown23:11:42

but yeah, things like integration testing, developing locally, performance testing, etc. become a pain because your code is complected with the environment it gets run in