Fork me on GitHub
#clojure
<
2017-04-30
>
qqq03:04:04

is it safe or dangerous to store symbols in data ? i.e. if I'm writing something out to file and reading it back, should I change everything into keyword, string, number, list, vector, hashmap, set -- or is it also okay to have literal symbols? (I'm concerned about otential namespacing issues)

noisesmith03:04:09

clojure will gladly read a symbol that belongs in a nonexistent namespace - are you planning on resolving the symbols?

noisesmith03:04:33

the main reason to use keywords instead of symbols would be to avoid the implication that the symbols should be resolved

qqq03:04:31

no, I am not planning on resolving the symbols

tbaldridge04:04:22

symbols are data, there's really nothing special about them

robert-stuttaford07:04:57

looking for help with a possible common gotcha using deftype and protocols in a jar. in my repl it all works fine. but in the jar on server, it can’t find the implemented method. i’ve traced everything out and all the types are as i would expect, when compared to the in-repl situation. what could cause this to occur?

robert-stuttaford07:04:59

it’s actually slightly worse, because it doesn’t happen in a jar on my machine, but it does happen on AWS

noisesmith09:04:59

do you require the namespace that defines TranslatedEntity from every namespace where you create one?

noisesmith09:04:01

one gotcha would be if one were created using the definition compiled via aot before the source defining the protocol/type was redefined by loading the file

robert-stuttaford09:04:49

@noisesmith so another namespace imports the deftype and invokes its constructor in a normal function, and all the other code uses this function. are you saying that all callsites of this factory fn should also import the deftype directly? i do know that all such factory fn uses are only at runtime (because they require a backing Datomic database to be available)

noisesmith09:04:22

not import, require

noisesmith09:04:31

just using import and not require is what will cause this problem

robert-stuttaford09:04:48

the factory ns require :refer :all’s the ns with the deftype as well

robert-stuttaford09:04:51

deftype ns, factory ns, client ns. you’re saying client must require deftype ns as well as factory ns, because otherwise it’d only have the aot deftype?

noisesmith09:04:56

OK - to be safe I would even eliminate any instance of importing a class defined by defrecord, deftype, or defprotocol- because that's one thing that can lead to constructing or holding onto stale definitions

robert-stuttaford09:04:39

so perhaps one simplification would be to move the factory to the same ns the deftype is in, thereby removing the import

noisesmith09:04:43

if you use require (and the vars created by deftype, defrecord, defprotocol), you'll be less likely to hit an issue like this (not saying that import causes this particular issue though)

noisesmith09:04:06

another thing to watch for is creating the object in a def form

noisesmith09:04:37

(but I think that would need to be in a namespace not using require in order to get a stale definition)

robert-stuttaford09:04:48

does all this apply even if the deftype directly implements the protocol it’s complaining about?

robert-stuttaford09:04:10

that is, the aot creates a class lacking this impl, and later the proto’s methods are added?

noisesmith09:04:22

it happens if you create an instance of the type, and then later the protocol is defined

noisesmith09:04:43

it isn't that aot lacks the protocol - it's that it uses a stale definition of the protocol that isn't equal to the one that exists at runtime

noisesmith09:04:51

this is one reason I never use aot

noisesmith09:04:40

or, to be precise, I create a nearly empty namespace that only requires clojure.core, and aot that, and inside -main it uses require followed by resolve to load and launch the real -main

robert-stuttaford09:04:04

i see. i define the proto, then the type immediately after, implementing the proto. no redefinitions

noisesmith09:04:08

which means only trivial code gets aot compiled - to avoid gotchas like this

noisesmith09:04:29

@robert-stuttaford aot guarantees redefinitions

noisesmith09:04:56

there's the version in the byte code, then the version that exists once everything is loaded

istvan17:04:34

is there a way to define a concrete implementation with reify in Clojure? https://gist.github.com/l1x/7cbe2dd3ffcae8d6378430cac3086df7

noisesmith17:04:03

it seems like you need to implement the method that returns String

noisesmith17:04:01

@istvan what happens if you use (^String handleRequest [this in ^Context ctx] "ok")

noisesmith17:04:39

(fixed - everything that is not Object needs to be hinted if anything on that method is hinted)

istvan18:04:29

@noisesmith unfortunately it does not fix the problem

noisesmith18:04:53

@istvan what if you implement both types as the decompiled java code does?

istvan18:04:19

i found a solution

istvan18:04:24

(:gen-class
    :implements [com.amazonaws.services.lambda.runtime.RequestHandler]
    :methods [[handleRequest [Object com.amazonaws.services.lambda.runtime.Context] String]]))

istvan18:04:47

it generates the same function signature in the classes as the java code

istvan18:04:59

however, AWS still errors oout

tclamb18:04:09

@noisesmith the type hint goes on the arg vector like (handleRequest ^String [this in ^Context ctx] "ok"), but it sounds like the problem here is Lambda’s using reflection to look for (missing) type arguments to RequestHandler (which don’t appear to exist with reify)

noisesmith18:04:27

@tclamb quoting (doc reify)

The return type can be indicated by a type hint on the method name,

tclamb18:04:44

@istvan it’s weird to me that @Override isn’t triggering a compiler warning in the Java example when you have the return value typed as Object here: implements RequestHandler<Object, *Object*>

tclamb18:04:01

whaaaaat, that’s totally different than in functions 😞

istvan18:04:12

Compiled from "Dbmgmt.java"
public class com.streambright.Dbmgmt implements com.amazonaws.services.lambda.runtime.RequestHandler<java.lang.Object, java.lang.Object> {
  public com.streambright.Dbmgmt();
  public java.lang.String handleRequest(java.lang.Object, com.amazonaws.services.lambda.runtime.Context);
  public java.lang.Object handleRequest(java.lang.Object, com.amazonaws.services.lambda.runtime.Context);
}

istvan18:04:21

this is the java class

istvan18:04:35

public class dbmgmt implements com.amazonaws.services.lambda.runtime.RequestHandler {
  public static {};
  public dbmgmt();
  public boolean equals(java.lang.Object);
  public java.lang.String toString();
  public int hashCode();
  public java.lang.Object clone();
  public java.lang.Object handleRequest(java.lang.Object, com.amazonaws.services.lambda.runtime.Context);
  public java.lang.String handleRequest(java.lang.Object, com.amazonaws.services.lambda.runtime.Context);
  public static void main(java.lang.String[]);
}

istvan18:04:38

the clojure one

istvan18:04:43

exactly the same

istvan18:04:54

i mean the handleRequest signatures

istvan18:04:01

aws still throws and error

istvan18:04:51

:methods [ [name [param-types] return-type], ...]
The generated class automatically defines all of the non-private
methods of its superclasses/interfaces. This parameter can be used
to specify the signatures of additional methods of the generated
class. Static methods can be specified with ^{:static true} in the
signature's metadata. Do not repeat superclass/interface signatures
here.

tclamb18:04:31

the implements clause is different between the two. the error message makes it sound like there’s weird reflection stuff happening to figure out the input and output from implements RequestHandler<INPUT, OUTPUT>

istvan18:04:31

is there a way to use in clojure implements with types like in the java code?

tclamb18:04:43

no idea. I think there’s an InputStream/`OutputStream` lambda interface that should work though?

istvan19:04:33

not sure what you mean

mobileink19:04:43

dunno if this will help but it seems kinda sorta related https://github.com/migae/boot-ask/tree/master/src/clj/migae/templates

tclamb19:04:36

hmm, try without implementing the Java interface? it doesn’t seem like you need it

istvan19:04:08

yeah i can do that too

istvan19:04:35

i was just wondering if i could use clojure the same way i use java

tclamb19:04:39

I don’t think clojure exposes generics at all (except through reflection like java)

istvan19:04:53

yeah, maybe in 2.0 🙂

istvan19:04:47

i will use this i guess

mobileink19:04:12

@istvan write a null java stub, then extend it with gen-class? i think problem is gen-class automatically implements interface methods so you can delegate them but not give them concrete args. if memory serves, it's been a while. i could swear i had to deal with this for boot-ask. thinkin out loud.

kwladyka20:04:47

https://pastebin.com/gReurXmS hmm i have puzzle about namespaces and load into the REPL. Just .setLevel works only when i load ns directly, not as dependency… so strange thing. More description inside the link. Who can solve the puzzle? I don’t have any idea why this happen.

weavejester21:04:49

@kwladyka The problem might be because load will always load the namespaces, whereas require loads the namespace only once (unless you specify :reload).

kwladyka21:04:30

@weavejester i am testing in the way i close and open REPL again to be 100% sure it is happening

kwladyka21:04:19

it is just very strange behaviour, i don’t have even idea what is happening there

kwladyka21:04:48

and i would swear i didn’t have this problem about 2-3 weaks ago

weavejester21:04:04

@kwladyka What does your project file look like? Could you be requiring the namespace implicitly when you start the REPL? Also does the problem go away if you add a :reload directive?

kwladyka21:04:57

@weavejester ha i think i found it

kwladyka21:04:10

to really understand for now but i found it 😉

kwladyka21:04:07

i use intellij + crusive. When i start the REPL it loads in some way all deps, because i see println effect. But…. all things about .setLevel disappear. When i load Y it load Y, but not X again… anyway it is strange… maybe this happen after cursive update. Not really sure, but about 2-3 weaks ago i didn’t have this issue.

weavejester21:04:22

I doubt it’s anything directly to do with cursive. If it’s require vs load, then the difference is that require has a cache.

kwladyka21:04:59

i know but 1) i didn’t have this issue 2-3 weaks ago (in that time i did cursive update and intellij too) 2) it loads in some way everything when start the REPL but lose information about Logger level.

weavejester21:04:38

Hm, well I guess it could be setting the Logger, but I’d have thought it would use a different VM.

kwladyka21:04:07

i could change JVM ver. too in that time

kwladyka21:04:14

not sure about that

kwladyka21:04:25

but for sure this issue just appear in last time so it has to be something about environment like JVM ver. or cursive or…

kwladyka21:04:50

thx for help

danielsz21:04:34

@tbaldridge Are blocking takes and puts verboten inside go blocks?

danielsz21:04:47

@tbaldridge They starve the threadpool and you shoot yourself in the foot, right?

tbaldridge21:04:17

yes, that's pretty much it. Same problem with other blocking ops inside go loops

danielsz21:04:39

@tbaldridge There's a helpful error message when you use parking take/put outside of the go block, I wondered if a similar approach wouldn't be helpful when using blocking put/take inside of a go block.

danielsz21:04:15

@tbaldridge I got bitten the other day and it hurt 😁

tbaldridge21:04:03

that could possibly be done, but for it to work well, it would require a perf hit on all code that used <!! and >!!

bradford21:04:48

Is there a library that takes HTTP headers and turns them into a perfect URL, following appropriate rules? (like replacing the host in the URI with the host in the Host header, appending a "/" to URIs that are just a hostm etc). I've tried urly and exploding-fish but each is incomplete/buggy (can't handle ports, etc).

tbaldridge21:04:47

welcome to the web....where the rules are made-up and most headers don't matter 🙂

danielsz21:04:16

@tbaldridge I see. There's a widespread belief that it's OK to use blocking semantics in go blocks. I stumbled on two instances just today. One is a popular Clojure learning source. The other a blog post. I should probably refer them to some authoritative source that states it is verboten. Are you aware of something to that effect in the Clojure docs?

tbaldridge21:04:56

to try to put it nicely....it's a case of people not understanding their tech

tbaldridge22:04:19

I mean, there's a reason why we invented go blocks, and very clear reasons why it would never work to do blocking ops in go blocks

tbaldridge22:04:34

and I know it's been mentioned several times in talks and the such

tbaldridge22:04:44

but perhaps it's not documented well enough

bradford22:04:18

yeah, HTTP makes me lose my mind 😉

tbaldridge22:04:38

@danielsz so in the docs it's the difference between "block" and "park". And I agree, I've seen these confused. I think in the start it was very clear what these meant since everyone watched Rich's talk on core.async when it came out

mobileink22:04:04

tbaldridge: +1. makes me feel stupid, cause i have to relearn it every time. worse, i always leave wondering if i got the joke.

danielsz22:04:02

@tbaldridge I couldn't sympathize more. But I feel for the fools as well (of which I count myself in). 🙂

tbaldridge22:04:56

These days, people probably just pick up the lib and run with it. So perhaps a giant "intro to core.async" we need at the top of the api docs: https://clojure.github.io/core.async/

mobileink22:04:58

doc is harder than code. :)

danielcompton22:04:34

I'm trying to use io.aviso/pretty to format my REPL output exceptions

danielcompton22:04:24

I've got it installed where it will print the exception 'prettily' if I call (pst *e), but if my code throws an exception in the REPL, I just get:

(component/start-system (new-dev-system))
clojure.lang.ExceptionInfo: Cannot turn resource-model into resource, because it doesn't conform to a resource-model schema
clojure.lang.ExceptionInfo: Error in component :web-server in system com.stuartsierra.component.SystemMap calling #'com.stuartsierra.component/start

danielcompton22:04:58

I've tried installing the leiningen plugin, but it doesn't seem to make any difference

danielcompton22:04:22

Is there something else I need to do? I'm running in a standard lein repl

danielcompton22:04:30

actually, that's not quite accurate, without the aviso plugin I just get:

ExceptionInfo Cannot turn resource-model into resource, because it doesn't conform to a resource-model schema  clojure.core/ex-info (core.clj:4725)
and it's not coloured

danielcompton22:04:54

Ah, there is a separate function pretty-repl-caught which is called for exceptions caught by the REPL

nagi22:04:04

I've loved playing with clojure and feel I'm not quite a complete beginner any more. I completed the 4clojure website problems, and have begun playing with figwheel for fun. I'm now thinking of starting a software company making an online staff rota scheduling software. It will enable managers to put employees in certain shifts, and notify the staff what their shifts are when the Rota is published. It will allow people to book holiday etc, and eventually encopass payroll and other HR functions. I want to make a monolith, and the app will target the web browser. So with the backgournd set, on to my question. Can I, in your opinion, get a competitive advantage by building this monolithic webapp with clojure / clojure script rather than an OOP Rails program?

nagi22:04:45

I guess a better question, is to ask "is clojure / clojurescript a good fit for my application?"

tbaldridge22:04:34

@nagi Clojure is a good fit for data driven or business rules driven apps. On average a Clojure app will have less code than a OOP app, and that translates into quicker turn-around times.

tbaldridge22:04:22

This talk goes into these features a bit: https://www.youtube.com/watch?v=VSdnJDO-xdg

tbaldridge22:04:52

But here's the problem...I strongly believe that a good Clojure team can easily out-innovate a team that uses OOP languages. But that ability to be agile may be counteracted by a unfamiliarity with the language.

tbaldridge22:04:24

I.e. I don't know that I'd want stake my business on any team who had only used a language for half a dozen months.

nagi22:04:15

@tbaldridge Thanks. I guess I need to decide whether my app is data driven. I guess it could be, the rota looks like a table, with rows representing employees. That could be described as data very naturally. There are lots of business rules, like Bob can't work more than 15 hours a week, and June is off on Friday. It's only me working on the software, so I only need to worry about myself learning the tooling. Looks like I can get to use clojure 🙂

tbaldridge22:04:46

(hint: all applications are data-driven, it's just that OOP has convinced people otherwise)

nagi22:04:34

Ahh, OK, I see. The more data mangling that needs to happen, I guess the better clojure shines. I have experienced it's really hard to mangle data stored on objects conntected in some graph.

nagi22:04:21

@tbaldridge Thanks for taking time to reply, I appreciate it 👍

danielcompton23:04:48

I agree with everything @tbaldridge has said, but... in the context of starting a business, the application is only a small part of being successful. You've also got to consider sales, marketing, operations, documentation, support, and probably more that I'm not thinking of right now. All of those things are as important or more than which language your app is written in, especially for a fairly standard CRUD webapp. Taking on Clojure will add a significant amount of work to get you to the same level of productivity as a rails app (if you already know rails). It really depends on what your constraints are. If you're wanting to build a business, and hit profitability as quickly as possible then learning a new language probably isn't the best option IMO. If you're more interested in learning Clojure and Clojure webdev, and want to build a project as part of learning, then the time investment to get productive with Clojure may be time well spent

danielcompton23:04:08

Ah, also saw you're looking at adding ClojureScript as well. That's an additional innovation token (or three) that you need to spend to get up and running: http://mcfunley.com/choose-boring-technology

cfleming23:04:24

+1 from me.

nagi23:04:13

@danielcompton I want to have fun building a product alone, and later to turn that into a business. I may want to build a very big HR application from this humble start, and it may last a long time, and need to be extended to do extra stuff. So I may look forward to enjoying the benefits of having spent the extra time implementing the app in clojure. I think the main thing for me is that it's fun to work on, and I've already started a luminus project and begun coding the app. I just wanted to make sure that I wasn't making a mistake, and I think I've made the right choice. Now I understand my app is not a bad fit for clojure & clojurescript, I can swallow the slower time to market. The Rota business is not going anywhere soon. Thanks for helping me understand the trade offs.

cfleming23:04:10

@nagi That sounds very reasonable.

darwin23:04:12

hehe, @danielcompton just reminded me that I had a biz idea I wanted to use cljs for, and here I am - 2 years later still fiddling with some other stuff. But don’t take me wrong, it’s been a hell lot of fun! 🙂

cfleming23:04:14

I definitely agree that having your app be pleasant to work on is really important for long term success.

nagi23:04:49

I think it would be most likely to fail if I stopped hacking on it during my spare time, so I agree it's important that I enjoy working on it.

bcbradley23:04:26

how would you convert an array of longs into a sequence in clojure?

bcbradley23:04:07

it says it only works on arrays of reference types

bcbradley23:04:09

what does that mean

danielcompton23:04:22

(seq (long-array [1 2 3 4 5]))

danielcompton23:04:12

reference types are classes, not primitives, i.e. everything except for boolean, long, char, e.t.c.

danielcompton23:04:18

I can't quite marry up that docstring with the behaviour I see though, as long-array returns a primitive array, but still seems to be handled by seq just fine

andy.fingerhut23:04:49

This could be another case of the documentation promising behavior, and not promising anything for other inputs, yet it happens to do something reasonable in the non-documented case. As opposed to other cases like clojure.set/union where undocumented cases look reasonable, but return things a casual user might not expect.

andy.fingerhut23:04:47

In general, if you use Clojure functions in cases that are not documented, you are really on your own.

andy.fingerhut23:04:47

i.e. if they happen to return values you like, good for you. If they don't, you are using the wrong function and should probably write your own.